merge main

This commit is contained in:
@clasher113 2024-05-18 16:39:02 +03:00
commit 92d4cc904f
413 changed files with 17872 additions and 13275 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ Debug/voxel_engine
/screenshots /screenshots
/out /out
/misc
/world /world
/worlds/**/* /worlds/**/*
/settings.toml /settings.toml

View File

@ -101,20 +101,19 @@ if (WIN32)
else() else()
find_package(PkgConfig) find_package(PkgConfig)
pkg_check_modules(LUAJIT REQUIRED luajit) pkg_check_modules(LUAJIT REQUIRED luajit)
pkg_check_modules(VORBIS REQUIRED vorbis vorbisfile)
set(LUA_LIBRARIES ${LUAJIT_LIBRARIES}) set(LUA_LIBRARIES ${LUAJIT_LIBRARIES})
set(LUA_INCLUDE_DIR ${LUAJIT_INCLUDE_DIR}) set(LUA_INCLUDE_DIR ${LUAJIT_INCLUDE_DIRS})
find_package(PNG REQUIRED) find_package(PNG REQUIRED)
find_package(Lua REQUIRED)
set(PNGLIB PNG::PNG) set(PNGLIB PNG::PNG)
set(VORBISLIB vorbis vorbisfile) set(VORBISLIB ${VORBIS_LDFLAGS})
endif() endif()
if (APPLE)
find_package(glfw3 3.3 REQUIRED)
endif ()
set(LIBS "") set(LIBS "")
if(UNIX) if(UNIX)
find_package(glfw3 3.3 REQUIRED)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
set(LIBS ${LIBS} Threads::Threads) set(LIBS ${LIBS} Threads::Threads)
endif() endif()

View File

@ -1,6 +1,6 @@
# Run with compiled executable file: # Run with compiled executable file:
[Download | Скачать](https://github.com/MihailRis/VoxelEngine-Cpp/releases/tag/v20.0) [Download | Скачать](https://github.com/MihailRis/VoxelEngine-Cpp/releases/latest)
# Controls: # Controls:
@ -42,7 +42,7 @@ sudo ln -s /usr/include/luajit-2.1 /usr/include/lua
#### RHEL-based distro: #### RHEL-based distro:
```sh ```sh
sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel libvorbis-devel openal-devel luajit sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel libvorbis-devel openal-devel luajit-devel
``` ```
#### Arch-based distro: #### Arch-based distro:

View File

@ -26,9 +26,7 @@ AppDir:
- libglew2.1 - libglew2.1
- libpng16-16 - libpng16-16
- libopenal1 - libopenal1
- libopengl0
- libasound2 - libasound2
- libglx0
- libogg0 - libogg0
- libvorbis0a - libvorbis0a
- libvorbisfile3 - libvorbisfile3

View File

@ -13,6 +13,30 @@ load_script("packid:scripts/script_name.lua") -- load Lua script if not loaded y
load_script("packid:scripts/script_name.lua", true) -- load Lua script anyway load_script("packid:scripts/script_name.lua", true) -- load Lua script anyway
``` ```
## *pack* library
```python
pack.is_installed(packid: str) -> bool
```
Check if specified pack is installed in the world
```python
pack.data_file(packid: str, filename: str) -> str
```
Returns data file path like `world:data/packid/filename`
and creates missing directories.
Use this function when saving pack settings or other data to the world.
Example:
```lua
file.write(pack.data_file(PACK_ID, "example.txt"), text)
```
For pack *containermod* will write text to the file `world:data/containermod/example.txt`
## *player* library ## *player* library
@ -71,6 +95,26 @@ world.get_seed() -> int
Returns world seed. Returns world seed.
## *pack* library
```python
pack.get_folder(packid: str) -> str
```
Returns installed content-pack folder
```python
pack.is_installed(packid: str) -> bool
```
Check if the world has specified pack installed
```python
pack.get_installed() -> array of strings
```
Returns all installed content-pack ids
## *gui* library ## *gui* library
Library contains ui elements access functions. Library should not be directly used, because script *layouts/layout_name.xml.lua* already has a generated variable **document** (instance of **Document**) Library contains ui elements access functions. Library should not be directly used, because script *layouts/layout_name.xml.lua* already has a generated variable **document** (instance of **Document**)
@ -137,6 +181,13 @@ inventory.clone(invid: int) -> int
Create inventory copy. Returns the created copy ID. Create inventory copy. Returns the created copy ID.
```python
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```
Move item from slotA of invA to slotB of invB. invA may be the same as invB.
If slotB will be chosen automaticly if argument is not specified.
## *block* library ## *block* library
```python ```python
@ -313,6 +364,13 @@ hud.close(layoutid: str)
Remove an element from the screen Remove an element from the screen
```python
hud.get_block_inventory() -> int
```
Get open block inventory ID or 0
## Block events ## Block events
```lua ```lua

View File

@ -60,10 +60,6 @@
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error` - `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
## Изображение `image`
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
# Текстовое поле `textbox` # Текстовое поле `textbox`
Внутренний текст - изначально введенный текст Внутренний текст - изначально введенный текст

View File

@ -10,6 +10,29 @@ load_script("контентпак:scripts/имя_скрипта.lua", true) --
require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается) require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается)
``` ```
## Библиотека pack
```python
pack.is_installed(packid: str) -> bool
```
Проверяет наличие установленного пака в мире
```python
pack.data_file(packid: str, filename: str) -> str
```
Возвращает путь к файлу данных по типу: `world:data/packid/filename`
и создает недостающие директории в пути.
Используйте эту функцию при сохранении настроек пака или иных данных в мире.
Пример:
```lua
file.write(pack.data_file(PACK_ID, "example.txt"), text)
```
Для пака *containermod* запишет текст в файл `world:data/containermod/example.txt`
## Библиотека player ## Библиотека player
```python ```python
player.get_pos(playerid: int) -> number, number, number player.get_pos(playerid: int) -> number, number, number
@ -66,6 +89,26 @@ world.get_seed() -> int
Возвращает зерно мира. Возвращает зерно мира.
## Библиотека pack
```python
pack.get_folder(packid: str) -> str
```
Возвращает путь к папке установленного контент-пака
```python
pack.is_installed(packid: str) -> bool
```
Проверяет наличие контент-пака в мире
```python
pack.get_installed() -> массив строк
```
Возращает id всех установленных в мире контент-паков
## Библиотека gui ## Библиотека gui
Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex: Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex:
@ -132,6 +175,14 @@ inventory.clone(invid: int) -> int
Создает копию инвентаря и возвращает id копии. Если копируемого инвентаря не существует, возвращает 0. Создает копию инвентаря и возвращает id копии. Если копируемого инвентаря не существует, возвращает 0.
```python
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```
Перемещает предмет из slotA инвентаря invA в slotB инвентаря invB.
invA и invB могут указывать на один инвентарь.
slotB будет выбран автоматически, если не указывать явно.
## Библиотека block ## Библиотека block
```python ```python
@ -303,6 +354,13 @@ hud.close(layoutid: str)
``` ```
Удаляет элемент с экрана Удаляет элемент с экрана
```python
hud.get_block_inventory() -> int
```
Получить ID инвентаря открытого блока или 0
## События блоков ## События блоков
```lua ```lua

View File

@ -1,22 +0,0 @@
bazalt=Bazalt
blue_lamp=Blue Lamp
brick=Brick
dirt=Dirt
flower=Flower
glass=Glass
grass_block=Ground
grass=Tall Grass
green_lamp=Green Lamp
lamp=Lamp
leaves=Leaves
lightbulb=Bulb
metal=Metal
pane=Pane
pipe=Pipe
planks=Planks
red_lamp=Red Lamp
rust=Rust
sand=Sand
stone=Stone
water=Water
wood=Wood

View File

@ -1,22 +1,22 @@
bazalt=Базальт bazalt=базальт
blue_lamp=Синяя Лампа blue lamp=синяя лампа
brick=Кирпич brick=кирпич
dirt=Земля dirt=земля
flower=Цветок flower=цветок
glass=Стекло glass=стекло
grass_block=Дёрн grass block=дёрн
grass=Высокая Трава tall grass=высокая трава
green_lamp=Зелёная Лампа green lamp=зелёная лампа
lamp=Лампа lamp=лампа
leaves=Листва leaves=листва
lightbulb=Лампочка light bulb=лампочка
metal=Металл metal=металл
pane=Панель pane=панель
pipe=Труба pipe=труба
planks=Доски planks=доски
red_lamp=Красная Лампа red lamp=красная лампа
rust=Ржавчина rust=ржавчина
sand=Песок sand=песок
stone=Камень stone=камень
water=Вода water=вода
wood=Бревно wood=бревно

22
res/layouts/console.xml Normal file
View File

@ -0,0 +1,22 @@
<container color='#00000080' size='400' size-func="unpack(gui.get_viewport())">
<container size-func="gui.get_viewport()[1],gui.get_viewport()[2]-40">
<textbox
id='log'
color='0'
autoresize='true'
margin='0'
editable='false'
multiline='true'
size-func="gui.get_viewport()[1],40"
gravity="bottom-left"
></textbox>
</container>
<textbox id='prompt'
consumer='submit'
margin='0'
gravity='bottom-left'
size-func="gui.get_viewport()[1],40"
onup="on_history_up()"
ondown="on_history_down()">
</textbox>
</container>

View File

@ -0,0 +1,53 @@
history = session.get_entry("commands_history")
history_pointer = #history
function setup_variables()
local x,y,z = player.get_pos(hud.get_player())
console.set('pos.x', x)
console.set('pos.y', y)
console.set('pos.z', z)
end
function on_history_up()
if history_pointer == 0 then
return
end
document.prompt.text = history[history_pointer]
document.prompt.caret = -1
history_pointer = history_pointer - 1
end
function on_history_down()
if history_pointer == #history-1 then
return
end
history_pointer = history_pointer + 1
document.prompt.text = history[history_pointer + 1]
document.prompt.caret = -1
end
function add_to_history(text)
table.insert(history, text)
history_pointer = #history
end
function submit(text)
add_to_history(text)
setup_variables()
local status, result = pcall(function() return console.execute(text) end)
if result ~= nil then
local prevtext = document.log.text
if #prevtext == 0 then
document.log:paste(tostring(result))
else
document.log:paste('\n'..tostring(result))
end
end
document.prompt.text = ""
document.prompt.focused = true
end
function on_open()
document.prompt.focused = true
end

View File

@ -1,3 +1,8 @@
function inventory_share_func(invid, slotid) function inventory_share_func(invid, slotid)
inventory.set(invid, slotid, 0, 0) local blockinv = hud.get_block_inventory()
if blockinv ~= 0 then
inventory.move(invid, slotid, blockinv)
else
inventory.set(invid, slotid, 0, 0)
end
end end

View File

@ -0,0 +1,4 @@
<panel size='400' padding='8' interval='8' color='0'>
<label context='menu'>@Page not found</label>
<button onclick='menu:back()'>@Back</button>
</panel>

View File

@ -0,0 +1,13 @@
<container size='887,454' color='#0F1E2DB2' padding='8' interval='5' context='menu'>
<panel id='packs_cur' pos='2' size='440,406' color='0' max-length='406'>
<!-- content is generated in script -->
</panel>
<panel id='packs_add' pos='445,2' size='440,406' color='0' max-length='406'>
<!-- content is generated in script -->
</panel>
<button id='apply_btn' pos='2,410' size='440,40' onclick='apply()'>@Apply</button>
<button pos='445,410' size='398,40' onclick='menu:back()'>@Cancel</button>
<image onclick='refresh()' interactive='true' src='gui/refresh'
size='32' margin='7' gravity='bottom-right'
color='#FFFFFF80' hover-color='#FFFFFF10'/>
</container>

View File

@ -0,0 +1,131 @@
function on_open(params)
if params then
mode = params.mode
end
refresh()
end
add_packs = {}
rem_packs = {}
function apply()
core.reconfig_packs(add_packs, rem_packs)
if mode ~= "world" then
menu:back()
end
end
function refresh_changes()
document.apply_btn.enabled = (#add_packs>0) or (#rem_packs>0)
end
function move_pack(id)
-- cancel pack addition
if table.has(add_packs, id) then
document["pack_"..id]:move_into(document.packs_add)
table.remove_value(add_packs, id)
-- cancel pack removal
elseif table.has(rem_packs, id) then
document["pack_"..id]:move_into(document.packs_cur)
table.remove_value(rem_packs, id)
-- add pack
elseif table.has(packs_installed, id) then
document["pack_"..id]:move_into(document.packs_add)
table.insert(rem_packs, id)
-- remove pack
else
document["pack_"..id]:move_into(document.packs_cur)
table.insert(add_packs, id)
end
refresh_changes()
end
function place_pack(panel, packinfo, callback)
if packinfo.error then
callback = nil
end
if packinfo.has_indices then
packinfo.id_verbose = packinfo.id.."*"
else
packinfo.id_verbose = packinfo.id
end
packinfo.callback = callback
panel:add(gui.template("pack", packinfo))
if not callback then
document["pack_"..packinfo.id].enabled = false
end
end
function check_dependencies(packinfo)
if packinfo.dependencies == nil then
return
end
for i,dep in ipairs(packinfo.dependencies) do
local depid = dep:sub(2,-1)
if dep:sub(1,1) == '!' then
if not table.has(packs_all, depid) then
return string.format(
"%s (%s)", gui.str("error.dependency-not-found"), depid
)
end
if table.has(packs_installed, packinfo.id) then
table.insert(required, depid)
end
end
end
return
end
function refresh()
packs_installed = pack.get_installed()
packs_available = pack.get_available()
base_packs = pack.get_base_packs()
packs_all = {unpack(packs_installed)}
required = {}
for i,k in ipairs(packs_available) do
table.insert(packs_all, k)
end
local packs_cur = document.packs_cur
local packs_add = document.packs_add
packs_cur:clear()
packs_add:clear()
for i,id in ipairs(packs_installed) do
local packinfo = pack.get_info(id)
packinfo.index = i
callback = not table.has(base_packs, id) and string.format('move_pack("%s")', id) or nil
packinfo.error = check_dependencies(packinfo)
place_pack(packs_cur, packinfo, callback)
end
for i,id in ipairs(packs_available) do
local packinfo = pack.get_info(id)
packinfo.index = i
callback = string.format('move_pack("%s")', id)
packinfo.error = check_dependencies(packinfo)
place_pack(packs_add, packinfo, callback)
end
for i,id in ipairs(packs_installed) do
if table.has(required, id) then
document["pack_"..id].enabled = false
end
end
apply_movements(packs_cur, packs_add)
refresh_changes()
end
function apply_movements(packs_cur, packs_add)
for i,id in ipairs(packs_installed) do
if table.has(rem_packs, id) then
document["pack_"..id]:move_into(packs_add)
end
end
for i,id in ipairs(packs_available) do
if table.has(add_packs, id) then
document["pack_"..id]:move_into(packs_cur)
end
end
end

View File

@ -0,0 +1,3 @@
<panel size='400' color='#00000080' padding='8' context='settings'>
<!-- content is generated in script -->
</panel>

View File

@ -0,0 +1,16 @@
settings = session.get_entry('new_world')
function on_open()
local names = core.get_generators()
table.sort(names)
local panel = document.root
for _,k in ipairs(names) do
panel:add(gui.template("generator", {
callback=string.format("settings.generator=%q menu:back()", k),
id=k,
name=settings.generator_name(k)
}))
end
panel:add("<button onclick='menu:back()'>@Back</button>")
end

View File

@ -0,0 +1,3 @@
<panel size='400' color='#00000080' padding='8'>
<!-- content is generated in script -->
</panel>

View File

@ -0,0 +1,19 @@
function on_open()
local locales = gui.get_locales_info()
local invlocales = {}
local names = {}
for k, v in pairs(locales) do
table.insert(names, v.name)
invlocales[v.name] = k
end
table.sort(names)
local panel = document.root
for _,k in ipairs(names) do
panel:add(string.format(
"<button onclick=%q>%s</button>",
string.format("core.set_setting('ui.language', %q) menu:back()", invlocales[k]), k
))
end
panel:add("<button onclick='menu:back()'>@Back</button>")
end

View File

@ -0,0 +1,7 @@
<panel size='400' color='0' interval='1' context='menu'>
<button onclick='menu.page="new_world"'>@New World</button>
<panel id='worlds' size='390,1' padding='5' color='#FFFFFF11' max-length='400'>
</panel>
<button onclick='menu.page="settings"'>@Settings</button>
<button onclick='core.quit()'>@Quit</button>
</panel>

View File

@ -0,0 +1,6 @@
function on_open()
local worlds = world.get_list()
for _, info in ipairs(worlds) do
document.worlds:add(gui.template("world", info))
end
end

View File

@ -0,0 +1,18 @@
<panel size='400' color='0' interval='1' context='world'>
<label>@Name</label>
<textbox id='name_box'
validator='world_name_validator'
placeholder='New World'
padding='4'>
</textbox>
<label>@Seed</label>
<textbox id='seed_box' placeholder='-' padding='4'></textbox>
<button onclick='save_state() menu.page="generators"' id='generator_btn'>
@World generator
</button>
<button id='content_btn' onclick='save_state() menu.page="content"'/>
<button onclick='create_world()' margin='1,20,1,1'>
@Create World
</button>
<button onclick='menu:back()'>@Back</button>
</panel>

View File

@ -0,0 +1,47 @@
settings = session.get_entry('new_world')
function world_name_validator(name)
return name:match("^[%w-\\.\\ ]+$") ~= nil and not world.exists(name)
end
function save_state()
settings.name = document.name_box.text
settings.seed = document.seed_box.text
end
function settings.generator_name(id)
local prefix, name = parse_path(id)
if prefix == "core" then
return gui.str(name, "world.generators")
else
return id
end
end
function create_world()
if not document.name_box.valid then
return
end
local name = document.name_box.text
local seed = document.seed_box.text
local generator = settings.generator
session.reset_entry('new_world')
core.new_world(name, seed, generator)
end
function on_open()
document.content_btn.text = string.format(
"%s [%s]", gui.str("Content", "menu"), #pack.get_installed()
)
if settings.generator == nil then
settings.generator = core.get_default_generator()
end
document.generator_btn.text = string.format(
"%s: %s",
gui.str("World generator", "world"),
settings.generator_name(settings.generator)
)
document.name_box.text = settings.name or ''
document.seed_box.text = settings.seed or ''
document.seed_box.placeholder = tostring(math.random()):sub(3)
end

View File

@ -0,0 +1,8 @@
<panel size='400' color='0' interval='1' context='menu'>
<button onclick='menu:reset()'>@Continue</button>
<button onclick='menu.page="content?mode=world"'>@Content</button>
<button onclick='menu.page="settings"'>@Settings</button>
<button onclick='core.close_world(true)'>
@Save and Quit to Menu
</button>
</panel>

View File

@ -0,0 +1,14 @@
<container size='668,418' color='#0F1E2DB2' context='menu'>
<panel pos='6' size='250' color='0' interval='1'>
<button id='s_aud' onclick='set_page("s_aud", "settings_audio")'>@Audio</button>
<button id='s_gfx' onclick='set_page("s_gfx", "settings_graphics")'>@Graphics</button>
<button id='s_ctl' onclick='set_page("s_ctl", "settings_controls")'>@Controls</button>
</panel>
<pagebox id='menu' pos='260,6' size='400'>
</pagebox>
<panel margin='6' gravity='bottom-left' size='250' color='0' interval='1'>
<button onclick='menu.page="languages"' id='langs_btn'>-</button>
<button onclick='menu:back()'>@Back</button>
</panel>
</container>

View File

@ -0,0 +1,15 @@
function on_open()
document.langs_btn.text = string.format(
"%s: %s", gui.str("Language", "settings"),
gui.get_locales_info()[core.get_setting("ui.language")].name
)
set_page("s_gfx", "settings_graphics")
end
function set_page(btn, page)
document.s_aud.enabled = true
document.s_gfx.enabled = true
document.s_ctl.enabled = true
document[btn].enabled = false
document.menu.page = page
end

View File

@ -0,0 +1,2 @@
<panel size='400' color='0' interval='1'>
</panel>

View File

@ -0,0 +1,33 @@
function create_setting(id, name, step, postfix)
local info = core.get_setting_info(id)
postfix = postfix or ""
document.root:add(gui.template("track_setting", {
id=id,
name=gui.str(name, "settings"),
value=core.get_setting(id),
min=info.min,
max=info.max,
step=step,
postfix=postfix
}))
update_setting(core.get_setting(id), id, name, postfix)
end
function update_setting(x, id, name, postfix)
core.set_setting(id, x)
-- updating label
document[id..".L"].text = string.format(
"%s: %s%s",
gui.str(name, "settings"),
core.str_setting(id),
postfix
)
end
function on_open()
create_setting("audio.volume-master", "Master Volume", 0.01)
create_setting("audio.volume-regular", "Regular Sounds", 0.01)
create_setting("audio.volume-ui", "UI Sounds", 0.01)
create_setting("audio.volume-ambient", "Ambient", 0.01)
create_setting("audio.volume-music", "Music", 0.01)
end

View File

@ -0,0 +1,10 @@
<panel size='400' color='0'>
<label id='sensitivity_label'>-</label>
<trackbar id='sensitivity_track'
min='0.1' max='10' value='2' step='0.1'
consumer='change_sensitivity'>
</trackbar>
<panel id='bindings_panel' size='380,204' padding='2' interval='1' max-length='350' color='#0000004C'>
<!-- content is generated in script -->
</panel>
</panel>

View File

@ -0,0 +1,25 @@
function refresh_sensitivity()
document.sensitivity_label.text = string.format(
"%s: %s",
gui.str("Mouse Sensitivity", "settings"),
core.str_setting("camera.sensitivity")
)
end
function change_sensitivity(val)
core.set_setting("camera.sensitivity", val)
refresh_sensitivity()
end
function on_open()
document.sensitivity_track.value = core.get_setting("camera.sensitivity")
refresh_sensitivity()
local panel = document.bindings_panel
local bindings = core.get_bindings()
for i,name in ipairs(bindings) do
panel:add(gui.template("binding", {
id=name, name=gui.str(name)
}))
end
end

View File

@ -0,0 +1,2 @@
<panel size='400' color='0' interval='1' context='menu'>
</panel>

View File

@ -0,0 +1,44 @@
function create_setting(id, name, step, postfix)
local info = core.get_setting_info(id)
postfix = postfix or ""
document.root:add(gui.template("track_setting", {
id=id,
name=gui.str(name, "settings"),
value=core.get_setting(id),
min=info.min,
max=info.max,
step=step,
postfix=postfix
}))
update_setting(core.get_setting(id), id, name, postfix)
end
function update_setting(x, id, name, postfix)
core.set_setting(id, x)
-- updating label
document[id..".L"].text = string.format(
"%s: %s%s",
gui.str(name, "settings"),
core.str_setting(id),
postfix
)
end
function create_checkbox(id, name)
document.root:add(string.format(
"<checkbox consumer='function(x) core.set_setting(\"%s\", x) end' checked='%s'>%s</checkbox>",
id, core.str_setting(id), gui.str(name, "settings")
))
end
function on_open()
create_setting("chunks.load-distance", "Load Distance", 1)
create_setting("chunks.load-speed", "Load Speed", 1)
create_setting("graphics.fog-curve", "Fog Curve", 0.1)
create_setting("graphics.gamma", "Gamma", 0.05)
create_setting("camera.fov", "FOV", 1, "°")
create_checkbox("display.fullscreen", "Fullscreen")
create_checkbox("display.vsync", "V-Sync")
create_checkbox("graphics.backlight", "Backlight")
create_checkbox("camera.shaking", "Camera Shaking")
end

4
res/layouts/process.xml Normal file
View File

@ -0,0 +1,4 @@
<panel size='400' padding='8' interval='1' color='#00000080'>
<label id='title_label'>???</label>
<label id='progress_label'>-</label>
</panel>

View File

@ -0,0 +1,10 @@
function on_progress(done, total)
local progress = done / total
document.progress_label.text = string.format(
"%s/%s (%s%%)", done, total, math.floor(progress*100)
)
end
function on_open(title)
document.title_label.text = title
end

View File

@ -0,0 +1,7 @@
<panel size='500' color='#00000080' padding='8' context='menu'>
<label>@missing-content</label>
<panel id='content_panel' size='480,100' color='#00000080' scrollable='true' max-length='400'>
<!-- content is generated in script -->
</panel>
<button onclick='menu:back()'>@Back to Main Menu</button>
</panel>

View File

@ -0,0 +1,5 @@
function on_open(report)
for i, entry in ipairs(report.content) do
document.content_panel:add(gui.template("content_entry", entry))
end
end

View File

@ -0,0 +1,4 @@
<panel size='400,40' padding='4' color='0' orientation='horizontal'>
<bindbox binding='%{id}'/>
<label margin='6'>%{name}</label>
</panel>

View File

@ -0,0 +1,4 @@
<panel size='500,20' color='0' orientation='horizontal' padding='2'>
<label color='#80808080'>[%{type}]</label>
<label color='#FF333380'>"%{name}</label>
</panel>

View File

@ -0,0 +1,4 @@
<container onclick='%{callback}' size='80,30' hover-color='#FFFFFF10'>
<label pos='80,4' size='300,25' color='#FFFFFF80' align='right'>[%{id}]</label>
<label pos='0,8'>%{name}</label>
</container>

View File

@ -0,0 +1,15 @@
<container id='pack_%{id}' onclick='%{callback}' size='0,80' color='#00000040' hover-color='#00000080' z-index="%{index}">
<label color='#FFFFFF80' size='300,25' align='right' gravity='top-right'>
[%{id_verbose}]
</label>
<label pos='78,6'>%{title}</label>
<label if='%{creator}' color='#CCFFE5B2' size='300,20' align='right'
gravity='bottom-right'>
%{creator}
</label>
<label if='%{error}' pos='80,28' size='355,50' multiline='true' color='#FF4040'>%{error}</label>
<label ifnot='%{error}' pos='80,28' size='355,50' multiline='true' color='#FFFFFFB2'>
%{description}
</label>
<image pos='8,8' src='%{icon}' size='64'/>
</container>

View File

@ -0,0 +1,6 @@
<panel color='0' context='settings'>
<label id='%{id}.L' margin='0,3,0,0'>%{name}: %{value}%{postfix}</label>
<trackbar
value='%{value}' min='%{min}' max='%{max}' step='%{step}'
consumer='function(x) update_setting(x, "%{id}", "%{name}", "%{postfix}") end'/>
</panel>

View File

@ -0,0 +1,17 @@
<container
size='380,66'
color='#0F1E2DB2'
hover-color='#162B3399'
onclick='core.open_world("%{name}")'
>
<image pos='1' src='%{icon}' size='96,64'></image>
<label pos='104,8'>%{name}</label>
<button margin='4'
gravity='center-right'
color='#00000000'
hover-color='#FFFFFF2B'
padding='0'
onclick='core.delete_world("%{name}")'>
<image src='gui/delete_icon' size='32,32' color='#FFFFFF50'/>
</button>
</container>

View File

@ -11,4 +11,3 @@
"gui/crosshair" "gui/crosshair"
] ]
} }

89
res/scripts/stdcmd.lua Normal file
View File

@ -0,0 +1,89 @@
local SEPARATOR = "________________"
SEPARATOR = SEPARATOR..SEPARATOR..SEPARATOR
function build_scheme(command)
local str = command.name.." "
for i,arg in ipairs(command.args) do
if arg.optional then
str = str.."["..arg.name.."] "
else
str = str.."<"..arg.name.."> "
end
end
return str
end
console.add_command(
"help name:str=''",
"Show help infomation for the specified command",
function (args, kwargs)
local name = args[1]
if #name == 0 then
local commands = console.get_commands_list()
table.sort(commands)
local str = "Available commands:"
for i,k in ipairs(commands) do
str = str.."\n "..build_scheme(console.get_command_info(k))
end
return str.."\nuse 'help <command>'"
end
local command = console.get_command_info(name)
if command == nil then
return string.format("command %q not found", name)
end
local where = ""
local str = SEPARATOR.."\n"..command.description.."\n"..name.." "
for i,arg in ipairs(command.args) do
where = where.."\n "..arg.name.." - "..arg.type
if arg.optional then
str = str.."["..arg.name.."] "
where = where.." (optional)"
else
str = str.."<"..arg.name.."> "
end
end
if #command.args then
str = str.."\nwhere"..where
end
return str.."\n"..SEPARATOR
end
)
console.add_command(
"obj.tp obj:sel=$obj.id x:num~pos.x y:num~pos.y z:num~pos.z",
"Teleport object",
function (args, kwargs)
player.set_pos(unpack(args))
end
)
console.add_command(
"echo value:str",
"Print value to the console",
function (args, kwargs)
return args[1]
end
)
console.add_command(
"time.set value:num",
"Set day time [0..1] where 0 is midnight, 0.5 is noon",
function (args, kwargs)
return world.set_day_time(args[1])
end
)
console.add_command(
"blocks.fill id:str x:num~pos.x y:num~pos.y z:num~pos.z w:int h:int d:int",
"Fill specified zone with blocks",
function (args, kwargs)
local name, x, y, z, w, h, d = unpack(args)
local id = block.index(name)
for ly=0,h-1 do
for lz=0,d-1 do
for lx=0,w-1 do
block.set(x+lx, y+ly, z+lz, id)
end
end
end
return tostring(w*h*d).." blocks set"
end
)

View File

@ -131,7 +131,9 @@ events = {
} }
function events.on(event, func) function events.on(event, func)
events.handlers[event] = events.handlers[event] or {} -- why an array? length is always = 1
-- FIXME: temporary fixed
events.handlers[event] = {} -- events.handlers[event] or {}
table.insert(events.handlers[event], func) table.insert(events.handlers[event], func)
end end
@ -175,11 +177,114 @@ Document = {}
function Document.new(docname) function Document.new(docname)
return setmetatable({name=docname}, { return setmetatable({name=docname}, {
__index=function(self, k) __index=function(self, k)
return Element.new(self.name, k) local elem = Element.new(self.name, k)
rawset(self, k, elem)
return elem
end end
}) })
end end
_GUI_ROOT = Document.new("core:root")
_MENU = _GUI_ROOT.menu
menu = _MENU
local __post_runnables = {}
function __process_post_runnables()
if #__post_runnables then
for _, func in ipairs(__post_runnables) do
func()
end
__post_runnables = {}
end
end
function time.post_runnable(runnable)
table.insert(__post_runnables, runnable)
end
function gui.template(name, params)
local text = file.read(file.find("layouts/templates/"..name..".xml"))
for k,v in pairs(params) do
local arg = tostring(v):gsub("'", "\\'"):gsub('"', '\\"')
text = text:gsub("(%%{"..k.."})", arg)
end
text = text:gsub("if%s*=%s*'%%{%w+}'", "if=''")
text = text:gsub("if%s*=%s*\"%%{%w+}\"", "if=\"\"")
-- remove unsolved properties: attr='%{var}'
text = text:gsub("%w+%s*=%s*'%%{%w+}'%s?", "")
text = text:gsub("%w+%s*=%s*\"%%{%w+}\"%s?", "")
return text
end
session = {
entries={}
}
function session.get_entry(name)
local entry = session.entries[name]
if entry == nil then
entry = {}
session.entries[name] = entry
end
return entry
end
function session.reset_entry(name)
session.entries[name] = nil
end
function timeit(func, ...)
local tm = time.uptime()
func(...)
print("[time mcs]", (time.uptime()-tm) * 1000000)
end
function table.has(t, x)
for i,v in ipairs(t) do
if v == x then
return true
end
end
return false
end
function table.index(t, x)
for i,v in ipairs(t) do
if v == x then
return i
end
end
return -1
end
function table.remove_value(t, x)
local index = table.index(t, x)
if index ~= -1 then
table.remove(t, index)
end
end
function table.tostring(t)
local s = '['
for i,v in ipairs(t) do
s = s..tostring(v)
if i < #t then
s = s..', '
end
end
return s..']'
end
function file.readlines(path)
local str = file.read(path)
local lines = {}
for s in str:gmatch("[^\r\n]+") do
table.insert(lines, s)
end
return lines
end
-- Deprecated functions -- Deprecated functions
block_index = block.index block_index = block.index
block_name = block.name block_name = block.name

70
res/texts/de_DE.txt Normal file
View File

@ -0,0 +1,70 @@
# Общее
Yes=Ja
No=Nein
Ok=OK
Cancel=Stornieren
Back=Zurück
Continue=Weitermachen
Add=Hinzufügen
Converting world...=Weltkonvertierung im Gange...
error.pack-not-found=Paket konnte nicht gefunden werden
error.dependency-not-found=Die verwendete Abhängigkeit wurde nicht gefunden
pack.remove-confirm=Alle vom Paket bereitgestellten Inhalte (dauerhaft) aus der Welt löschen?
# Меню
menu.Apply=Anwenden
menu.Audio=Klang
menu.Back to Main Menu=Zurück zum Menü
menu.Content Error=Inhaltsfehler
menu.Content=Inhalt
menu.Continue=Weitermachen
menu.Controls=Kontrolle
menu.Create World=Erschaffe eine Welt
menu.missing-content=Fehlender Inhalt!
menu.New World=Neue Welt
menu.Quit=Ausfahrt
menu.Save and Quit to Menu=Speichern und zum Menü zurückkehren
menu.Settings=Einstellungen
world.Seed=Mais
world.Name=Name
world.World generator=Weltgenerator
world.generators.default=Normal
world.generators.flat=Wohnung
world.Create World=Welt Erschaffen
world.convert-request=Es gibt Änderungen in den Indizes! Die Welt bekehren?
world.delete-confirm=Die Welt dauerhaft löschen?
# Настройки
settings.Ambient=Hintergrund
settings.Backlight=Hintergrundbeleuchtung
settings.Camera Shaking=Wackelnde Kamera
settings.Fog Curve=Nebelkurve
settings.FOV=Sichtlinie
settings.Language=Sprache
settings.Load Distance=Ladeentfernung
settings.Load Speed=Download-Geschwindigkeit
settings.Master Volume=Allgemeine Lautstärke
settings.Mouse Sensitivity=Maus-Empfindlichkeit
settings.Music=Musik
settings.Regular Sounds=Normale Geräusche
settings.UI Sounds=Interface-Sounds
settings.V-Sync=Vertikale Synchronisation
# Управление
movement.forward=Vorwärts
movement.back=Zurück
movement.left=Links
movement.right=Rechts
movement.jump=Prallen
movement.sprint=Beschleunigung
movement.crouch=Stehlen
movement.cheat=Schummeln
hud.inventory=Inventar
player.pick=Wählen Sie Blockieren
player.attack=Angriff / Pause
player.build=Setzen Sie einen Block
player.flight=Flug
camera.zoom=Annäherung
camera.mode=Kameramodus ändern

View File

@ -1,7 +1,7 @@
# Menu # Menu
menu.missing-content=Missing Content! menu.missing-content=Missing Content!
world.convert-request=Content indices have changed! Convert world files? world.convert-request=Content indices have changed! Convert world files?
pack.remove-confirm=Do you want to erase all pack content from the world forever? pack.remove-confirm=Do you want to erase all pack(s) content from the world forever?
error.pack-not-found=Could not to find pack error.pack-not-found=Could not to find pack
error.dependency-not-found=Dependency pack is not found error.dependency-not-found=Dependency pack is not found
world.delete-confirm=Do you want to delete world forever? world.delete-confirm=Do you want to delete world forever?
@ -9,6 +9,7 @@ world.generators.default=Default
world.generators.flat=Flat world.generators.flat=Flat
# Bindings # Bindings
devtools.console=Console
movement.forward=Forward movement.forward=Forward
movement.back=Back movement.back=Back
movement.left=Left movement.left=Left

View File

@ -1,33 +1,39 @@
# Общее # Общее
Yes=Joo Yes=Joo
No=Ei No=Ei
Ok = Kyllä Ok=Ok
Cancel= Hylää Cancel=Hylää
Back = Takaisin Back=Takaisin
Continue=Jatka Continue=Jatka
Add=lisätä
Converting world...=Maailman muuttuminen on käynnissä...
error.pack-not-found=Pakettia ei löytynyt! error.pack-not-found=Pakettia ei löytynyt!
error.dependency-not-found=Käytetty riippuvuutta ei löytynyt
pack.remove-confirm=Poistetaanko kaikki pakettien sisältö maailmasta (peruuttamattomasti)?
# Меню # Меню
menu.New World = Uusi Maailma menu.New World=Uusi Maailma
menu.Quit=Poistu menu.Quit=Poistu
menu.Continue=Jatka menu.Continue=Jatka
menu.Save and Quit to Menu=Tallenna ja poistu valikkoon menu.Save and Quit to Menu=Tallenna ja poistu valikkoon
menu.missing-content=Puuttuu lisäosia! menu.missing-content=Ei ole sisältöä!
menu.Content Error=Lisäosa virhe! menu.Content Error=Sisältövirhe
menu.Controls=Ohjaus menu.Controls=Ohjaus
menu.Back to Main Menu=Takaisin Valikoon menu.Back to Main Menu=Takaisin Valikoon
menu.Settings=Asetukset menu.Settings=Asetukset
menu.Content=Lisäosat menu.Content=Sisältö
menu.Seed=Siemen menu.Audio=Audio
menu.Name=Nimi world.Seed=Siemen
world.Name=Nimi
world.World generator=Maailman generaattori
world.generators.default=Tavallinen
world.generators.flat=Tasainen
menu.Create World=Luo Maailma menu.Create World=Luo Maailma
world.World generator=Valon generaattori world.convert-request=Indeksit ovat muuttuneet! Luodaanko maailma uudeleen?
world.generators.default=Äärimmäinen world.delete-confirm=Poistetaanko maailma peruuttamattomasti?
world.generators.flat=Tasainen
world.convert-request=Indeksit vaihdettu! Lataa maailma uudeleen?
world.delete-confirm=Poistetaanko maailma ikuisesti?
# Настройки # Настройки
settings.Load Distance=Latausalue settings.Load Distance=Latausalue
@ -35,6 +41,12 @@ settings.Load Speed=Latausnopeus
settings.Fog Curve=Sumun tiheys settings.Fog Curve=Sumun tiheys
settings.Backlight=Taustavalo settings.Backlight=Taustavalo
settings.V-Sync=Pystytahdistus settings.V-Sync=Pystytahdistus
settings.Camera Shaking=Kameran tärähdys
settings.Master Volume=Pää-äänekkyys
settings.Regular Sounds=Tavalliset äänet
settings.UI Sounds=Käyttöliittymän äänet
settings.Ambient=Tausta
settings.Music=Musiikki
settings.FOV=Näkökenttä settings.FOV=Näkökenttä
settings.Mouse Sensitivity=Hiiren nopeus settings.Mouse Sensitivity=Hiiren nopeus

View File

@ -3,6 +3,9 @@
"be_BY": { "be_BY": {
"name": "Беларуская" "name": "Беларуская"
}, },
"de_DE": {
"name": "Deutsch"
},
"en_US": { "en_US": {
"name": "English" "name": "English"
}, },

View File

@ -5,38 +5,53 @@ Ok=OK
Cancel=Anulowanie Cancel=Anulowanie
Back=Powrót Back=Powrót
Continue=Kontynuacja Continue=Kontynuacja
Add=Dodać
Converting world...=Konwersja świata w toku...
error.pack-not-found=Nie udało się znaleźć pakietu error.pack-not-found=Nie udało się znaleźć pakietu
# Menu # Menu
menu.New World=Nowy Świat menu.Apply=Zastosować
menu.Quit=Wyjście menu.Audio=Dźwięki
menu.Continue=Kontynuacja
menu.Save and Quit to Menu=Zapisz i wyjdź do menu
menu.missing-content=Brak treści!
menu.Controls=Zarządzanie
menu.Back to Main Menu=Powrót do menu menu.Back to Main Menu=Powrót do menu
menu.Settings=Ustawienia
menu.Content=Treść menu.Content=Treść
menu.Continue=Kontynuacja
menu.Controls=Zarządzanie
menu.Graphics=Grafika
menu.Create World=Stwórz świat
menu.missing-content=Brakująca treść!
menu.New World=Nowy Świat
menu.Page not found=Strona nie znaleziona
menu.Quit=Wyjście
menu.Save and Quit to Menu=Zapisz i wyjdź do menu
menu.Settings=Ustawienia
world.Seed=Ziarno world.Seed=Ziarno
world.Name=Nazwa world.Name=Nazwa
menu.Create World=Stwórz świat
world.World generator=Generator światła world.World generator=Generator światła
world.generators.default=Ekstremalne world.generators.default=Ekstremalne
world.generators.flat=Mieszkanie world.generators.flat=Mieszkanie
world.Create World=Stwórz świat
world.convert-request=Szykują się zmiany w indeksach! Przekształcić świat? world.convert-request=Szykują się zmiany w indeksach! Przekształcić świat?
world.delete-confirm=Trwale usunąć świat?
# Ustawienia # Ustawienia
settings.Ambient=Tło
settings.Backlight=Podświetlenie
settings.Camera Shaking=Drżąca kamera
settings.Fog Curve=Krzywa mgły
settings.FOV=Wzrok
settings.Fullscreen=Pełny ekran
settings.Gamma=Gamma
settings.Language=Język
settings.Load Distance=Odległość ładowania settings.Load Distance=Odległość ładowania
settings.Load Speed=Prędkość pobierania settings.Load Speed=Prędkość pobierania
settings.Fog Curve=Krzywa mgły settings.Master Volume=Głośność główna
settings.Backlight=Podświetlenie
settings.V-Sync=Synchronizacja pionowa
settings.FOV=Wzrok
settings.Mouse Sensitivity=Wrażliwość myszy settings.Mouse Sensitivity=Wrażliwość myszy
settings.Language=Język settings.Music=Muzyka
settings.Regular Sounds=Normalne dźwięki
settings.UI Sounds=Dźwięki interfejsu
settings.V-Sync=Synchronizacja pionowa
# Zarządzanie # Zarządzanie
movement.forward=Naprzód movement.forward=Naprzód

View File

@ -10,49 +10,53 @@ Converting world...=Выполняется конвертация мира...
error.pack-not-found=Не удалось найти пакет error.pack-not-found=Не удалось найти пакет
error.dependency-not-found=Используемая зависимость не найдена error.dependency-not-found=Используемая зависимость не найдена
pack.remove-confirm=Удалить весь поставляемый паком контент из мира (безвозвратно)? pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
# Меню # Меню
menu.New World=Новый Мир menu.Apply=Применить
menu.Quit=Выход
menu.Continue=Продолжить
menu.Save and Quit to Menu=Сохранить и Выйти в Меню
menu.missing-content=Отсутствует Контент!
menu.Content Error=Ошибка Контента
menu.Controls=Управление
menu.Back to Main Menu=Вернуться в Меню
menu.Settings=Настройки
menu.Content=Контент
menu.Audio=Звук menu.Audio=Звук
menu.Back to Main Menu=Вернуться в Меню
menu.Content Error=Ошибка Контента
menu.Content=Контент
menu.Continue=Продолжить
menu.Controls=Управление
menu.Graphics=Графика
menu.missing-content=Отсутствует Контент!
menu.New World=Новый Мир
menu.Page not found=Страница не найдена
menu.Quit=Выход
menu.Save and Quit to Menu=Сохранить и Выйти в Меню
menu.Settings=Настройки
world.Seed=Зерно world.Seed=Зерно
world.Name=Название world.Name=Название
world.World generator=Генератор мира world.World generator=Генератор мира
world.generators.default=Обычный world.generators.default=Обычный
world.generators.flat=Плоский world.generators.flat=Плоский
menu.Create World=Создать Мир world.Create World=Создать Мир
world.convert-request=Есть изменения в индексах! Конвертировать мир? world.convert-request=Есть изменения в индексах! Конвертировать мир?
world.delete-confirm=Удалить мир безвозвратно? world.delete-confirm=Удалить мир безвозвратно?
# Настройки # Настройки
settings.Ambient=Фон
settings.Backlight=Подсветка
settings.Camera Shaking=Тряска Камеры
settings.Fog Curve=Кривая Тумана
settings.FOV=Поле Зрения
settings.Fullscreen=Полный экран
settings.Gamma=Гамма
settings.Language=Язык
settings.Load Distance=Дистанция Загрузки settings.Load Distance=Дистанция Загрузки
settings.Load Speed=Скорость Загрузки settings.Load Speed=Скорость Загрузки
settings.Fog Curve=Кривая Тумана
settings.Backlight=Подсветка
settings.V-Sync=Вертикальная Синхронизация
settings.Camera Shaking=Тряска Камеры
settings.Master Volume=Общая Громкость settings.Master Volume=Общая Громкость
settings.Mouse Sensitivity=Чувствительность Мыши
settings.Music=Музыка
settings.Regular Sounds=Обычные Звуки settings.Regular Sounds=Обычные Звуки
settings.UI Sounds=Звуки Интерфейса settings.UI Sounds=Звуки Интерфейса
settings.Ambient=Фон settings.V-Sync=Вертикальная Синхронизация
settings.Music=Музыка
settings.FOV=Поле Зрения
settings.Mouse Sensitivity=Чувствительность Мыши
settings.Language=Язык
# Управление # Управление
devtools.console=Консоль
movement.forward=Вперёд movement.forward=Вперёд
movement.back=Назад movement.back=Назад
movement.left=Влево movement.left=Влево

View File

@ -9,46 +9,48 @@ Add=Додати
Converting world...=Виконується конвертація світу... Converting world...=Виконується конвертація світу...
error.pack-not-found=Не вдалося знайти пакет error.pack-not-found=Не вдалося знайти пакет
error.dependency-not-found=Використовувана залежність не знайдена
pack.remove-confirm=Видалити назавжди весь контент, що постачається паком, зі світу?
# Меню # Меню
menu.New World=Новий Світ menu.Apply=Застосувати
menu.Quit=Вихід
menu.Continue=Продовжити
menu.Save and Quit to Menu=Зберегти і Вийти в Меню
menu.missing-content=Відсутній Контент!
menu.Controls=Керування
menu.Back to Main Menu=Повернутися до Меню
menu.Settings=Налаштування
menu.Content=Контент
menu.Audio=Звук menu.Audio=Звук
menu.Back to Main Menu=Повернутися до Меню
menu.Content Error=Помилка Контенту
menu.Content=Контент
menu.Continue=Продовжити
menu.Controls=Керування
menu.Graphics=Графіка
menu.missing-content=Відсутній Контент!
menu.New World=Новий Світ
menu.Page not found=Сторінка не знайдена
menu.Quit=Вихід
menu.Save and Quit to Menu=Зберегти і Вийти в Меню
menu.Settings=Налаштування
world.Seed=Зерно world.Seed=Зерно
world.Name=Назва world.Name=Назва
menu.Create World=Створити Світ menu.Create World=Створити Світ
world.World generator=Генератор світу world.World generator=Генератор світу
world.generators.default=Звичайний world.generators.default=Звичайні
world.generators.flat=Плоский world.generators.flat=Плоскі
world.convert-request=Є зміни в індексах! Конвертувати світ? world.convert-request=Є зміни в індексах! Конвертувати світ?
world.delete-confirm=Видалити світ назавжди?
# Налаштування # Налаштування
settings.Ambient=Фон
settings.Backlight=Підсвічування
settings.Camera Shaking=Тряска Камери
settings.Fog Curve=Крива Туману
settings.FOV=Поле зору
settings.Fullscreen=Повний екран
settings.Gamma=Гамма
settings.Language=Мова
settings.Load Distance=Дистанція Завантаження settings.Load Distance=Дистанція Завантаження
settings.Load Speed=Швидкість Завантаження settings.Load Speed=Швидкість Завантаження
settings.Fog Curve=Крива Туману
settings.Backlight=Підсвічування
settings.V-Sync=Вертикальна Синхронізація
settings.Camera Shaking=Коливання Камери
settings.Master Volume=Загальна Гучність settings.Master Volume=Загальна Гучність
settings.Regular Sounds=Звичайні Звуки
settings.UI Sounds=Звуки Інтерфейсу
settings.Ambient=Середовище
settings.Music=Музика
settings.FOV=Поле зору
settings.Mouse Sensitivity=Чутливість Миші settings.Mouse Sensitivity=Чутливість Миші
settings.Language=Мова settings.Music=Музика
settings.Regular Sounds=Звичайні Звуки
settings.UI Sounds=Звуки інтерфейсу
settings.V-Sync=Вертикальна Синхронізація
# Керування # Керування
movement.forward=Уперед movement.forward=Уперед

BIN
res/textures/gui/circle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -1,112 +1,87 @@
#include "Assets.h" #include "Assets.hpp"
#include "../audio/audio.h" #include "../audio/audio.hpp"
#include "../graphics/Texture.h" #include "../graphics/core/Texture.hpp"
#include "../graphics/Shader.h" #include "../graphics/core/Shader.hpp"
#include "../graphics/Atlas.h" #include "../graphics/core/Atlas.hpp"
#include "../graphics/Font.h" #include "../graphics/core/Font.hpp"
#include "../frontend/UiDocument.h" #include "../frontend/UiDocument.hpp"
#include "../logic/scripting/scripting.h" #include "../logic/scripting/scripting.hpp"
Assets::~Assets() { Assets::~Assets() {
} }
Texture* Assets::getTexture(std::string name) const { Texture* Assets::getTexture(std::string name) const {
auto found = textures.find(name); auto found = textures.find(name);
if (found == textures.end()) if (found == textures.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
void Assets::store(Texture* texture, std::string name){ void Assets::store(Texture* texture, std::string name){
textures.emplace(name, texture); textures.emplace(name, texture);
} }
Shader* Assets::getShader(std::string name) const{ Shader* Assets::getShader(std::string name) const{
auto found = shaders.find(name); auto found = shaders.find(name);
if (found == shaders.end()) if (found == shaders.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
void Assets::store(Shader* shader, std::string name){ void Assets::store(Shader* shader, std::string name){
shaders.emplace(name, shader); shaders.emplace(name, shader);
} }
Font* Assets::getFont(std::string name) const { Font* Assets::getFont(std::string name) const {
auto found = fonts.find(name); auto found = fonts.find(name);
if (found == fonts.end()) if (found == fonts.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
void Assets::store(Font* font, std::string name){ void Assets::store(Font* font, std::string name){
fonts.emplace(name, font); fonts.emplace(name, font);
} }
Atlas* Assets::getAtlas(std::string name) const { Atlas* Assets::getAtlas(std::string name) const {
auto found = atlases.find(name); auto found = atlases.find(name);
if (found == atlases.end()) if (found == atlases.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
void Assets::store(Atlas* atlas, std::string name){ void Assets::store(Atlas* atlas, std::string name){
atlases.emplace(name, atlas); atlases.emplace(name, atlas);
} }
audio::Sound* Assets::getSound(std::string name) const { audio::Sound* Assets::getSound(std::string name) const {
auto found = sounds.find(name); auto found = sounds.find(name);
if (found == sounds.end()) if (found == sounds.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
void Assets::store(audio::Sound* sound, std::string name) { void Assets::store(audio::Sound* sound, std::string name) {
sounds.emplace(name, sound); sounds.emplace(name, sound);
} }
const std::vector<TextureAnimation>& Assets::getAnimations() { const std::vector<TextureAnimation>& Assets::getAnimations() {
return animations; return animations;
} }
void Assets::store(const TextureAnimation& animation) { void Assets::store(const TextureAnimation& animation) {
animations.emplace_back(animation); animations.emplace_back(animation);
} }
UiDocument* Assets::getLayout(std::string name) const { UiDocument* Assets::getLayout(std::string name) const {
auto found = layouts.find(name); auto found = layouts.find(name);
if (found == layouts.end()) if (found == layouts.end())
return nullptr; return nullptr;
return found->second.get(); return found->second.get();
} }
void Assets::store(UiDocument* layout, std::string name) { void Assets::store(UiDocument* layout, std::string name) {
layouts.emplace(name, layout); layouts[name] = std::shared_ptr<UiDocument>(layout);
}
void Assets::extend(const Assets& assets) {
for (auto entry : assets.textures) {
textures[entry.first] = entry.second;
}
for (auto entry : assets.shaders) {
shaders[entry.first] = entry.second;
}
for (auto entry : assets.fonts) {
fonts[entry.first] = entry.second;
}
for (auto entry : assets.atlases) {
atlases[entry.first] = entry.second;
}
for (auto entry : assets.layouts) {
layouts[entry.first] = entry.second;
}
for (auto entry : assets.sounds) {
sounds[entry.first] = entry.second;
}
animations.clear();
for (auto entry : assets.animations) {
animations.emplace_back(entry);
}
} }

View File

@ -1,55 +0,0 @@
#ifndef ASSETS_ASSETS_H_
#define ASSETS_ASSETS_H_
#include "../graphics/TextureAnimation.h"
#include <string>
#include <memory>
#include <unordered_map>
#include <vector>
class Texture;
class Shader;
class Font;
class Atlas;
class UiDocument;
namespace audio {
class Sound;
}
class Assets {
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
std::unordered_map<std::string, std::shared_ptr<Shader>> shaders;
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
std::unordered_map<std::string, std::shared_ptr<Atlas>> atlases;
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
std::unordered_map<std::string, std::shared_ptr<audio::Sound>> sounds;
std::vector<TextureAnimation> animations;
public:
~Assets();
Texture* getTexture(std::string name) const;
void store(Texture* texture, std::string name);
Shader* getShader(std::string name) const;
void store(Shader* shader, std::string name);
Font* getFont(std::string name) const;
void store(Font* font, std::string name);
Atlas* getAtlas(std::string name) const;
void store(Atlas* atlas, std::string name);
audio::Sound* getSound(std::string name) const;
void store(audio::Sound* sound, std::string name);
const std::vector<TextureAnimation>& getAnimations();
void store(const TextureAnimation& animation);
UiDocument* getLayout(std::string name) const;
void store(UiDocument* layout, std::string name);
void extend(const Assets& assets);
};
#endif /* ASSETS_ASSETS_H_ */

63
src/assets/Assets.hpp Normal file
View File

@ -0,0 +1,63 @@
#ifndef ASSETS_ASSETS_HPP_
#define ASSETS_ASSETS_HPP_
#include "../graphics/core/TextureAnimation.hpp"
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>
#include <vector>
class Texture;
class Shader;
class Font;
class Atlas;
class Assets;
class UiDocument;
namespace audio {
class Sound;
}
namespace assetload {
/// @brief final work to do in the main thread
using postfunc = std::function<void(Assets*)>;
}
class Assets {
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
std::unordered_map<std::string, std::shared_ptr<Shader>> shaders;
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
std::unordered_map<std::string, std::shared_ptr<Atlas>> atlases;
std::unordered_map<std::string, std::shared_ptr<UiDocument>> layouts;
std::unordered_map<std::string, std::shared_ptr<audio::Sound>> sounds;
std::vector<TextureAnimation> animations;
public:
Assets() {}
Assets(const Assets&) = delete;
~Assets();
Texture* getTexture(std::string name) const;
void store(Texture* texture, std::string name);
Shader* getShader(std::string name) const;
void store(Shader* shader, std::string name);
Font* getFont(std::string name) const;
void store(Font* font, std::string name);
Atlas* getAtlas(std::string name) const;
void store(Atlas* atlas, std::string name);
audio::Sound* getSound(std::string name) const;
void store(audio::Sound* sound, std::string name);
const std::vector<TextureAnimation>& getAnimations();
void store(const TextureAnimation& animation);
UiDocument* getLayout(std::string name) const;
void store(UiDocument* layout, std::string name);
};
#endif // ASSETS_ASSETS_HPP_

View File

@ -1,58 +1,74 @@
#include "AssetsLoader.h" #include "AssetsLoader.hpp"
#include "Assets.h"
#include "assetload_funcs.h" #include "Assets.hpp"
#include "assetload_funcs.hpp"
#include "../util/ThreadPool.hpp"
#include "../constants.hpp"
#include "../data/dynamic.hpp"
#include "../debug/Logger.hpp"
#include "../coders/imageio.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include "../content/Content.hpp"
#include "../content/ContentPack.hpp"
#include "../graphics/core/Texture.hpp"
#include "../logic/scripting/scripting.hpp"
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include "../constants.h" static debug::Logger logger("assets-loader");
#include "../data/dynamic.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
#include "../content/Content.h"
#include "../content/ContentPack.h"
#include "../logic/scripting/scripting.h"
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths) AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
: assets(assets), paths(paths) : assets(assets), paths(paths)
{ {
addLoader(AssetType::shader, assetload::shader); addLoader(AssetType::shader, assetload::shader);
addLoader(AssetType::texture, assetload::texture); addLoader(AssetType::texture, assetload::texture);
addLoader(AssetType::font, assetload::font); addLoader(AssetType::font, assetload::font);
addLoader(AssetType::atlas, assetload::atlas); addLoader(AssetType::atlas, assetload::atlas);
addLoader(AssetType::layout, assetload::layout); addLoader(AssetType::layout, assetload::layout);
addLoader(AssetType::sound, assetload::sound); addLoader(AssetType::sound, assetload::sound);
} }
void AssetsLoader::addLoader(AssetType tag, aloader_func func) { void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
loaders[tag] = func; loaders[tag] = func;
} }
void AssetsLoader::add(AssetType tag, const std::string filename, const std::string alias, std::shared_ptr<AssetCfg> settings) { void AssetsLoader::add(AssetType tag, const std::string filename, const std::string alias, std::shared_ptr<AssetCfg> settings) {
entries.push(aloader_entry{tag, filename, alias, settings}); entries.push(aloader_entry{tag, filename, alias, settings});
} }
bool AssetsLoader::hasNext() const { bool AssetsLoader::hasNext() const {
return !entries.empty(); return !entries.empty();
}
aloader_func AssetsLoader::getLoader(AssetType tag) {
auto found = loaders.find(tag);
if (found == loaders.end()) {
throw std::runtime_error(
"unknown asset tag "+std::to_string(static_cast<int>(tag))
);
}
return found->second;
} }
bool AssetsLoader::loadNext() { bool AssetsLoader::loadNext() {
const aloader_entry& entry = entries.front(); const aloader_entry& entry = entries.front();
std::cout << " loading " << entry.filename << " as " << entry.alias << std::endl; logger.info() << "loading " << entry.filename << " as " << entry.alias;
std::cout.flush(); try {
auto found = loaders.find(entry.tag); aloader_func loader = getLoader(entry.tag);
if (found == loaders.end()) { auto postfunc = loader(this, paths, entry.filename, entry.alias, entry.config);
std::cerr << "unknown asset tag " << static_cast<int>(entry.tag) << std::endl; postfunc(assets);
return false; entries.pop();
} return true;
aloader_func loader = found->second; } catch (std::runtime_error& err) {
bool status = loader(*this, assets, paths, entry.filename, entry.alias, entry.config); logger.error() << err.what();
entries.pop(); entries.pop();
return status; return false;
}
} }
void addLayouts(int env, const std::string& prefix, const fs::path& folder, AssetsLoader& loader) { void addLayouts(scriptenv env, const std::string& prefix, const fs::path& folder, AssetsLoader& loader) {
if (!fs::is_directory(folder)) { if (!fs::is_directory(folder)) {
return; return;
} }
@ -100,7 +116,7 @@ void AssetsLoader::processPreload(
switch (tag) { switch (tag) {
case AssetType::sound: case AssetType::sound:
add(tag, path, name, std::make_shared<SoundCfg>( add(tag, path, name, std::make_shared<SoundCfg>(
map->getBool("keep-pcm", false) map->get("keep-pcm", false)
)); ));
break; break;
default: default:
@ -115,13 +131,14 @@ void AssetsLoader::processPreloadList(AssetType tag, dynamic::List* list) {
} }
for (uint i = 0; i < list->size(); i++) { for (uint i = 0; i < list->size(); i++) {
auto value = list->get(i); auto value = list->get(i);
switch (value->type) { switch (static_cast<dynamic::Type>(value.index())) {
case dynamic::valtype::string: case dynamic::Type::string:
processPreload(tag, *value->value.str, nullptr); processPreload(tag, std::get<std::string>(value), nullptr);
break; break;
case dynamic::valtype::map: { case dynamic::Type::map: {
auto name = value->value.map->getStr("name"); auto map = std::get<dynamic::Map_sptr>(value);
processPreload(tag, name, value->value.map); auto name = map->get<std::string>("name");
processPreload(tag, name, map.get());
break; break;
} }
default: default:
@ -161,9 +178,11 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/menubg", "gui/menubg"); loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/menubg", "gui/menubg");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/delete_icon", "gui/delete_icon"); loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/delete_icon", "gui/delete_icon");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_icon", "gui/no_icon"); loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_icon", "gui/no_icon");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/no_world_icon", "gui/no_world_icon");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/warning", "gui/warning"); loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/warning", "gui/warning");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/error", "gui/error"); loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/error", "gui/error");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/cross", "gui/cross"); loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/cross", "gui/cross");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh");
if (content) { if (content) {
loader.processPreloadConfigs(content); loader.processPreloadConfigs(content);
@ -179,13 +198,68 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
auto pack = entry.second.get(); auto pack = entry.second.get();
auto& info = pack->getInfo(); auto& info = pack->getInfo();
fs::path folder = info.folder / fs::path("layouts"); fs::path folder = info.folder / fs::path("layouts");
addLayouts(pack->getEnvironment()->getId(), info.id, folder, loader); addLayouts(pack->getEnvironment(), info.id, folder, loader);
} }
} }
loader.add(AssetType::atlas, TEXTURES_FOLDER+"/blocks", "blocks"); loader.add(AssetType::atlas, TEXTURES_FOLDER+"/blocks", "blocks");
loader.add(AssetType::atlas, TEXTURES_FOLDER+"/items", "items"); loader.add(AssetType::atlas, TEXTURES_FOLDER+"/items", "items");
} }
const ResPaths* AssetsLoader::getPaths() const { bool AssetsLoader::loadExternalTexture(
return paths; Assets* assets,
const std::string& name,
std::vector<std::filesystem::path> alternatives
) {
if (assets->getTexture(name) != nullptr) {
return true;
}
for (auto& path : alternatives) {
if (fs::exists(path)) {
try {
auto image = imageio::read(path.string());
assets->store(Texture::from(image.get()).release(), name);
return true;
} catch (const std::exception& err) {
logger.error() << "error while loading external "
<< path.u8string() << ": " << err.what();
}
}
}
return false;
}
const ResPaths* AssetsLoader::getPaths() const {
return paths;
}
class LoaderWorker : public util::Worker<aloader_entry, assetload::postfunc> {
AssetsLoader* loader;
public:
LoaderWorker(AssetsLoader* loader) : loader(loader) {
}
assetload::postfunc operator()(const std::shared_ptr<aloader_entry>& entry) override {
aloader_func loadfunc = loader->getLoader(entry->tag);
return loadfunc(loader, loader->getPaths(), entry->filename, entry->alias, entry->config);
}
};
std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone) {
auto pool = std::make_shared<
util::ThreadPool<aloader_entry, assetload::postfunc>
>(
"assets-loader-pool",
[=](){return std::make_shared<LoaderWorker>(this);},
[=](assetload::postfunc& func) {
func(assets);
}
);
pool->setOnComplete(onDone);
while (!entries.empty()) {
const aloader_entry& entry = entries.front();
auto ptr = std::make_shared<aloader_entry>(entry);
pool->enqueueJob(ptr);
entries.pop();
}
return pool;
} }

View File

@ -1,94 +0,0 @@
#ifndef ASSETS_ASSETS_LOADER_H
#define ASSETS_ASSETS_LOADER_H
#include <string>
#include <memory>
#include <filesystem>
#include <functional>
#include <map>
#include <queue>
namespace dynamic {
class Map;
class List;
}
enum class AssetType {
texture,
shader,
font,
atlas,
layout,
sound
};
class ResPaths;
class Assets;
class AssetsLoader;
class Content;
struct AssetCfg {
virtual ~AssetCfg() {}
};
struct LayoutCfg : AssetCfg {
int env;
LayoutCfg(int env) : env(env) {}
};
struct SoundCfg : AssetCfg {
bool keepPCM;
SoundCfg(bool keepPCM) : keepPCM(keepPCM) {}
};
using aloader_func = std::function<bool(AssetsLoader&, Assets*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
struct aloader_entry {
AssetType tag;
const std::string filename;
const std::string alias;
std::shared_ptr<AssetCfg> config;
};
class AssetsLoader {
Assets* assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
const ResPaths* paths;
void tryAddSound(std::string name);
void processPreload(AssetType tag, const std::string& name, dynamic::Map* map);
void processPreloadList(AssetType tag, dynamic::List* list);
void processPreloadConfig(std::filesystem::path file);
void processPreloadConfigs(const Content* content);
public:
AssetsLoader(Assets* assets, const ResPaths* paths);
void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load
/// @param tag asset type
/// @param filename asset file path
/// @param alias internal asset name
/// @param settings asset loading settings (based on asset type)
void add(
AssetType tag,
const std::string filename,
const std::string alias,
std::shared_ptr<AssetCfg> settings=nullptr
);
bool hasNext() const;
bool loadNext();
/// @brief Enqueue core and content assets
/// @param loader target loader
/// @param content engine content
static void addDefaults(AssetsLoader& loader, const Content* content);
const ResPaths* getPaths() const;
};
#endif // ASSETS_ASSETS_LOADER_H

113
src/assets/AssetsLoader.hpp Normal file
View File

@ -0,0 +1,113 @@
#ifndef ASSETS_ASSETS_LOADER_HPP_
#define ASSETS_ASSETS_LOADER_HPP_
#include "Assets.hpp"
#include "../interfaces/Task.hpp"
#include "../typedefs.hpp"
#include "../delegates.hpp"
#include <string>
#include <memory>
#include <filesystem>
#include <functional>
#include <map>
#include <queue>
namespace dynamic {
class Map;
class List;
}
enum class AssetType {
texture,
shader,
font,
atlas,
layout,
sound
};
class ResPaths;
class AssetsLoader;
class Content;
struct AssetCfg {
virtual ~AssetCfg() {}
};
struct LayoutCfg : AssetCfg {
scriptenv env;
LayoutCfg(scriptenv env) : env(env) {}
};
struct SoundCfg : AssetCfg {
bool keepPCM;
SoundCfg(bool keepPCM) : keepPCM(keepPCM) {}
};
using aloader_func = std::function<assetload::postfunc(
AssetsLoader*, // redundant?
const ResPaths*,
const std::string&,
const std::string&,
std::shared_ptr<AssetCfg>)
>;
struct aloader_entry {
AssetType tag;
const std::string filename;
const std::string alias;
std::shared_ptr<AssetCfg> config;
};
class AssetsLoader {
Assets* assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
const ResPaths* paths;
void tryAddSound(std::string name);
void processPreload(AssetType tag, const std::string& name, dynamic::Map* map);
void processPreloadList(AssetType tag, dynamic::List* list);
void processPreloadConfig(std::filesystem::path file);
void processPreloadConfigs(const Content* content);
public:
AssetsLoader(Assets* assets, const ResPaths* paths);
void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load
/// @param tag asset type
/// @param filename asset file path
/// @param alias internal asset name
/// @param settings asset loading settings (based on asset type)
void add(
AssetType tag,
const std::string filename,
const std::string alias,
std::shared_ptr<AssetCfg> settings=nullptr
);
bool hasNext() const;
bool loadNext();
std::shared_ptr<Task> startTask(runnable onDone);
const ResPaths* getPaths() const;
aloader_func getLoader(AssetType tag);
/// @brief Enqueue core and content assets
/// @param loader target loader
/// @param content engine content
static void addDefaults(AssetsLoader& loader, const Content* content);
static bool loadExternalTexture(
Assets* assets,
const std::string& name,
std::vector<std::filesystem::path> alternatives
);
};
#endif // ASSETS_ASSETS_LOADER_HPP_

View File

@ -1,55 +1,54 @@
#include "assetload_funcs.h" #include "assetload_funcs.hpp"
#include "Assets.hpp"
#include "AssetsLoader.hpp"
#include "../data/dynamic.hpp"
#include "../audio/audio.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include "../coders/imageio.hpp"
#include "../coders/json.hpp"
#include "../coders/GLSLExtension.hpp"
#include "../graphics/core/Shader.hpp"
#include "../graphics/core/Texture.hpp"
#include "../graphics/core/ImageData.hpp"
#include "../graphics/core/Atlas.hpp"
#include "../graphics/core/Font.hpp"
#include "../graphics/core/TextureAnimation.hpp"
#include "../frontend/UiDocument.hpp"
#include <iostream> #include <iostream>
#include <stdexcept>
#include <filesystem> #include <filesystem>
#include "Assets.h"
#include "AssetsLoader.h"
#include "../audio/audio.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
#include "../coders/png.h"
#include "../coders/json.h"
#include "../graphics/Shader.h"
#include "../graphics/Texture.h"
#include "../graphics/ImageData.h"
#include "../graphics/Atlas.h"
#include "../graphics/Font.h"
#include "../graphics/TextureAnimation.h"
#include "../frontend/UiDocument.h"
#include "../logic/scripting/scripting.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
static bool animation( static bool animation(
Assets* assets, Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string directory, const std::string& atlasName,
const std::string name, const std::string& directory,
const std::string& name,
Atlas* dstAtlas Atlas* dstAtlas
); );
bool assetload::texture( assetload::postfunc assetload::texture(
AssetsLoader&, AssetsLoader*,
Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> std::shared_ptr<AssetCfg>
) { ) {
std::unique_ptr<Texture> texture( std::shared_ptr<ImageData> image (
png::load_texture(paths->find(filename+".png").u8string()) imageio::read(paths->find(filename+".png").u8string()).release()
); );
if (texture == nullptr) { return [name, image](auto assets) {
std::cerr << "failed to load texture '" << name << "'" << std::endl; assets->store(Texture::from(image.get()).release(), name);
return false; };
}
assets->store(texture.release(), name);
return true;
} }
bool assetload::shader( assetload::postfunc assetload::shader(
AssetsLoader&, AssetsLoader*,
Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name, const std::string name,
@ -61,43 +60,32 @@ bool assetload::shader(
std::string vertexSource = files::read_string(vertexFile); std::string vertexSource = files::read_string(vertexFile);
std::string fragmentSource = files::read_string(fragmentFile); std::string fragmentSource = files::read_string(fragmentFile);
Shader* shader = Shader::create( vertexSource = Shader::preprocessor->process(vertexFile, vertexSource);
vertexFile.string(), fragmentSource = Shader::preprocessor->process(fragmentFile, fragmentSource);
fragmentFile.string(),
vertexSource, fragmentSource
);
if (shader == nullptr) { return [=](auto assets) {
std::cerr << "failed to load shader '" << name << "'" << std::endl; assets->store(Shader::create(
return false; vertexFile.u8string(),
} fragmentFile.u8string(),
assets->store(shader, name); vertexSource, fragmentSource
return true; ), name);
};
} }
static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) { static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
// png is only supported format
if (file.extension() != ".png")
return false;
std::string name = file.stem().string(); std::string name = file.stem().string();
// skip duplicates // skip duplicates
if (atlas.has(name)) { if (atlas.has(name)) {
return false; return false;
} }
std::unique_ptr<ImageData> image(png::load_image(file.string())); auto image = imageio::read(file.string());
if (image == nullptr) {
std::cerr << "could not to load " << file.string() << std::endl;
return false;
}
image->fixAlphaColor(); image->fixAlphaColor();
atlas.add(name, image.release()); atlas.add(name, std::move(image));
return true; return true;
} }
bool assetload::atlas( assetload::postfunc assetload::atlas(
AssetsLoader&, AssetsLoader*,
Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string directory, const std::string directory,
const std::string name, const std::string name,
@ -105,201 +93,219 @@ bool assetload::atlas(
) { ) {
AtlasBuilder builder; AtlasBuilder builder;
for (const auto& file : paths->listdir(directory)) { for (const auto& file : paths->listdir(directory)) {
if (!appendAtlas(builder, file)) continue; if (!imageio::is_read_supported(file.extension().u8string()))
continue;
if (!append_atlas(builder, file))
continue;
} }
Atlas* atlas = builder.build(2); std::set<std::string> names = builder.getNames();
assets->store(atlas, name); Atlas* atlas = builder.build(2, false).release();
for (const auto& file : builder.getNames()) { return [=](auto assets) {
animation(assets, paths, "textures", file, atlas); atlas->prepare();
} assets->store(atlas, name);
return true; for (const auto& file : names) {
animation(assets, paths, name, directory, file, atlas);
}
};
} }
bool assetload::font( assetload::postfunc assetload::font(
AssetsLoader&, AssetsLoader*,
Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> std::shared_ptr<AssetCfg>
) { ) {
std::vector<std::unique_ptr<Texture>> pages; auto pages = std::make_shared<std::vector<std::unique_ptr<ImageData>>>();
for (size_t i = 0; i <= 4; i++) { for (size_t i = 0; i <= 4; i++) {
std::string name = filename + "_" + std::to_string(i) + ".png"; std::string name = filename + "_" + std::to_string(i) + ".png";
name = paths->find(name).string(); name = paths->find(name).string();
std::unique_ptr<Texture> texture (png::load_texture(name)); pages->push_back(imageio::read(name));
if (texture == nullptr) {
std::cerr << "failed to load bitmap font '" << name;
std::cerr << "' (missing page " << std::to_string(i) << ")";
std::cerr << std::endl;
return false;
}
pages.push_back(std::move(texture));
} }
int res = pages[0]->getHeight() / 16; return [=](auto assets) {
assets->store(new Font(std::move(pages), res, 4), name); int res = pages->at(0)->getHeight() / 16;
return true; std::vector<std::unique_ptr<Texture>> textures;
for (auto& page : *pages) {
textures.emplace_back(Texture::from(page.get()));
}
assets->store(new Font(std::move(textures), res, 4), name);
};
} }
bool assetload::layout( assetload::postfunc assetload::layout(
AssetsLoader& loader, AssetsLoader*,
Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string file, const std::string file,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> config std::shared_ptr<AssetCfg> config
) { ) {
try { return [=](auto assets) {
auto cfg = dynamic_cast<LayoutCfg*>(config.get()); try {
auto document = UiDocument::read(loader, cfg->env, name, file); auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
assets->store(document.release(), name); auto document = UiDocument::read(cfg->env, name, file);
return true; assets->store(document.release(), name);
} catch (const parsing_error& err) { } catch (const parsing_error& err) {
std::cerr << "failed to parse layout XML '" << file << "'" << std::endl; throw std::runtime_error(
std::cerr << err.errorLog() << std::endl; "failed to parse layout XML '"+file+"':\n"+err.errorLog()
return false; );
} }
};
} }
bool assetload::sound( assetload::postfunc assetload::sound(
AssetsLoader& loader, AssetsLoader*,
Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string file, const std::string file,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> config std::shared_ptr<AssetCfg> config
) { ) {
auto cfg = dynamic_cast<SoundCfg*>(config.get()); auto cfg = std::dynamic_pointer_cast<SoundCfg>(config);
bool keepPCM = cfg ? cfg->keepPCM : false; bool keepPCM = cfg ? cfg->keepPCM : false;
std::string extension = ".ogg"; std::string extension = ".ogg";
try { std::unique_ptr<audio::Sound> baseSound = nullptr;
std::unique_ptr<audio::Sound> baseSound = nullptr;
// looking for 'sound_name' as base sound // looking for 'sound_name' as base sound
auto soundFile = paths->find(file+extension); auto soundFile = paths->find(file+extension);
if (fs::exists(soundFile)) { if (fs::exists(soundFile)) {
baseSound.reset(audio::load_sound(soundFile, keepPCM)); baseSound = audio::load_sound(soundFile, keepPCM);
} }
// looking for 'sound_name_0' as base sound // looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file+"_0"+extension); auto variantFile = paths->find(file+"_0"+extension);
if (fs::exists(variantFile)) { if (fs::exists(variantFile)) {
baseSound.reset(audio::load_sound(variantFile, keepPCM)); baseSound = audio::load_sound(variantFile, keepPCM);
} }
// loading sound variants // loading sound variants
for (uint i = 1; ; i++) { for (uint i = 1; ; i++) {
auto variantFile = paths->find(file+"_"+std::to_string(i)+extension); auto variantFile = paths->find(file+"_"+std::to_string(i)+extension);
if (!fs::exists(variantFile)) { if (!fs::exists(variantFile)) {
break; break;
}
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM));
}
if (baseSound == nullptr) {
throw std::runtime_error("could not to find sound: " + file);
}
auto sound = baseSound.release();
return [=](auto assets) {
assets->store(sound, name);
};
}
static void read_anim_file(
const std::string& animFile,
std::vector<std::pair<std::string, int>>& frameList
) {
auto root = files::read_json(animFile);
auto frameArr = root->list("frames");
float frameDuration = DEFAULT_FRAME_DURATION;
std::string frameName;
if (frameArr) {
for (size_t i = 0; i < frameArr->size(); i++) {
auto currentFrame = frameArr->list(i);
frameName = currentFrame->str(0);
if (currentFrame->size() > 1) {
frameDuration = currentFrame->integer(1);
} }
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM)); frameList.emplace_back(frameName, frameDuration);
} }
assets->store(baseSound.release(), name);
} }
catch (std::runtime_error& err) { }
std::cerr << err.what() << std::endl;
return false; static TextureAnimation create_animation(
Atlas* srcAtlas,
Atlas* dstAtlas,
const std::string& name,
const std::set<std::string>& frameNames,
const std::vector<std::pair<std::string, int>>& frameList
) {
Texture* srcTex = srcAtlas->getTexture();
Texture* dstTex = dstAtlas->getTexture();
UVRegion region = dstAtlas->get(name);
TextureAnimation animation(srcTex, dstTex);
Frame frame;
uint dstWidth = dstTex->getWidth();
uint dstHeight = dstTex->getHeight();
uint srcWidth = srcTex->getWidth();
uint srcHeight = srcTex->getHeight();
frame.dstPos = glm::ivec2(region.u1 * dstWidth, region.v1 * dstHeight);
frame.size = glm::ivec2(region.u2 * dstWidth, region.v2 * dstHeight) - frame.dstPos;
for (const auto& elem : frameList) {
if (!srcAtlas->has(elem.first)) {
std::cerr << "Unknown frame name: " << elem.first << std::endl;
continue;
}
region = srcAtlas->get(elem.first);
if (elem.second > 0) {
frame.duration = static_cast<float>(elem.second) / 1000.0f;
}
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
} }
return true; return animation;
}
inline bool contains(
const std::vector<std::pair<std::string, int>>& frameList,
const std::string& frameName
) {
for (const auto& elem : frameList) {
if (frameName == elem.first) {
return true;
}
}
return false;
} }
static bool animation( static bool animation(
Assets* assets, Assets* assets,
const ResPaths* paths, const ResPaths* paths,
const std::string directory, const std::string& atlasName,
const std::string name, const std::string& directory,
const std::string& name,
Atlas* dstAtlas Atlas* dstAtlas
) { ) {
std::string animsDir = directory + "/animations"; std::string animsDir = directory + "/animation";
std::string blocksDir = directory + "/blocks";
for (const auto& folder : paths->listdir(animsDir)) { for (const auto& folder : paths->listdir(animsDir)) {
if (!fs::is_directory(folder)) continue; if (!fs::is_directory(folder)) continue;
if (folder.filename().string() != name) continue; if (folder.filename().u8string() != name) continue;
if (fs::is_empty(folder)) continue; if (fs::is_empty(folder)) continue;
AtlasBuilder builder; AtlasBuilder builder;
appendAtlas(builder, paths->find(blocksDir + "/" + name + ".png")); append_atlas(builder, paths->find(directory + "/" + name + ".png"));
std::string animFile = folder.string() + "/animation.json";
std::vector<std::pair<std::string, float>> frameList;
std::vector<std::pair<std::string, int>> frameList;
std::string animFile = folder.u8string() + "/animation.json";
if (fs::exists(animFile)) { if (fs::exists(animFile)) {
auto root = files::read_json(animFile); read_anim_file(animFile, frameList);
auto frameArr = root->list("frames");
float frameDuration = DEFAULT_FRAME_DURATION;
std::string frameName;
if (frameArr) {
for (size_t i = 0; i < frameArr->size(); i++) {
auto currentFrame = frameArr->list(i);
frameName = currentFrame->str(0);
if (currentFrame->size() > 1)
frameDuration = static_cast<float>(currentFrame->integer(1)) / 1000;
frameList.emplace_back(frameName, frameDuration);
}
}
} }
for (const auto& file : paths->listdir(animsDir + "/" + name)) { for (const auto& file : paths->listdir(animsDir + "/" + name)) {
if (!frameList.empty()) { if (!frameList.empty() && !contains(frameList, file.stem().u8string())) {
bool contains = false; continue;
for (const auto& elem : frameList) {
if (file.stem() == elem.first) {
contains = true;
break;
}
}
if (!contains) continue;
} }
if (!appendAtlas(builder, file)) continue; if (!append_atlas(builder, file))
continue;
} }
auto srcAtlas = builder.build(2, true);
std::unique_ptr<Atlas> srcAtlas (builder.build(2));
Texture* srcTex = srcAtlas->getTexture();
Texture* dstTex = dstAtlas->getTexture();
TextureAnimation animation(srcTex, dstTex);
Frame frame;
UVRegion region = dstAtlas->get(name);
uint dstWidth = dstTex->getWidth();
uint dstHeight = dstTex->getHeight();
uint srcWidth = srcTex->getWidth();
uint srcHeight = srcTex->getHeight();
frame.dstPos = glm::ivec2(region.u1 * dstWidth, region.v1 * dstHeight);
frame.size = glm::ivec2(region.u2 * dstWidth, region.v2 * dstHeight) - frame.dstPos;
if (frameList.empty()) { if (frameList.empty()) {
for (const auto& elem : builder.getNames()) { for (const auto& frameName : builder.getNames()) {
region = srcAtlas->get(elem); frameList.emplace_back(frameName, 0);
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
} }
} }
else { auto animation = create_animation(
for (const auto& elem : frameList) { srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList
if (!srcAtlas->has(elem.first)) { );
std::cerr << "Unknown frame name: " << elem.first << std::endl; assets->store(srcAtlas.release(), atlasName + "/" + name + "_animation");
continue;
}
region = srcAtlas->get(elem.first);
frame.duration = elem.second;
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
}
}
assets->store(srcAtlas.release(), name + "_animation");
assets->store(animation); assets->store(animation);
return true; return true;
} }
return true; return true;

View File

@ -1,5 +1,7 @@
#ifndef ASSETS_ASSET_LOADERS_H_ #ifndef ASSETS_ASSET_LOADERS_HPP_
#define ASSETS_ASSET_LOADERS_H_ #define ASSETS_ASSET_LOADERS_HPP_
#include "Assets.hpp"
#include <string> #include <string>
#include <memory> #include <memory>
@ -12,50 +14,44 @@ struct AssetCfg;
/// @brief see AssetsLoader.h: aloader_func /// @brief see AssetsLoader.h: aloader_func
namespace assetload { namespace assetload {
bool texture( postfunc texture(
AssetsLoader&, AssetsLoader*,
Assets*,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> settings std::shared_ptr<AssetCfg> settings
); );
bool shader( postfunc shader(
AssetsLoader&, AssetsLoader*,
Assets*,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> settings std::shared_ptr<AssetCfg> settings
); );
bool atlas( postfunc atlas(
AssetsLoader&, AssetsLoader*,
Assets*,
const ResPaths* paths, const ResPaths* paths,
const std::string directory, const std::string directory,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> settings std::shared_ptr<AssetCfg> settings
); );
bool font( postfunc font(
AssetsLoader&, AssetsLoader*,
Assets*,
const ResPaths* paths, const ResPaths* paths,
const std::string filename, const std::string filename,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> settings std::shared_ptr<AssetCfg> settings
); );
bool layout( postfunc layout(
AssetsLoader&, AssetsLoader*,
Assets*,
const ResPaths* paths, const ResPaths* paths,
const std::string file, const std::string file,
const std::string name, const std::string name,
std::shared_ptr<AssetCfg> settings std::shared_ptr<AssetCfg> settings
); );
bool sound( postfunc sound(
AssetsLoader&, AssetsLoader*,
Assets*,
const ResPaths* paths, const ResPaths* paths,
const std::string file, const std::string file,
const std::string name, const std::string name,
@ -63,4 +59,4 @@ namespace assetload {
); );
} }
#endif // ASSETS_ASSET_LOADERS_H_ #endif // ASSETS_ASSET_LOADERS_HPP_

View File

@ -1,7 +1,11 @@
#include "ALAudio.h" #include "ALAudio.hpp"
#include "alutil.h"
#include "alutil.hpp"
#include "../../debug/Logger.hpp"
#include <string> #include <string>
#include <iostream>
static debug::Logger logger("al-audio");
using namespace audio; using namespace audio;
@ -19,14 +23,14 @@ ALSound::~ALSound() {
buffer = 0; buffer = 0;
} }
Speaker* ALSound::newInstance(int priority, int channel) const { std::unique_ptr<Speaker> ALSound::newInstance(int priority, int channel) const {
uint source = al->getFreeSource(); uint source = al->getFreeSource();
if (source == 0) { if (source == 0) {
return nullptr; return nullptr;
} }
AL_CHECK(alSourcei(source, AL_BUFFER, buffer)); AL_CHECK(alSourcei(source, AL_BUFFER, buffer));
auto speaker = new ALSpeaker(al, source, priority, channel); auto speaker = std::make_unique<ALSpeaker>(al, source, priority, channel);
speaker->duration = duration; speaker->duration = duration;
return speaker; return speaker;
} }
@ -62,7 +66,7 @@ bool ALStream::preloadBuffer(uint buffer, bool loop) {
return true; return true;
} }
Speaker* ALStream::createSpeaker(bool loop, int channel) { std::unique_ptr<Speaker> ALStream::createSpeaker(bool loop, int channel) {
this->loop = loop; this->loop = loop;
uint source = al->getFreeSource(); uint source = al->getFreeSource();
if (source == 0) { if (source == 0) {
@ -75,7 +79,7 @@ Speaker* ALStream::createSpeaker(bool loop, int channel) {
} }
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer)); AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
} }
return new ALSpeaker(al, source, PRIORITY_HIGH, channel); return std::make_unique<ALSpeaker>(al, source, PRIORITY_HIGH, channel);
} }
@ -203,10 +207,10 @@ ALSpeaker::~ALSpeaker() {
} }
} }
void ALSpeaker::update(const Channel* channel, float masterVolume) { void ALSpeaker::update(const Channel* channel) {
if (source == 0) if (source == 0)
return; return;
float gain = this->volume * channel->getVolume()*masterVolume; float gain = this->volume * channel->getVolume();
AL_CHECK(alSourcef(source, AL_GAIN, gain)); AL_CHECK(alSourcef(source, AL_GAIN, gain));
if (!paused) { if (!paused) {
@ -259,7 +263,7 @@ void ALSpeaker::play() {
paused = false; paused = false;
stopped = false; stopped = false;
auto channel = get_channel(this->channel); auto channel = get_channel(this->channel);
AL_CHECK(alSourcef(source, AL_GAIN, volume * channel->getVolume())); AL_CHECK(alSourcef(source, AL_GAIN, volume * channel->getVolume() * get_channel(0)->getVolume()));
AL_CHECK(alSourcePlay(source)); AL_CHECK(alSourcePlay(source));
} }
@ -341,14 +345,14 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
alcGetIntegerv(device, ALC_ALL_ATTRIBUTES, size, &attrs[0]); alcGetIntegerv(device, ALC_ALL_ATTRIBUTES, size, &attrs[0]);
for (size_t i = 0; i < attrs.size(); ++i){ for (size_t i = 0; i < attrs.size(); ++i){
if (attrs[i] == ALC_MONO_SOURCES) { if (attrs[i] == ALC_MONO_SOURCES) {
std::cout << "AL: max mono sources: " << attrs[i+1] << std::endl; logger.info() << "max mono sources: " << attrs[i+1];
maxSources = attrs[i+1]; maxSources = attrs[i+1];
} }
} }
auto devices = getAvailableDevices(); auto devices = getAvailableDevices();
std::cout << "AL devices:" << std::endl; logger.info() << "devices:";
for (auto& name : devices) { for (auto& name : devices) {
std::cout << " " << name << std::endl; logger.info() << " " << name;
} }
} }
@ -368,21 +372,21 @@ ALAudio::~ALAudio() {
AL_CHECK(alcMakeContextCurrent(context)); AL_CHECK(alcMakeContextCurrent(context));
alcDestroyContext(context); alcDestroyContext(context);
if (!alcCloseDevice(device)) { if (!alcCloseDevice(device)) {
std::cerr << "AL: device not closed!" << std::endl; logger.error() << "device not closed!";
} }
device = nullptr; device = nullptr;
context = nullptr; context = nullptr;
} }
Sound* ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) { std::unique_ptr<Sound> ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
auto format = AL::to_al_format(pcm->channels, pcm->bitsPerSample); auto format = AL::to_al_format(pcm->channels, pcm->bitsPerSample);
uint buffer = getFreeBuffer(); uint buffer = getFreeBuffer();
AL_CHECK(alBufferData(buffer, format, pcm->data.data(), pcm->data.size(), pcm->sampleRate)); AL_CHECK(alBufferData(buffer, format, pcm->data.data(), pcm->data.size(), pcm->sampleRate));
return new ALSound(this, buffer, pcm, keepPCM); return std::make_unique<ALSound>(this, buffer, pcm, keepPCM);
} }
Stream* ALAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) { std::unique_ptr<Stream> ALAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new ALStream(this, stream, keepSource); return std::make_unique<ALStream>(this, stream, keepSource);
} }
ALAudio* ALAudio::create() { ALAudio* ALAudio::create() {
@ -395,7 +399,7 @@ ALAudio* ALAudio::create() {
return nullptr; return nullptr;
} }
AL_CHECK(); AL_CHECK();
std::cout << "AL: initialized" << std::endl; logger.info() << "initialized";
return new ALAudio(device, context); return new ALAudio(device, context);
} }
@ -406,7 +410,7 @@ uint ALAudio::getFreeSource(){
return source; return source;
} }
if (allsources.size() == maxSources){ if (allsources.size() == maxSources){
std::cerr << "attempted to create new source, but limit is " << maxSources << std::endl; logger.error() << "attempted to create new source, but limit is " << maxSources;
return 0; return 0;
} }
ALuint id; ALuint id;
@ -467,7 +471,8 @@ void ALAudio::setListener(glm::vec3 position, glm::vec3 velocity, glm::vec3 at,
AL_CHECK(alListener3f(AL_POSITION, position.x, position.y, position.z)); AL_CHECK(alListener3f(AL_POSITION, position.x, position.y, position.z));
AL_CHECK(alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z)); AL_CHECK(alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z));
AL_CHECK(alListenerfv(AL_ORIENTATION, listenerOri)); AL_CHECK(alListenerfv(AL_ORIENTATION, listenerOri));
AL_CHECK(alListenerf(AL_GAIN, get_channel(0)->getVolume()));
} }
void ALAudio::update(double delta) { void ALAudio::update(double) {
} }

View File

@ -1,5 +1,8 @@
#ifndef SRC_AUDIO_AUDIO_H_ #ifndef SRC_AUDIO_AUDIO_HPP_
#define SRC_AUDIO_AUDIO_H_ #define SRC_AUDIO_AUDIO_HPP_
#include "../audio.hpp"
#include "../../typedefs.hpp"
#include <queue> #include <queue>
#include <vector> #include <vector>
@ -15,9 +18,6 @@
#include <AL/alc.h> #include <AL/alc.h>
#endif #endif
#include "../audio.h"
#include "../../typedefs.h"
namespace audio { namespace audio {
struct ALBuffer; struct ALBuffer;
class ALAudio; class ALAudio;
@ -40,7 +40,7 @@ namespace audio {
return pcm; return pcm;
} }
Speaker* newInstance(int priority, int channel) const override; std::unique_ptr<Speaker> newInstance(int priority, int channel) const override;
}; };
class ALStream : public Stream { class ALStream : public Stream {
@ -65,7 +65,7 @@ namespace audio {
std::shared_ptr<PCMStream> getSource() const override; std::shared_ptr<PCMStream> getSource() const override;
void bindSpeaker(speakerid_t speaker) override; void bindSpeaker(speakerid_t speaker) override;
Speaker* createSpeaker(bool loop, int channel) override; std::unique_ptr<Speaker> createSpeaker(bool loop, int channel) override;
speakerid_t getSpeaker() const override; speakerid_t getSpeaker() const override;
void update(double delta) override; void update(double delta) override;
@ -91,7 +91,7 @@ namespace audio {
ALSpeaker(ALAudio* al, uint source, int priority, int channel); ALSpeaker(ALAudio* al, uint source, int priority, int channel);
~ALSpeaker(); ~ALSpeaker();
void update(const Channel* channel, float masterVolume) override; void update(const Channel* channel) override;
int getChannel() const override; int getChannel() const override;
State getState() const override; State getState() const override;
@ -135,7 +135,7 @@ namespace audio {
std::vector<uint> allbuffers; std::vector<uint> allbuffers;
std::vector<uint> freebuffers; std::vector<uint> freebuffers;
uint maxSources; uint maxSources = 256;
ALAudio(ALCdevice* device, ALCcontext* context); ALAudio(ALCdevice* device, ALCcontext* context);
public: public:
@ -148,8 +148,8 @@ namespace audio {
std::vector<std::string> getAvailableDevices() const; std::vector<std::string> getAvailableDevices() const;
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override; std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override; std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener( void setListener(
glm::vec3 position, glm::vec3 position,
@ -168,4 +168,4 @@ namespace audio {
}; };
} }
#endif /* SRC_AUDIO_AUDIO_H_ */ #endif // SRC_AUDIO_AUDIO_HPP_

View File

@ -1,6 +1,7 @@
#include "alutil.h" #include "alutil.hpp"
#include "../../debug/Logger.hpp"
#include <iostream>
#include <fstream> #include <fstream>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@ -14,30 +15,31 @@
#include <AL/alc.h> #include <AL/alc.h>
#endif #endif
static debug::Logger logger("open-al");
bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line){ bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line){
ALenum error = alGetError(); ALenum error = alGetError();
if(error != AL_NO_ERROR){ if(error != AL_NO_ERROR){
std::cerr << "OpenAL ERROR (" << filename << ": " << line << ")\n" ; logger.error() << filename << ": " << line;
switch(error){ switch(error){
case AL_INVALID_NAME: case AL_INVALID_NAME:
std::cerr << "AL_INVALID_NAME: a bad name (ID) was passed to an OpenAL function"; logger.error() << "a bad name (ID) was passed to an OpenAL function";
break; break;
case AL_INVALID_ENUM: case AL_INVALID_ENUM:
std::cerr << "AL_INVALID_ENUM: an invalid enum value was passed to an OpenAL function"; logger.error() << "an invalid enum value was passed to an OpenAL function";
break; break;
case AL_INVALID_VALUE: case AL_INVALID_VALUE:
std::cerr << "AL_INVALID_VALUE: an invalid value was passed to an OpenAL function"; logger.error() << "an invalid value was passed to an OpenAL function";
break; break;
case AL_INVALID_OPERATION: case AL_INVALID_OPERATION:
std::cerr << "AL_INVALID_OPERATION: the requested operation is not valid"; logger.error() << "the requested operation is not valid";
break; break;
case AL_OUT_OF_MEMORY: case AL_OUT_OF_MEMORY:
std::cerr << "AL_OUT_OF_MEMORY: the requested operation resulted in OpenAL running out of memory"; logger.error() << "the requested operation resulted in OpenAL running out of memory";
break; break;
default: default:
std::cerr << "UNKNOWN AL ERROR: " << error; logger.error() << "UNKNOWN AL ERROR: " << error;
} }
std::cerr << std::endl;
return false; return false;
} }
return true; return true;

View File

@ -1,5 +1,7 @@
#ifndef SRC_AUDIO_AUDIOUTIL_H_ #ifndef AUDIO_AUDIOUTIL_HPP_
#define SRC_AUDIO_AUDIOUTIL_H_ #define AUDIO_AUDIOUTIL_HPP_
#include "../../typedefs.hpp"
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@ -12,7 +14,6 @@
#endif #endif
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "../../typedefs.h"
#define AL_CHECK(STATEMENT) STATEMENT; AL::check_errors(__FILE__, __LINE__) #define AL_CHECK(STATEMENT) STATEMENT; AL::check_errors(__FILE__, __LINE__)
#define AL_GET_ERROR() AL::check_errors(__FILE__, __LINE__) #define AL_GET_ERROR() AL::check_errors(__FILE__, __LINE__)
@ -80,4 +81,4 @@ namespace AL {
} }
} }
#endif /* SRC_AUDIO_AUDIOUTIL_H_ */ #endif // AUDIO_AUDIOUTIL_HPP_

View File

@ -1,4 +1,4 @@
#include "NoAudio.h" #include "NoAudio.hpp"
using namespace audio; using namespace audio;
@ -9,12 +9,12 @@ NoSound::NoSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
} }
} }
Sound* NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) { std::unique_ptr<Sound> NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return new NoSound(pcm, keepPCM); return std::make_unique<NoSound>(pcm, keepPCM);
} }
Stream* NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) { std::unique_ptr<Stream> NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new NoStream(stream, keepSource); return std::make_unique<NoStream>(stream, keepSource);
} }
NoAudio* NoAudio::create() { NoAudio* NoAudio::create() {

View File

@ -1,7 +1,7 @@
#ifndef AUDIO_NOAUDIO_H_ #ifndef AUDIO_NOAUDIO_HPP_
#define AUDIO_NOAUDIO_H_ #define AUDIO_NOAUDIO_HPP_
#include "audio.h" #include "audio.hpp"
namespace audio { namespace audio {
class NoSound : public Sound { class NoSound : public Sound {
@ -19,7 +19,7 @@ namespace audio {
return pcm; return pcm;
} }
Speaker* newInstance(int priority, int channel) const override { std::unique_ptr<Speaker> newInstance(int priority, int channel) const override {
return nullptr; return nullptr;
} }
}; };
@ -42,7 +42,7 @@ namespace audio {
void bindSpeaker(speakerid_t speaker) override { void bindSpeaker(speakerid_t speaker) override {
} }
Speaker* createSpeaker(bool loop, int channel) override{ std::unique_ptr<Speaker> createSpeaker(bool loop, int channel) override {
return nullptr; return nullptr;
} }
@ -65,8 +65,8 @@ namespace audio {
public: public:
~NoAudio() {} ~NoAudio() {}
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override; std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override; std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener( void setListener(
glm::vec3 position, glm::vec3 position,
@ -85,4 +85,4 @@ namespace audio {
}; };
} }
#endif // AUDIO_NOAUDIO_H_ #endif // AUDIO_NOAUDIO_HPP_

View File

@ -1,14 +1,14 @@
#include "audio.h" #include "audio.hpp"
#include "NoAudio.hpp"
#include "AL/ALAudio.hpp"
#include "../coders/wav.hpp"
#include "../coders/ogg.hpp"
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include "NoAudio.h"
#include "AL/ALAudio.h"
#include "../coders/wav.h"
#include "../coders/ogg.h"
namespace audio { namespace audio {
static speakerid_t nextId = 1; static speakerid_t nextId = 1;
static Backend* backend; static Backend* backend;
@ -72,9 +72,6 @@ size_t PCMStream::readFully(char* buffer, size_t bufferSize, bool loop) {
if (loop) { if (loop) {
seek(0); seek(0);
} }
if (bufferSize == 0) {
return size;
}
} while (loop); } while (loop);
return size; return size;
} }
@ -94,7 +91,7 @@ public:
seekable(seekable) seekable(seekable)
{} {}
size_t read(char* buffer, size_t bufferSize) override { size_t read(char*, size_t bufferSize) override {
if (closed) { if (closed) {
return 0; return 0;
} }
@ -159,7 +156,7 @@ void audio::initialize(bool enabled) {
create_channel("master"); create_channel("master");
} }
PCM* audio::load_PCM(const fs::path& file, bool headerOnly) { std::unique_ptr<PCM> audio::load_PCM(const fs::path& file, bool headerOnly) {
if (!fs::exists(file)) { if (!fs::exists(file)) {
throw std::runtime_error("file not found '"+file.u8string()+"'"); throw std::runtime_error("file not found '"+file.u8string()+"'");
} }
@ -172,16 +169,16 @@ PCM* audio::load_PCM(const fs::path& file, bool headerOnly) {
throw std::runtime_error("unsupported audio format"); throw std::runtime_error("unsupported audio format");
} }
Sound* audio::load_sound(const fs::path& file, bool keepPCM) { std::unique_ptr<Sound> audio::load_sound(const fs::path& file, bool keepPCM) {
std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy())); std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy()).release());
return create_sound(pcm, keepPCM); return create_sound(pcm, keepPCM);
} }
Sound* audio::create_sound(std::shared_ptr<PCM> pcm, bool keepPCM) { std::unique_ptr<Sound> audio::create_sound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return backend->createSound(pcm, keepPCM); return backend->createSound(pcm, keepPCM);
} }
PCMStream* audio::open_PCM_stream(const fs::path& file) { std::unique_ptr<PCMStream> audio::open_PCM_stream(const fs::path& file) {
std::string ext = file.extension().u8string(); std::string ext = file.extension().u8string();
if (ext == ".wav" || ext == ".WAV") { if (ext == ".wav" || ext == ".WAV") {
return wav::create_stream(file); return wav::create_stream(file);
@ -191,7 +188,7 @@ PCMStream* audio::open_PCM_stream(const fs::path& file) {
throw std::runtime_error("unsupported audio stream format"); throw std::runtime_error("unsupported audio stream format");
} }
Stream* audio::open_stream(const fs::path& file, bool keepSource) { std::unique_ptr<Stream> audio::open_stream(const fs::path& file, bool keepSource) {
if (!keepSource && backend->isDummy()) { if (!keepSource && backend->isDummy()) {
auto header = load_PCM(file, true); auto header = load_PCM(file, true);
// using void source sized as audio instead of actual audio file // using void source sized as audio instead of actual audio file
@ -206,7 +203,7 @@ Stream* audio::open_stream(const fs::path& file, bool keepSource) {
); );
} }
Stream* audio::open_stream(std::shared_ptr<PCMStream> stream, bool keepSource) { std::unique_ptr<Stream> audio::open_stream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return backend->openStream(stream, keepSource); return backend->openStream(stream, keepSource);
} }
@ -258,16 +255,17 @@ speakerid_t audio::play(
sound = sound->variants.at(index).get(); sound = sound->variants.at(index).get();
} }
} }
Speaker* speaker = sound->newInstance(priority, channel); auto speaker_ptr = sound->newInstance(priority, channel);
if (speaker == nullptr) { if (speaker_ptr == nullptr) {
remove_lower_priority_speaker(priority); remove_lower_priority_speaker(priority);
speaker = sound->newInstance(priority, channel); speaker_ptr = sound->newInstance(priority, channel);
} }
if (speaker == nullptr) { if (speaker_ptr == nullptr) {
return 0; return 0;
} }
auto speaker = speaker_ptr.get();
speakerid_t id = nextId++; speakerid_t id = nextId++;
speakers.emplace(id, speaker); speakers.emplace(id, std::move(speaker_ptr));
speaker->setPosition(position); speaker->setPosition(position);
speaker->setVolume(volume); speaker->setVolume(volume);
speaker->setPitch(pitch); speaker->setPitch(pitch);
@ -286,17 +284,18 @@ speakerid_t audio::play(
bool loop, bool loop,
int channel int channel
) { ) {
Speaker* speaker = stream->createSpeaker(loop, channel); auto speaker_ptr = stream->createSpeaker(loop, channel);
if (speaker == nullptr) { if (speaker_ptr == nullptr) {
remove_lower_priority_speaker(PRIORITY_HIGH); remove_lower_priority_speaker(PRIORITY_HIGH);
speaker = stream->createSpeaker(loop, channel); speaker_ptr = stream->createSpeaker(loop, channel);
} }
if (speaker == nullptr) { if (speaker_ptr == nullptr) {
return 0; return 0;
} }
auto speaker = speaker_ptr.get();
speakerid_t id = nextId++; speakerid_t id = nextId++;
streams.emplace(id, stream); streams.emplace(id, stream);
speakers.emplace(id, speaker); speakers.emplace(id, std::move(speaker_ptr));
stream->bindSpeaker(id); stream->bindSpeaker(id);
speaker->setPosition(position); speaker->setPosition(position);
@ -383,13 +382,12 @@ void audio::update(double delta) {
entry.second->update(delta); entry.second->update(delta);
} }
float masterVolume = channels.at(0)->getVolume();
for (auto it = speakers.begin(); it != speakers.end();) { for (auto it = speakers.begin(); it != speakers.end();) {
auto speaker = it->second.get(); auto speaker = it->second.get();
int speakerChannel = speaker->getChannel(); int speakerChannel = speaker->getChannel();
auto channel = get_channel(speakerChannel); auto channel = get_channel(speakerChannel);
if (channel != nullptr) { if (channel != nullptr) {
speaker->update(channel, speakerChannel == 0 ? 1.0f : masterVolume); speaker->update(channel);
} }
if (speaker->isStopped()) { if (speaker->isStopped()) {
streams.erase(it->first); streams.erase(it->first);

View File

@ -1,11 +1,12 @@
#ifndef AUDIO_AUDIO_H_ #ifndef AUDIO_AUDIO_HPP_
#define AUDIO_AUDIO_H_ #define AUDIO_AUDIO_HPP_
#include "../typedefs.hpp"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <filesystem> #include <filesystem>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "../typedefs.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -178,7 +179,7 @@ namespace audio {
/// @param loop is stream looped (required for correct buffers preload) /// @param loop is stream looped (required for correct buffers preload)
/// @param channel channel index /// @param channel channel index
/// @return speaker id or 0 /// @return speaker id or 0
virtual Speaker* createSpeaker(bool loop, int channel) = 0; virtual std::unique_ptr<Speaker> createSpeaker(bool loop, int channel) = 0;
/// @brief Unbind previous speaker and bind new speaker to the stream /// @brief Unbind previous speaker and bind new speaker to the stream
/// @param speaker speaker id or 0 if all you need is unbind speaker /// @param speaker speaker id or 0 if all you need is unbind speaker
@ -224,7 +225,7 @@ namespace audio {
/// @param channel channel index /// @param channel channel index
/// @return new speaker with sound bound or nullptr /// @return new speaker with sound bound or nullptr
/// if all speakers are in use /// if all speakers are in use
virtual Speaker* newInstance(int priority, int channel) const = 0; virtual std::unique_ptr<Speaker> newInstance(int priority, int channel) const = 0;
}; };
/// @brief Audio source controller interface. /// @brief Audio source controller interface.
@ -235,8 +236,7 @@ namespace audio {
/// @brief Synchronize the speaker with channel settings /// @brief Synchronize the speaker with channel settings
/// @param channel speaker channel /// @param channel speaker channel
/// @param masterVolume volume of the master channel virtual void update(const Channel* channel) = 0;
virtual void update(const Channel* channel, float masterVolume) = 0;
/// @brief Check speaker channel index /// @brief Check speaker channel index
virtual int getChannel() const = 0; virtual int getChannel() const = 0;
@ -336,8 +336,8 @@ namespace audio {
public: public:
virtual ~Backend() {}; virtual ~Backend() {};
virtual Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0; virtual std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
virtual Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) = 0; virtual std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) = 0;
virtual void setListener( virtual void setListener(
glm::vec3 position, glm::vec3 position,
glm::vec3 velocity, glm::vec3 velocity,
@ -353,52 +353,52 @@ namespace audio {
/// @brief Initialize audio system or use no audio mode /// @brief Initialize audio system or use no audio mode
/// @param enabled try to initialize actual audio /// @param enabled try to initialize actual audio
extern void initialize(bool enabled); void initialize(bool enabled);
/// @brief Load audio file info and PCM data /// @brief Load audio file info and PCM data
/// @param file audio file /// @param file audio file
/// @param headerOnly read header only /// @param headerOnly read header only
/// @throws std::runtime_error if I/O error ocurred or format is unknown /// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @return PCM audio data /// @return PCM audio data
extern PCM* load_PCM(const fs::path& file, bool headerOnly); std::unique_ptr<PCM> load_PCM(const fs::path& file, bool headerOnly);
/// @brief Load sound from file /// @brief Load sound from file
/// @param file audio file path /// @param file audio file path
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM /// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
/// @throws std::runtime_error if I/O error ocurred or format is unknown /// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @return new Sound instance /// @return new Sound instance
extern Sound* load_sound(const fs::path& file, bool keepPCM); std::unique_ptr<Sound> load_sound(const fs::path& file, bool keepPCM);
/// @brief Create new sound from PCM data /// @brief Create new sound from PCM data
/// @param pcm PCM data /// @param pcm PCM data
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM /// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
/// @return new Sound instance /// @return new Sound instance
extern Sound* create_sound(std::shared_ptr<PCM> pcm, bool keepPCM); std::unique_ptr<Sound> create_sound(std::shared_ptr<PCM> pcm, bool keepPCM);
/// @brief Open new PCM stream from file /// @brief Open new PCM stream from file
/// @param file audio file path /// @param file audio file path
/// @throws std::runtime_error if I/O error ocurred or format is unknown /// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @return new PCMStream instance /// @return new PCMStream instance
extern PCMStream* open_PCM_stream(const fs::path& file); std::unique_ptr<PCMStream> open_PCM_stream(const fs::path& file);
/// @brief Open new audio stream from file /// @brief Open new audio stream from file
/// @param file audio file path /// @param file audio file path
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource /// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @return new Stream instance /// @return new Stream instance
extern Stream* open_stream(const fs::path& file, bool keepSource); std::unique_ptr<Stream> open_stream(const fs::path& file, bool keepSource);
/// @brief Open new audio stream from source /// @brief Open new audio stream from source
/// @param stream PCM data source /// @param stream PCM data source
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource /// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @return new Stream instance /// @return new Stream instance
extern Stream* open_stream(std::shared_ptr<PCMStream> stream, bool keepSource); std::unique_ptr<Stream> open_stream(std::shared_ptr<PCMStream> stream, bool keepSource);
/// @brief Configure 3D listener /// @brief Configure 3D listener
/// @param position listener position /// @param position listener position
/// @param velocity listener velocity (used for Doppler effect) /// @param velocity listener velocity (used for Doppler effect)
/// @param lookAt point the listener look at /// @param lookAt point the listener look at
/// @param up camera up vector /// @param up camera up vector
extern void set_listener( void set_listener(
glm::vec3 position, glm::vec3 position,
glm::vec3 velocity, glm::vec3 velocity,
glm::vec3 lookAt, glm::vec3 lookAt,
@ -416,7 +416,7 @@ namespace audio {
/// (PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH) /// (PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH)
/// @param channel channel index /// @param channel channel index
/// @return speaker id or 0 /// @return speaker id or 0
extern speakerid_t play( speakerid_t play(
Sound* sound, Sound* sound,
glm::vec3 position, glm::vec3 position,
bool relative, bool relative,
@ -436,7 +436,7 @@ namespace audio {
/// @param loop loop stream /// @param loop loop stream
/// @param channel channel index /// @param channel channel index
/// @return speaker id or 0 /// @return speaker id or 0
extern speakerid_t play( speakerid_t play(
std::shared_ptr<Stream> stream, std::shared_ptr<Stream> stream,
glm::vec3 position, glm::vec3 position,
bool relative, bool relative,
@ -455,7 +455,7 @@ namespace audio {
/// @param loop loop stream /// @param loop loop stream
/// @param channel channel index /// @param channel channel index
/// @return speaker id or 0 /// @return speaker id or 0
extern speakerid_t play_stream( speakerid_t play_stream(
const fs::path& file, const fs::path& file,
glm::vec3 position, glm::vec3 position,
bool relative, bool relative,
@ -468,49 +468,49 @@ namespace audio {
/// @brief Get speaker by id /// @brief Get speaker by id
/// @param id speaker id /// @param id speaker id
/// @return speaker or nullptr /// @return speaker or nullptr
extern Speaker* get_speaker(speakerid_t id); Speaker* get_speaker(speakerid_t id);
/// @brief Create new channel. /// @brief Create new channel.
/// All non-builtin channels will be destroyed on audio::reset() call /// All non-builtin channels will be destroyed on audio::reset() call
/// @param name channel name /// @param name channel name
/// @return new channel index /// @return new channel index
extern int create_channel(const std::string& name); int create_channel(const std::string& name);
/// @brief Get channel index by name /// @brief Get channel index by name
/// @param name channel name /// @param name channel name
/// @return channel index or -1 /// @return channel index or -1
extern int get_channel_index(const std::string& name); int get_channel_index(const std::string& name);
/// @brief Get channel by index. 0 - is master channel /// @brief Get channel by index. 0 - is master channel
/// @param index channel index /// @param index channel index
/// @return channel or nullptr /// @return channel or nullptr
extern Channel* get_channel(int index); Channel* get_channel(int index);
/// @brief Get channel by name. /// @brief Get channel by name.
/// @param name channel name /// @param name channel name
/// @return channel or nullptr /// @return channel or nullptr
extern Channel* get_channel(const std::string& name); Channel* get_channel(const std::string& name);
/// @brief Get stream associated with speaker /// @brief Get stream associated with speaker
/// @param id speaker id /// @param id speaker id
/// @return stream or nullptr /// @return stream or nullptr
extern std::shared_ptr<Stream> get_associated_stream(speakerid_t id); std::shared_ptr<Stream> get_associated_stream(speakerid_t id);
/// @brief Get alive speakers number (including paused) /// @brief Get alive speakers number (including paused)
extern size_t count_speakers(); size_t count_speakers();
/// @brief Get playing streams number (including paused) /// @brief Get playing streams number (including paused)
extern size_t count_streams(); size_t count_streams();
/// @brief Update audio streams and sound instanced /// @brief Update audio streams and sound instanced
/// @param delta time elapsed since the last update (seconds) /// @param delta time elapsed since the last update (seconds)
extern void update(double delta); void update(double delta);
/// @brief Stop all playing audio in channel, reset channel state /// @brief Stop all playing audio in channel, reset channel state
extern void reset_channel(int channel); void reset_channel(int channel);
/// @brief Finalize audio system /// @brief Finalize audio system
extern void close(); void close();
}; };
#endif // AUDIO_AUDIO_H_ #endif // AUDIO_AUDIO_HPP_

View File

@ -1,11 +1,13 @@
#include "GLSLExtension.h" #include "GLSLExtension.hpp"
#include "../util/stringutil.hpp"
#include "../typedefs.hpp"
#include "../files/files.hpp"
#include "../files/engine_paths.hpp"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "../util/stringutil.h"
#include "../typedefs.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -81,7 +83,7 @@ inline void source_line(std::stringstream& ss, uint linenum) {
ss << "#line " << linenum << "\n"; ss << "#line " << linenum << "\n";
} }
const std::string GLSLExtension::process(const fs::path file, const std::string& source) { const std::string GLSLExtension::process(const fs::path& file, const std::string& source) {
std::stringstream ss; std::stringstream ss;
size_t pos = 0; size_t pos = 0;
uint linenum = 1; uint linenum = 1;

View File

@ -1,5 +1,5 @@
#ifndef CODERS_GLSL_EXTESION_H_ #ifndef CODERS_GLSL_EXTESION_HPP_
#define CODERS_GLSL_EXTESION_H_ #define CODERS_GLSL_EXTESION_HPP_
#include <string> #include <string>
#include <vector> #include <vector>
@ -29,7 +29,10 @@ public:
bool hasHeader(const std::string& name) const; bool hasHeader(const std::string& name) const;
bool hasDefine(const std::string& name) const; bool hasDefine(const std::string& name) const;
const std::string process(const std::filesystem::path file, const std::string& source); const std::string process(
const std::filesystem::path& file,
const std::string& source
);
}; };
#endif // CODERS_GLSL_EXTESION_H_ #endif // CODERS_GLSL_EXTESION_HPP_

View File

@ -1,29 +1,32 @@
#include "binary_json.h" #include "binary_json.hpp"
#include "gzip.hpp"
#include "byte_utils.hpp"
#include "../data/dynamic.hpp"
#include <stdexcept> #include <stdexcept>
#include "gzip.h"
#include "byte_utils.h"
using namespace json; using namespace json;
using namespace dynamic; using namespace dynamic;
static void to_binary(ByteBuilder& builder, const Value* value) { static void to_binary(ByteBuilder& builder, const Value& value) {
switch (value->type) { switch (static_cast<Type>(value.index())) {
case valtype::map: { case Type::none:
std::vector<ubyte> bytes = to_binary(value->value.map); throw std::runtime_error("none value is not implemented");
case Type::map: {
auto bytes = to_binary(std::get<Map_sptr>(value).get());
builder.put(bytes.data(), bytes.size()); builder.put(bytes.data(), bytes.size());
break; break;
} }
case valtype::list: case Type::list:
builder.put(BJSON_TYPE_LIST); builder.put(BJSON_TYPE_LIST);
for (auto& element : value->value.list->values) { for (auto& element : std::get<List_sptr>(value)->values) {
to_binary(builder, element.get()); to_binary(builder, element);
} }
builder.put(BJSON_END); builder.put(BJSON_END);
break; break;
case valtype::integer: { case Type::integer: {
int64_t val = value->value.integer; auto val = std::get<integer_t>(value);
if (val >= 0 && val <= 255) { if (val >= 0 && val <= 255) {
builder.put(BJSON_TYPE_BYTE); builder.put(BJSON_TYPE_BYTE);
builder.put(val); builder.put(val);
@ -39,22 +42,22 @@ static void to_binary(ByteBuilder& builder, const Value* value) {
} }
break; break;
} }
case valtype::number: case Type::number:
builder.put(BJSON_TYPE_NUMBER); builder.put(BJSON_TYPE_NUMBER);
builder.putFloat64(value->value.decimal); builder.putFloat64(std::get<number_t>(value));
break; break;
case valtype::boolean: case Type::boolean:
builder.put(BJSON_TYPE_FALSE + value->value.boolean); builder.put(BJSON_TYPE_FALSE + std::get<bool>(value));
break; break;
case valtype::string: case Type::string:
builder.put(BJSON_TYPE_STRING); builder.put(BJSON_TYPE_STRING);
builder.put(*value->value.str); builder.put(std::get<std::string>(value));
break; break;
} }
} }
static List* array_from_binary(ByteReader& reader); static std::unique_ptr<List> array_from_binary(ByteReader& reader);
static Map* object_from_binary(ByteReader& reader); static std::unique_ptr<Map> object_from_binary(ByteReader& reader);
std::vector<ubyte> json::to_binary(const Map* obj, bool compress) { std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
if (compress) { if (compress) {
@ -70,7 +73,7 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
// writing entries // writing entries
for (auto& entry : obj->values) { for (auto& entry : obj->values) {
builder.putCStr(entry.first.c_str()); builder.putCStr(entry.first.c_str());
to_binary(builder, entry.second.get()); to_binary(builder, entry.second);
} }
// terminating byte // terminating byte
builder.put(BJSON_END); builder.put(BJSON_END);
@ -80,79 +83,56 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
return builder.build(); return builder.build();
} }
static Value* value_from_binary(ByteReader& reader) { static Value value_from_binary(ByteReader& reader) {
ubyte typecode = reader.get(); ubyte typecode = reader.get();
valtype type;
valvalue val;
switch (typecode) { switch (typecode) {
case BJSON_TYPE_DOCUMENT: case BJSON_TYPE_DOCUMENT:
type = valtype::map;
reader.getInt32(); reader.getInt32();
val.map = object_from_binary(reader); return Map_sptr(object_from_binary(reader).release());
break;
case BJSON_TYPE_LIST: case BJSON_TYPE_LIST:
type = valtype::list; return List_sptr(array_from_binary(reader).release());
val.list = array_from_binary(reader);
break;
case BJSON_TYPE_BYTE: case BJSON_TYPE_BYTE:
type = valtype::integer; return static_cast<integer_t>(reader.get());
val.integer = reader.get();
break;
case BJSON_TYPE_INT16: case BJSON_TYPE_INT16:
type = valtype::integer; return static_cast<integer_t>(reader.getInt16());
val.integer = reader.getInt16();
break;
case BJSON_TYPE_INT32: case BJSON_TYPE_INT32:
type = valtype::integer; return static_cast<integer_t>(reader.getInt32());
val.integer = reader.getInt32();
break;
case BJSON_TYPE_INT64: case BJSON_TYPE_INT64:
type = valtype::integer; return reader.getInt64();
val.integer = reader.getInt64();
break;
case BJSON_TYPE_NUMBER: case BJSON_TYPE_NUMBER:
type = valtype::number; return reader.getFloat64();
val.decimal = reader.getFloat64();
break;
case BJSON_TYPE_FALSE: case BJSON_TYPE_FALSE:
case BJSON_TYPE_TRUE: case BJSON_TYPE_TRUE:
type = valtype::boolean; return (typecode - BJSON_TYPE_FALSE) != 0;
val.boolean = typecode - BJSON_TYPE_FALSE;
break;
case BJSON_TYPE_STRING: case BJSON_TYPE_STRING:
type = valtype::string; return reader.getString();
val.str = new std::string(reader.getString());
break;
default: default:
throw std::runtime_error( throw std::runtime_error(
"type "+std::to_string(typecode)+" is not supported"); "type "+std::to_string(typecode)+" is not supported"
);
} }
return new Value(type, val);
} }
static List* array_from_binary(ByteReader& reader) { static std::unique_ptr<List> array_from_binary(ByteReader& reader) {
auto array = std::make_unique<List>(); auto array = std::make_unique<List>();
auto& items = array->values;
while (reader.peek() != BJSON_END) { while (reader.peek() != BJSON_END) {
items.push_back(std::unique_ptr<Value>(value_from_binary(reader))); array->put(value_from_binary(reader));
} }
reader.get(); reader.get();
return array.release(); return array;
} }
static Map* object_from_binary(ByteReader& reader) { static std::unique_ptr<Map> object_from_binary(ByteReader& reader) {
auto obj = std::make_unique<Map>(); auto obj = std::make_unique<Map>();
auto& map = obj->values;
while (reader.peek() != BJSON_END) { while (reader.peek() != BJSON_END) {
const char* key = reader.getCString(); const char* key = reader.getCString();
Value* value = value_from_binary(reader); obj->put(key, value_from_binary(reader));
map.insert(std::make_pair(key, value));
} }
reader.get(); reader.get();
return obj.release(); return obj;
} }
std::unique_ptr<Map> json::from_binary(const ubyte* src, size_t size) { std::shared_ptr<Map> json::from_binary(const ubyte* src, size_t size) {
if (size < 2) { if (size < 2) {
throw std::runtime_error("bytes length is less than 2"); throw std::runtime_error("bytes length is less than 2");
} }
@ -162,12 +142,12 @@ std::unique_ptr<Map> json::from_binary(const ubyte* src, size_t size) {
return from_binary(data.data(), data.size()); return from_binary(data.data(), data.size());
} else { } else {
ByteReader reader(src, size); ByteReader reader(src, size);
std::unique_ptr<Value> value (value_from_binary(reader)); Value value = value_from_binary(reader);
if (value->type != valtype::map) {
if (auto map = std::get_if<Map_sptr>(&value)) {
return *map;
} else {
throw std::runtime_error("root value is not an object"); throw std::runtime_error("root value is not an object");
} }
std::unique_ptr<Map> obj (value->value.map);
value->value.map = nullptr;
return obj;
} }
} }

View File

@ -1,9 +1,14 @@
#ifndef CODERS_BINARY_JSON_H_ #ifndef CODERS_BINARY_JSON_HPP_
#define CODERS_BINARY_JSON_H_ #define CODERS_BINARY_JSON_HPP_
#include "../typedefs.hpp"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "../data/dynamic.h"
namespace dynamic {
class Map;
}
namespace json { namespace json {
const int BJSON_END = 0x0; const int BJSON_END = 0x0;
@ -22,7 +27,7 @@ namespace json {
const int BJSON_TYPE_CDOCUMENT = 0x1F; const int BJSON_TYPE_CDOCUMENT = 0x1F;
extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false); extern std::vector<ubyte> to_binary(const dynamic::Map* obj, bool compress=false);
extern std::unique_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size); extern std::shared_ptr<dynamic::Map> from_binary(const ubyte* src, size_t size);
} }
#endif // CODERS_BINARY_JSON_H_ #endif // CODERS_BINARY_JSON_HPP_

View File

@ -1,4 +1,4 @@
#include "byte_utils.h" #include "byte_utils.hpp"
#include <cstring> #include <cstring>
#include <limits> #include <limits>

View File

@ -1,9 +1,10 @@
#ifndef CODERS_BYTE_UTILS_H_ #ifndef CODERS_BYTE_UTILS_HPP_
#define CODERS_BYTE_UTILS_H_ #define CODERS_BYTE_UTILS_HPP_
#include "../typedefs.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
#include "../typedefs.h"
/* byteorder: little-endian */ /* byteorder: little-endian */
class ByteBuilder { class ByteBuilder {
@ -44,7 +45,7 @@ public:
std::vector<ubyte> build(); std::vector<ubyte> build();
}; };
/* byteorder: little-endian */ /// byteorder: little-endian
class ByteReader { class ByteReader {
const ubyte* data; const ubyte* data;
size_t size; size_t size;
@ -54,26 +55,29 @@ public:
ByteReader(const ubyte* data); ByteReader(const ubyte* data);
void checkMagic(const char* data, size_t size); void checkMagic(const char* data, size_t size);
/* Read one byte (unsigned 8 bit integer) */ /// @brief Read one byte (unsigned 8 bit integer)
ubyte get(); ubyte get();
/* Read one byte (unsigned 8 bit integer) without pointer move */ /// @brief Read one byte (unsigned 8 bit integer) without pointer move
ubyte peek(); ubyte peek();
/* Read signed 16 bit integer */ /// @brief Read signed 16 bit integer
int16_t getInt16(); int16_t getInt16();
/* Read signed 32 bit integer */ /// @brief Read signed 32 bit integer
int32_t getInt32(); int32_t getInt32();
/* Read signed 64 bit integer */ /// @brief Read signed 64 bit integer
int64_t getInt64(); int64_t getInt64();
/* Read 32 bit floating-point number */ /// @brief Read 32 bit floating-point number
float getFloat32(); float getFloat32();
/* Read 64 bit floating-point number */ /// @brief Read 64 bit floating-point number
double getFloat64(); double getFloat64();
/// @brief Read C-String
const char* getCString(); const char* getCString();
/// @brief Read string with unsigned 32 bit number before (length)
std::string getString(); std::string getString();
/// @return true if there is at least one byte remains
bool hasNext() const; bool hasNext() const;
const ubyte* pointer() const; const ubyte* pointer() const;
void skip(size_t n); void skip(size_t n);
}; };
#endif // CODERS_BYTE_UTILS_H_ #endif // CODERS_BYTE_UTILS_HPP_

View File

@ -1,4 +1,6 @@
#include "commons.h" #include "commons.hpp"
#include "../util/stringutil.hpp"
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
@ -14,13 +16,19 @@ inline double power(double base, int64_t power) {
parsing_error::parsing_error( parsing_error::parsing_error(
std::string message, std::string message,
std::string filename, std::string_view filename,
std::string source, std::string_view source,
uint pos, uint pos,
uint line, uint line,
uint linestart) uint linestart
: std::runtime_error(message), filename(filename), source(source), ) : std::runtime_error(message), filename(filename),
pos(pos), line(line), linestart(linestart) { pos(pos), line(line), linestart(linestart)
{
size_t end = source.find("\n", linestart);
if (end == std::string::npos) {
end = source.length();
}
this->source = source.substr(linestart, end-linestart);
} }
std::string parsing_error::errorLog() const { std::string parsing_error::errorLog() const {
@ -28,45 +36,18 @@ std::string parsing_error::errorLog() const {
uint linepos = pos - linestart; uint linepos = pos - linestart;
ss << "parsing error in file '" << filename; ss << "parsing error in file '" << filename;
ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n"; ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n";
size_t end = source.find("\n", linestart); ss << source << "\n";
if (end == std::string::npos) {
end = source.length();
}
ss << source.substr(linestart, end-linestart) << "\n";
for (uint i = 0; i < linepos; i++) { for (uint i = 0; i < linepos; i++) {
ss << " "; ss << " ";
} }
ss << "^"; ss << "^";
return ss.str(); return ss.str();
} }
std::string escape_string(std::string s) { BasicParser::BasicParser(
std::stringstream ss; std::string_view file,
ss << '"'; std::string_view source
for (char c : s) { ) : filename(file), source(source) {
switch (c) {
case '\n': ss << "\\n"; break;
case '\r': ss << "\\r"; break;
case '\t': ss << "\\t"; break;
case '\f': ss << "\\f"; break;
case '\b': ss << "\\b"; break;
case '"': ss << "\\\""; break;
case '\\': ss << "\\\\"; break;
default:
if (c < ' ') {
ss << "\\" << std::oct << uint(ubyte(c));
break;
}
ss << c;
break;
}
}
ss << '"';
return ss.str();
}
BasicParser::BasicParser(std::string file, std::string source) : filename(file), source(source) {
} }
void BasicParser::skipWhitespace() { void BasicParser::skipWhitespace() {
@ -151,7 +132,7 @@ void BasicParser::expect(const std::string& substring) {
return; return;
for (uint i = 0; i < substring.length(); i++) { for (uint i = 0; i < substring.length(); i++) {
if (source.length() <= pos + i || source[pos+i] != substring[i]) { if (source.length() <= pos + i || source[pos+i] != substring[i]) {
throw error(escape_string(substring)+" expected"); throw error(util::quote(substring)+" expected");
} }
} }
pos += substring.length(); pos += substring.length();
@ -173,8 +154,13 @@ void BasicParser::expectNewLine() {
} }
} }
void BasicParser::goBack() { void BasicParser::goBack(size_t count) {
if (pos) pos--; if (pos < count) {
throw std::runtime_error("pos < jump");
}
if (pos) {
pos -= count;
}
} }
char BasicParser::peek() { char BasicParser::peek() {
@ -185,6 +171,21 @@ char BasicParser::peek() {
return source[pos]; return source[pos];
} }
char BasicParser::peekNoJump() {
if (pos >= source.length()) {
throw error("unexpected end");
}
return source[pos];
}
std::string_view BasicParser::readUntil(char c) {
int start = pos;
while (hasNext() && source[pos] != c) {
pos++;
}
return source.substr(start, pos-start);
}
std::string BasicParser::parseName() { std::string BasicParser::parseName() {
char c = peek(); char c = peek();
if (!is_identifier_start(c)) { if (!is_identifier_start(c)) {
@ -198,7 +199,7 @@ std::string BasicParser::parseName() {
while (hasNext() && is_identifier_part(source[pos])) { while (hasNext() && is_identifier_part(source[pos])) {
pos++; pos++;
} }
return source.substr(start, pos-start); return std::string(source.substr(start, pos-start));
} }
int64_t BasicParser::parseSimpleInt(int base) { int64_t BasicParser::parseSimpleInt(int base) {
@ -225,27 +226,23 @@ int64_t BasicParser::parseSimpleInt(int base) {
return value; return value;
} }
bool BasicParser::parseNumber(int sign, number_u& out) { dynamic::Value BasicParser::parseNumber(int sign) {
char c = peek(); char c = peek();
int base = 10; int base = 10;
if (c == '0' && pos + 1 < source.length() && if (c == '0' && pos + 1 < source.length() &&
(base = is_box(source[pos+1])) != 10) { (base = is_box(source[pos+1])) != 10) {
pos += 2; pos += 2;
out.ival = parseSimpleInt(base); return parseSimpleInt(base);
return true;
} else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') { } else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') {
pos += 3; pos += 3;
out.fval = INFINITY * sign; return INFINITY * sign;
return false;
} else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') { } else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') {
pos += 3; pos += 3;
out.fval = NAN * sign; return NAN * sign;
return false;
} }
int64_t value = parseSimpleInt(base); int64_t value = parseSimpleInt(base);
if (!hasNext()) { if (!hasNext()) {
out.ival = value * sign; return value * sign;
return true;
} }
c = source[pos]; c = source[pos];
if (c == 'e' || c == 'E') { if (c == 'e' || c == 'E') {
@ -257,8 +254,7 @@ bool BasicParser::parseNumber(int sign, number_u& out) {
} else if (peek() == '+'){ } else if (peek() == '+'){
pos++; pos++;
} }
out.fval = sign * value * power(10.0, s * parseSimpleInt(10)); return sign * value * power(10.0, s * parseSimpleInt(10));
return false;
} }
if (c == '.') { if (c == '.') {
pos++; pos++;
@ -284,14 +280,11 @@ bool BasicParser::parseNumber(int sign, number_u& out) {
} else if (peek() == '+'){ } else if (peek() == '+'){
pos++; pos++;
} }
out.fval = sign * dvalue * power(10.0, s * parseSimpleInt(10)); return sign * dvalue * power(10.0, s * parseSimpleInt(10));
return false;
} }
out.fval = sign * dvalue; return sign * dvalue;
return false;
} }
out.ival = sign * value; return sign * value;
return true;
} }
std::string BasicParser::parseString(char quote, bool closeRequired) { std::string BasicParser::parseString(char quote, bool closeRequired) {

View File

@ -1,14 +1,11 @@
#ifndef CODERS_COMMONS_H_ #ifndef CODERS_COMMONS_HPP_
#define CODERS_COMMONS_H_ #define CODERS_COMMONS_HPP_
#include "../data/dynamic.hpp"
#include "../typedefs.hpp"
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
#include "../typedefs.h"
union number_u {
double fval;
int64_t ival;
};
inline int is_box(int c) { inline int is_box(int c) {
switch (c) { switch (c) {
@ -34,11 +31,11 @@ inline bool is_whitespace(int c) {
} }
inline bool is_identifier_start(int c) { inline bool is_identifier_start(int c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '-' || c == '.'; return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '.';
} }
inline bool is_identifier_part(int c) { inline bool is_identifier_part(int c) {
return is_identifier_start(c) || is_digit(c); return is_identifier_start(c) || is_digit(c) || c == '-';
} }
inline int hexchar2int(int c) { inline int hexchar2int(int c) {
@ -54,8 +51,6 @@ inline int hexchar2int(int c) {
return -1; return -1;
} }
extern std::string escape_string(std::string s);
class parsing_error : public std::runtime_error { class parsing_error : public std::runtime_error {
public: public:
std::string filename; std::string filename;
@ -64,20 +59,21 @@ public:
uint line; uint line;
uint linestart; uint linestart;
parsing_error(std::string message, parsing_error(
std::string filename, std::string message,
std::string source, std::string_view filename,
uint pos, std::string_view source,
uint line, uint pos,
uint linestart); uint line,
uint linestart
);
std::string errorLog() const; std::string errorLog() const;
}; };
class BasicParser { class BasicParser {
protected: protected:
std::string filename; std::string_view filename;
std::string source; std::string_view source;
uint pos = 0; uint pos = 0;
uint line = 1; uint line = 1;
uint linestart = 0; uint linestart = 0;
@ -88,21 +84,25 @@ protected:
bool skipTo(const std::string& substring); bool skipTo(const std::string& substring);
void expect(char expected); void expect(char expected);
void expect(const std::string& substring); void expect(const std::string& substring);
char peek();
char nextChar();
bool hasNext();
bool isNext(const std::string& substring); bool isNext(const std::string& substring);
void expectNewLine(); void expectNewLine();
void goBack(); void goBack(size_t count=1);
std::string parseName();
int64_t parseSimpleInt(int base); int64_t parseSimpleInt(int base);
bool parseNumber(int sign, number_u& out); dynamic::Value parseNumber(int sign);
std::string parseString(char chr, bool closeRequired=true); std::string parseString(char chr, bool closeRequired=true);
parsing_error error(std::string message); parsing_error error(std::string message);
BasicParser(std::string filename, std::string source); public:
std::string_view readUntil(char c);
std::string parseName();
bool hasNext();
char peek();
char peekNoJump();
char nextChar();
BasicParser(std::string_view file, std::string_view source);
}; };
#endif // CODERS_COMMONS_H_ #endif // CODERS_COMMONS_HPP_

View File

@ -1,10 +1,11 @@
#include "gzip.h" #include "gzip.hpp"
#include "byte_utils.hpp"
#define ZLIB_CONST #define ZLIB_CONST
#include <zlib.h> #include <zlib.h>
#include <math.h> #include <math.h>
#include <memory> #include <memory>
#include "byte_utils.h"
std::vector<ubyte> gzip::compress(const ubyte* src, size_t size) { std::vector<ubyte> gzip::compress(const ubyte* src, size_t size) {
size_t buffer_size = 23+size*1.01; size_t buffer_size = 23+size*1.01;

View File

@ -1,8 +1,8 @@
#ifndef CODERS_GZIP_H_ #ifndef CODERS_GZIP_HPP_
#define CODERS_GZIP_H_ #define CODERS_GZIP_HPP_
#include "../typedefs.hpp"
#include <vector> #include <vector>
#include "../typedefs.h"
namespace gzip { namespace gzip {
const unsigned char MAGIC[] = "\x1F\x8B"; const unsigned char MAGIC[] = "\x1F\x8B";
@ -18,4 +18,4 @@ namespace gzip {
std::vector<ubyte> decompress(const ubyte* src, size_t size); std::vector<ubyte> decompress(const ubyte* src, size_t size);
} }
#endif // CODERS_GZIP_H_ #endif // CODERS_GZIP_HPP_

49
src/coders/imageio.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "imageio.hpp"
#include "png.hpp"
#include "../graphics/core/ImageData.hpp"
#include <filesystem>
#include <functional>
#include <unordered_map>
namespace fs = std::filesystem;
using image_reader = std::function<std::unique_ptr<ImageData>(const std::string&)>;
using image_writer = std::function<void(const std::string&, const ImageData*)>;
static std::unordered_map<std::string, image_reader> readers {
{".png", png::load_image},
};
static std::unordered_map<std::string, image_writer> writers {
{".png", png::write_image},
};
bool imageio::is_read_supported(const std::string& extension) {
return readers.find(extension) != readers.end();
}
bool imageio::is_write_supported(const std::string& extension) {
return writers.find(extension) != writers.end();
}
inline std::string extensionOf(const std::string& filename) {
return fs::u8path(filename).extension().u8string();
}
std::unique_ptr<ImageData> imageio::read(const std::string& filename) {
auto found = readers.find(extensionOf(filename));
if (found == readers.end()) {
throw std::runtime_error("file format is not supported (read): "+filename);
}
return std::unique_ptr<ImageData>(found->second(filename));
}
void imageio::write(const std::string& filename, const ImageData* image) {
auto found = writers.find(extensionOf(filename));
if (found == writers.end()) {
throw std::runtime_error("file format is not supported (write): "+filename);
}
return found->second(filename, image);
}

19
src/coders/imageio.hpp Normal file
View File

@ -0,0 +1,19 @@
#ifndef CODERS_IMAGEIO_HPP_
#define CODERS_IMAGEIO_HPP_
#include <string>
#include <memory>
class ImageData;
namespace imageio {
inline const std::string PNG = ".png";
bool is_read_supported(const std::string& extension);
bool is_write_supported(const std::string& extension);
std::unique_ptr<ImageData> read(const std::string& filename);
void write(const std::string& filename, const ImageData* image);
}
#endif // CODERS_IMAGEIO_HPP_

View File

@ -1,19 +1,31 @@
#include "json.h" #include "json.hpp"
#include "../data/dynamic.hpp"
#include "../util/stringutil.hpp"
#include <math.h> #include <math.h>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include "commons.h"
#include "../data/dynamic.h"
using namespace json; using namespace json;
using namespace dynamic; using namespace dynamic;
inline void newline(std::stringstream& ss, class Parser : BasicParser {
bool nice, uint indent, std::unique_ptr<dynamic::List> parseList();
const std::string& indentstr) { std::unique_ptr<dynamic::Map> parseObject();
dynamic::Value parseValue();
public:
Parser(std::string_view filename, std::string_view source);
std::unique_ptr<dynamic::Map> parse();
};
inline void newline(
std::stringstream& ss,
bool nice, uint indent,
const std::string& indentstr
) {
if (nice) { if (nice) {
ss << "\n"; ss << "\n";
for (uint i = 0; i < indent; i++) { for (uint i = 0; i < indent; i++) {
@ -24,39 +36,37 @@ inline void newline(std::stringstream& ss,
} }
} }
void stringify(const Value* value, void stringifyObj(
std::stringstream& ss, const Map* obj,
int indent, std::stringstream& ss,
const std::string& indentstr, int indent,
bool nice); const std::string& indentstr,
bool nice
);
void stringifyObj(const Map* obj, void stringifyValue(
std::stringstream& ss, const Value& value,
int indent, std::stringstream& ss,
const std::string& indentstr, int indent,
bool nice); const std::string& indentstr,
bool nice
void stringify(const Value* value, ) {
std::stringstream& ss, if (auto map = std::get_if<Map_sptr>(&value)) {
int indent, stringifyObj(map->get(), ss, indent, indentstr, nice);
const std::string& indentstr,
bool nice) {
if (value->type == valtype::map) {
stringifyObj(value->value.map, ss, indent, indentstr, nice);
} }
else if (value->type == valtype::list) { else if (auto listptr = std::get_if<List_sptr>(&value)) {
auto list = value->value.list; auto list = *listptr;
if (list->size() == 0) { if (list->size() == 0) {
ss << "[]"; ss << "[]";
return; return;
} }
ss << '['; ss << '[';
for (uint i = 0; i < list->size(); i++) { for (uint i = 0; i < list->size(); i++) {
Value* value = list->get(i); Value& value = list->get(i);
if (i > 0 || nice) { if (i > 0 || nice) {
newline(ss, nice, indent, indentstr); newline(ss, nice, indent, indentstr);
} }
stringify(value, ss, indent+1, indentstr, nice); stringifyValue(value, ss, indent+1, indentstr, nice);
if (i + 1 < list->size()) { if (i + 1 < list->size()) {
ss << ','; ss << ',';
} }
@ -65,23 +75,26 @@ void stringify(const Value* value,
newline(ss, true, indent - 1, indentstr); newline(ss, true, indent - 1, indentstr);
} }
ss << ']'; ss << ']';
} else if (value->type == valtype::boolean) { } else if (auto flag = std::get_if<bool>(&value)) {
ss << (value->value.boolean ? "true" : "false"); ss << (*flag ? "true" : "false");
} else if (value->type == valtype::number) { } else if (auto num = std::get_if<number_t>(&value)) {
ss << std::setprecision(15); ss << std::setprecision(15) << *num;
ss << value->value.decimal; } else if (auto num = std::get_if<integer_t>(&value)) {
} else if (value->type == valtype::integer) { ss << *num;
ss << value->value.integer; } else if (auto str = std::get_if<std::string>(&value)) {
} else if (value->type == valtype::string) { ss << util::escape(*str);
ss << escape_string(*value->value.str); } else {
ss << "null";
} }
} }
void stringifyObj(const Map* obj, void stringifyObj(
std::stringstream& ss, const Map* obj,
int indent, std::stringstream& ss,
const std::string& indentstr, int indent,
bool nice) { const std::string& indentstr,
bool nice
) {
if (obj->values.empty()) { if (obj->values.empty()) {
ss << "{}"; ss << "{}";
return; return;
@ -93,9 +106,9 @@ void stringifyObj(const Map* obj,
if (index > 0 || nice) { if (index > 0 || nice) {
newline(ss, nice, indent, indentstr); newline(ss, nice, indent, indentstr);
} }
Value* value = entry.second.get(); const Value& value = entry.second;
ss << escape_string(key) << ": "; ss << util::escape(key) << ": ";
stringify(value, ss, indent+1, indentstr, nice); stringifyValue(value, ss, indent+1, indentstr, nice);
index++; index++;
if (index < obj->values.size()) { if (index < obj->values.size()) {
ss << ','; ss << ',';
@ -108,19 +121,30 @@ void stringifyObj(const Map* obj,
} }
std::string json::stringify( std::string json::stringify(
const Map* obj, const Map* obj,
bool nice, bool nice,
const std::string& indent) { const std::string& indent
) {
std::stringstream ss; std::stringstream ss;
stringifyObj(obj, ss, 1, indent, nice); stringifyObj(obj, ss, 1, indent, nice);
return ss.str(); return ss.str();
} }
Parser::Parser(std::string filename, std::string source) std::string json::stringify(
: BasicParser(filename, source) { const dynamic::Value& value,
bool nice,
const std::string& indent
) {
std::stringstream ss;
stringifyValue(value, ss, 1, indent, nice);
return ss.str();
} }
Map* Parser::parse() { Parser::Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
}
std::unique_ptr<Map> Parser::parse() {
char next = peek(); char next = peek();
if (next != '{') { if (next != '{') {
throw error("'{' expected"); throw error("'{' expected");
@ -128,7 +152,7 @@ Map* Parser::parse() {
return parseObject(); return parseObject();
} }
Map* Parser::parseObject() { std::unique_ptr<Map> Parser::parseObject() {
expect('{'); expect('{');
auto obj = std::make_unique<Map>(); auto obj = std::make_unique<Map>();
auto& map = obj->values; auto& map = obj->values;
@ -154,10 +178,10 @@ Map* Parser::parseObject() {
} }
} }
pos++; pos++;
return obj.release(); return obj;
} }
List* Parser::parseList() { std::unique_ptr<List> Parser::parseList() {
expect('['); expect('[');
auto arr = std::make_unique<List>(); auto arr = std::make_unique<List>();
auto& values = arr->values; auto& values = arr->values;
@ -166,7 +190,7 @@ List* Parser::parseList() {
skipLine(); skipLine();
continue; continue;
} }
values.push_back(std::unique_ptr<Value>(parseValue())); values.push_back(parseValue());
char next = peek(); char next = peek();
if (next == ',') { if (next == ',') {
@ -178,75 +202,49 @@ List* Parser::parseList() {
} }
} }
pos++; pos++;
return arr.release(); return arr;
} }
Value* Parser::parseValue() { Value Parser::parseValue() {
char next = peek(); char next = peek();
dynamic::valvalue val;
if (next == '-' || next == '+') { if (next == '-' || next == '+') {
pos++; pos++;
number_u num; return parseNumber(next == '-' ? -1 : 1);
valtype type;
if (parseNumber(next == '-' ? -1 : 1, num)) {
val.integer = num.ival;
type = valtype::integer;
} else {
val.decimal = num.fval;
type = valtype::number;
}
return new Value(type, val);
} }
if (is_identifier_start(next)) { if (is_identifier_start(next)) {
std::string literal = parseName(); std::string literal = parseName();
if (literal == "true") { if (literal == "true") {
val.boolean = true; return true;
return new Value(valtype::boolean, val);
} else if (literal == "false") { } else if (literal == "false") {
val.boolean = false; return false;
return new Value(valtype::boolean, val);
} else if (literal == "inf") { } else if (literal == "inf") {
val.decimal = INFINITY; return INFINITY;
return new Value(valtype::number, val);
} else if (literal == "nan") { } else if (literal == "nan") {
val.decimal = NAN; return NAN;
return new Value(valtype::number, val);
} }
throw error("invalid literal "); throw error("invalid literal ");
} }
if (next == '{') { if (next == '{') {
val.map = parseObject(); return Map_sptr(parseObject().release());
return new Value(valtype::map, val);
} }
if (next == '[') { if (next == '[') {
val.list = parseList(); return List_sptr(parseList().release());
return new Value(valtype::list, val);
} }
if (is_digit(next)) { if (is_digit(next)) {
number_u num; return parseNumber(1);
valtype type;
if (parseNumber(1, num)) {
val.integer = num.ival;
type = valtype::integer;
} else {
val.decimal = num.fval;
type = valtype::number;
}
return new Value(type, val);
} }
if (next == '"' || next == '\'') { if (next == '"' || next == '\'') {
pos++; pos++;
val.str = new std::string(parseString(next)); return parseString(next);
return new Value(valtype::string, val);
} }
throw error("unexpected character '"+std::string({next})+"'"); throw error("unexpected character '"+std::string({next})+"'");
} }
std::unique_ptr<Map> json::parse(std::string filename, std::string source) { std::unique_ptr<Map> json::parse(const std::string& filename, const std::string& source) {
Parser parser(filename, source); Parser parser(filename, source);
return std::unique_ptr<Map>(parser.parse()); return parser.parse();
} }
std::unique_ptr<Map> json::parse(std::string source) { std::unique_ptr<Map> json::parse(const std::string& source) {
return parse("<string>", source); return parse("<string>", source);
} }

View File

@ -1,41 +0,0 @@
#ifndef CODERS_JSON_H_
#define CODERS_JSON_H_
#include <vector>
#include <string>
#include <stdint.h>
#include <stdexcept>
#include <unordered_map>
#include "commons.h"
#include "../typedefs.h"
#include "binary_json.h"
namespace dynamic {
class Map;
class List;
class Value;
}
namespace json {
class Parser : public BasicParser {
dynamic::List* parseList();
dynamic::Map* parseObject();
dynamic::Value* parseValue();
public:
Parser(std::string filename, std::string source);
dynamic::Map* parse();
};
extern std::unique_ptr<dynamic::Map> parse(std::string filename, std::string source);
extern std::unique_ptr<dynamic::Map> parse(std::string source);
extern std::string stringify(
const dynamic::Map* obj,
bool nice,
const std::string& indent);
}
#endif // CODERS_JSON_H_

33
src/coders/json.hpp Normal file
View File

@ -0,0 +1,33 @@
#ifndef CODERS_JSON_HPP_
#define CODERS_JSON_HPP_
#include "commons.hpp"
#include "binary_json.hpp"
#include "../data/dynamic.hpp"
#include "../typedefs.hpp"
#include <vector>
#include <string>
#include <stdint.h>
#include <stdexcept>
#include <unordered_map>
namespace json {
std::unique_ptr<dynamic::Map> parse(const std::string& filename, const std::string& source);
std::unique_ptr<dynamic::Map> parse(const std::string& source);
std::string stringify(
const dynamic::Map* obj,
bool nice,
const std::string& indent
);
std::string stringify(
const dynamic::Value& value,
bool nice,
const std::string& indent
);
}
#endif // CODERS_JSON_HPP_

View File

@ -1,13 +1,16 @@
#include "ogg.h" #include "ogg.hpp"
#include "../debug/Logger.hpp"
#include "../audio/audio.hpp"
#include "../typedefs.hpp"
#include <string> #include <string>
#include <iostream>
#include <vorbis/codec.h> #include <vorbis/codec.h>
#include <vorbis/vorbisfile.h> #include <vorbis/vorbisfile.h>
#include "../audio/audio.h" static debug::Logger logger("ogg");
#include "../typedefs.h"
namespace fs = std::filesystem;
using namespace audio; using namespace audio;
static inline std::string vorbis_error_message(int code) { static inline std::string vorbis_error_message(int code) {
@ -31,7 +34,7 @@ static inline std::string vorbis_error_message(int code) {
} }
} }
audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) { std::unique_ptr<audio::PCM> ogg::load_pcm(const fs::path& file, bool headerOnly) {
OggVorbis_File vf; OggVorbis_File vf;
int code; int code;
if ((code = ov_fopen(file.u8string().c_str(), &vf))) { if ((code = ov_fopen(file.u8string().c_str(), &vf))) {
@ -56,7 +59,7 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
if (ret == 0) { if (ret == 0) {
eof = true; eof = true;
} else if (ret < 0) { } else if (ret < 0) {
std::cerr << "ogg::load_pcm: " << vorbis_error_message(ret) << std::endl; logger.error() << "ogg::load_pcm: " << vorbis_error_message(ret);
} else { } else {
data.insert(data.end(), std::begin(buffer), std::begin(buffer)+ret); data.insert(data.end(), std::begin(buffer), std::begin(buffer)+ret);
} }
@ -64,7 +67,9 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
totalSamples = data.size() / channels / 2; totalSamples = data.size() / channels / 2;
} }
ov_clear(&vf); ov_clear(&vf);
return new PCM(std::move(data), totalSamples, channels, 16, sampleRate, seekable); return std::make_unique<PCM>(
std::move(data), totalSamples, channels, 16, sampleRate, seekable
);
} }
class OggStream : public PCMStream { class OggStream : public PCMStream {
@ -98,7 +103,7 @@ public:
int bitstream = 0; int bitstream = 0;
long bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream); long bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream);
if (bytes < 0) { if (bytes < 0) {
std::cerr << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes << std::endl; logger.error() << "ogg::load_pcm: " << vorbis_error_message(bytes) << " " << bytes;
return PCMStream::ERROR; return PCMStream::ERROR;
} }
return bytes; return bytes;
@ -147,11 +152,11 @@ public:
} }
}; };
PCMStream* ogg::create_stream(const std::filesystem::path& file) { std::unique_ptr<PCMStream> ogg::create_stream(const fs::path& file) {
OggVorbis_File vf; OggVorbis_File vf;
int code; int code;
if ((code = ov_fopen(file.u8string().c_str(), &vf))) { if ((code = ov_fopen(file.u8string().c_str(), &vf))) {
throw std::runtime_error("vorbis: "+vorbis_error_message(code)); throw std::runtime_error("vorbis: "+vorbis_error_message(code));
} }
return new OggStream(std::move(vf)); return std::make_unique<OggStream>(std::move(vf));
} }

View File

@ -1,16 +0,0 @@
#ifndef CODERS_OGG_H_
#define CODERS_OGG_H_
#include <filesystem>
namespace audio {
struct PCM;
class PCMStream;
}
namespace ogg {
extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly);
extern audio::PCMStream* create_stream(const std::filesystem::path& file);
}
#endif // CODERS_OGG_H_

16
src/coders/ogg.hpp Normal file
View File

@ -0,0 +1,16 @@
#ifndef CODERS_OGG_HPP_
#define CODERS_OGG_HPP_
#include <filesystem>
namespace audio {
struct PCM;
class PCMStream;
}
namespace ogg {
std::unique_ptr<audio::PCM> load_pcm(const std::filesystem::path& file, bool headerOnly);
std::unique_ptr<audio::PCMStream> create_stream(const std::filesystem::path& file);
}
#endif // CODERS_OGG_HPP_

View File

@ -1,12 +1,14 @@
#include "png.h" #include "png.hpp"
#include "../graphics/core/ImageData.hpp"
#include "../graphics/core/Texture.hpp"
#include "../files/files.hpp"
#include "../debug/Logger.hpp"
#include <iostream> #include <iostream>
#include <memory>
#include <GL/glew.h> #include <GL/glew.h>
#include "../graphics/ImageData.h" static debug::Logger logger("png-coder");
#include "../graphics/Texture.h"
#include "../files/files.h"
#ifndef _WIN32 #ifndef _WIN32
#define LIBPNG #define LIBPNG
@ -19,78 +21,76 @@
int _png_write(const char* filename, uint width, uint height, const ubyte* data, bool alpha) { int _png_write(const char* filename, uint width, uint height, const ubyte* data, bool alpha) {
uint pixsize = alpha ? 4 : 3; uint pixsize = alpha ? 4 : 3;
// Open file for writing (binary mode) // Open file for writing (binary mode)
FILE* fp = fopen(filename, "wb"); FILE* fp = fopen(filename, "wb");
if (fp == nullptr) { if (fp == nullptr) {
fprintf(stderr, "Could not open file %s for writing\n", filename); logger.error() << "could not open file " << filename << " for writing";
fclose(fp); fclose(fp);
return 1; return 1;
} }
// Initialize write structure // Initialize write structure
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) { if (png_ptr == nullptr) {
fprintf(stderr, "Could not allocate write struct\n"); logger.error() << "could not allocate write struct";
fclose(fp); fclose(fp);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); return 1;
return 1; }
}
// Initialize info structure // Initialize info structure
png_infop info_ptr = png_create_info_struct(png_ptr); png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) { if (info_ptr == nullptr) {
fprintf(stderr, "Could not allocate info struct\n"); logger.error() << "could not allocate info struct";
fclose(fp); fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
return 1; return 1;
} }
// Setup Exception handling // Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) { if (setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr, "Error during png creation\n"); logger.error() << "error during png creation";
fclose(fp); fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); png_destroy_write_struct(&png_ptr, &info_ptr);
return 1; return 1;
} }
png_init_io(png_ptr, fp); png_init_io(png_ptr, fp);
// Write header (8 bit colour depth) // Write header (8 bit colour depth)
png_set_IHDR(png_ptr, info_ptr, width, height, png_set_IHDR(png_ptr, info_ptr, width, height,
8, 8,
alpha ? PNG_COLOR_TYPE_RGBA : alpha ? PNG_COLOR_TYPE_RGBA :
PNG_COLOR_TYPE_RGB, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE); PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr); png_write_info(png_ptr, info_ptr);
std::unique_ptr<png_byte[]> row(new png_byte[pixsize * width]); auto row = std::make_unique<png_byte[]>(pixsize * width);
// Write image data
// Write image data for (uint y = 0; y < height; y++) {
for (uint y = 0; y < height; y++) { for (uint x = 0; x < width; x++) {
for (uint x = 0; x < width; x++) {
for (uint i = 0; i < pixsize; i++) { for (uint i = 0; i < pixsize; i++) {
row[x * pixsize + i] = (png_byte)data[(y * width + x) * pixsize + i]; row[x * pixsize + i] = (png_byte)data[(y * width + x) * pixsize + i];
} }
} }
png_write_row(png_ptr, row.get()); png_write_row(png_ptr, row.get());
} }
// End write // End write
png_write_end(png_ptr, nullptr); png_write_end(png_ptr, nullptr);
fclose(fp); fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); png_destroy_write_struct(&png_ptr, &info_ptr);
return 0; return 0;
} }
ImageData* _png_load(const char* file){ std::unique_ptr<ImageData> _png_load(const char* file){
FILE* fp = nullptr; FILE* fp = nullptr;
if ((fp = fopen(file, "rb")) == nullptr) { if ((fp = fopen(file, "rb")) == nullptr) {
return nullptr; return nullptr;
@ -175,12 +175,12 @@ ImageData* _png_load(const char* file){
format = ImageFormat::rgb888; format = ImageFormat::rgb888;
break; break;
default: default:
printf("Color type %d not supported!\n", color_type); logger.error() << "color type " << color_type << " is not supported!";
png_destroy_read_struct(&png, &info, &end_info); png_destroy_read_struct(&png, &info, &end_info);
fclose(fp); fclose(fp);
return nullptr; return nullptr;
} }
ImageData* image = new ImageData(format, width, height, (void*)image_data.release()); auto image = std::make_unique<ImageData>(format, width, height, (void*)image_data.release());
png_destroy_read_struct(&png, &info, &end_info); png_destroy_read_struct(&png, &info, &end_info);
fclose(fp); fclose(fp);
return image; return image;
@ -194,172 +194,159 @@ ImageData* _png_load(const char* file){
static const int SPNG_SUCCESS = 0; static const int SPNG_SUCCESS = 0;
//returns spng result code //returns spng result code
int _png_write(const char* filename, uint width, uint height, const ubyte* data, bool alpha) { int _png_write(const char* filename, uint width, uint height, const ubyte* data, bool alpha) {
int fmt; int fmt;
int ret = 0; int ret = 0;
spng_ctx* ctx = nullptr; spng_ctx* ctx = nullptr;
spng_ihdr ihdr = { 0 }; spng_ihdr ihdr = { 0 };
uint pixsize = alpha ? 4 : 3; uint pixsize = alpha ? 4 : 3;
ctx = spng_ctx_new(SPNG_CTX_ENCODER); ctx = spng_ctx_new(SPNG_CTX_ENCODER);
spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1); spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1);
ihdr.width = width; ihdr.width = width;
ihdr.height = height; ihdr.height = height;
ihdr.color_type = alpha ? SPNG_COLOR_TYPE_TRUECOLOR_ALPHA : SPNG_COLOR_TYPE_TRUECOLOR; ihdr.color_type = alpha ? SPNG_COLOR_TYPE_TRUECOLOR_ALPHA : SPNG_COLOR_TYPE_TRUECOLOR;
ihdr.bit_depth = 8; ihdr.bit_depth = 8;
spng_set_ihdr(ctx, &ihdr); spng_set_ihdr(ctx, &ihdr);
fmt = SPNG_FMT_PNG; fmt = SPNG_FMT_PNG;
ret = spng_encode_image(ctx, data, (size_t)width * (size_t)height * pixsize , fmt, SPNG_ENCODE_FINALIZE); ret = spng_encode_image(ctx, data, (size_t)width * (size_t)height * pixsize , fmt, SPNG_ENCODE_FINALIZE);
if (ret != SPNG_SUCCESS) { if (ret != SPNG_SUCCESS) {
printf("spng_encode_image() error: %s\n", spng_strerror(ret)); logger.error() << "spng_encode_image() error: " << spng_strerror(ret);
fflush(stdout); spng_ctx_free(ctx);
spng_ctx_free(ctx); return ret;
return ret; }
}
size_t png_size; size_t png_size;
void* png_buf = spng_get_png_buffer(ctx, &png_size, &ret); void* png_buf = spng_get_png_buffer(ctx, &png_size, &ret);
if (png_buf == nullptr) { if (png_buf == nullptr) {
printf("spng_get_png_buffer() error: %s\n", spng_strerror(ret)); logger.error() << "spng_get_png_buffer() error: " << spng_strerror(ret);
} }
else { else {
files::write_bytes(filename, (const unsigned char*)png_buf, png_size); files::write_bytes(filename, (const unsigned char*)png_buf, png_size);
} }
fflush(stdout); spng_ctx_free(ctx);
spng_ctx_free(ctx); return ret;
return ret;
} }
ImageData* _png_load(const char* file){ std::unique_ptr<ImageData> _png_load(const char* file){
int r = 0; int r = 0;
FILE *png = nullptr; FILE *png = nullptr;
char *pngbuf = nullptr; char *pngbuf = nullptr;
spng_ctx *ctx = nullptr; spng_ctx *ctx = nullptr;
unsigned char *out = nullptr; unsigned char *out = nullptr;
png = fopen(file, "rb"); png = fopen(file, "rb");
if (png == nullptr){ if (png == nullptr){
std::cerr << "could not to open file " << file << std::endl; logger.error() << "could not to open file " << file;
return nullptr; return nullptr;
} }
fseek(png, 0, SEEK_END); fseek(png, 0, SEEK_END);
long siz_pngbuf = ftell(png); long siz_pngbuf = ftell(png);
rewind(png); rewind(png);
if(siz_pngbuf < 1) { if(siz_pngbuf < 1) {
fclose(png); fclose(png);
std::cerr << "could not to read file " << file << std::endl; logger.error() << "could not to read file " << file;
return nullptr; return nullptr;
} }
pngbuf = new char[siz_pngbuf]; pngbuf = new char[siz_pngbuf];
if(fread(pngbuf, siz_pngbuf, 1, png) != 1){ //check of read elements count if(fread(pngbuf, siz_pngbuf, 1, png) != 1){ //check of read elements count
fclose(png); fclose(png);
delete[] pngbuf; delete[] pngbuf;
std::cerr << "fread() failed" << std::endl; logger.error() << "fread() failed: " << file;
return nullptr; return nullptr;
} }
fclose(png); // <- finally closing file fclose(png); // <- finally closing file
ctx = spng_ctx_new(0); ctx = spng_ctx_new(0);
if (ctx == nullptr){ if (ctx == nullptr){
delete[] pngbuf; delete[] pngbuf;
std::cerr << "spng_ctx_new() failed" << std::endl; logger.error() << "spng_ctx_new() failed";
return nullptr; return nullptr;
} }
r = spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE); r = spng_set_crc_action(ctx, SPNG_CRC_USE, SPNG_CRC_USE);
if (r != SPNG_SUCCESS){ if (r != SPNG_SUCCESS){
delete[] pngbuf; delete[] pngbuf;
spng_ctx_free(ctx); spng_ctx_free(ctx);
std::cerr << "spng_set_crc_action() error: " << spng_strerror(r) << std::endl; logger.error() << "spng_set_crc_action(): " << spng_strerror(r);
return nullptr; return nullptr;
} }
r = spng_set_png_buffer(ctx, pngbuf, siz_pngbuf); r = spng_set_png_buffer(ctx, pngbuf, siz_pngbuf);
if (r != SPNG_SUCCESS){ if (r != SPNG_SUCCESS){
delete[] pngbuf; delete[] pngbuf;
spng_ctx_free(ctx); spng_ctx_free(ctx);
std::cerr << "spng_set_png_buffer() error: " << spng_strerror(r) << std::endl; logger.error() << "spng_set_png_buffer(): " << spng_strerror(r);
return nullptr; return nullptr;
} }
spng_ihdr ihdr; spng_ihdr ihdr;
r = spng_get_ihdr(ctx, &ihdr); r = spng_get_ihdr(ctx, &ihdr);
if (r != SPNG_SUCCESS){ if (r != SPNG_SUCCESS){
delete[] pngbuf; delete[] pngbuf;
spng_ctx_free(ctx); spng_ctx_free(ctx);
std::cerr << "spng_get_ihdr() error: " << spng_strerror(r) << std::endl; logger.error() << "spng_get_ihdr(): " << spng_strerror(r);
return nullptr; return nullptr;
} }
//// Unused "something"
//const char *clr_type_str;
//if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE)
// clr_type_str = "grayscale";
//else if(ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR)
// clr_type_str = "truecolor";
//else if(ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
// clr_type_str = "indexed color";
//else if(ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
// clr_type_str = "grayscale with alpha";
//else
// clr_type_str = "truecolor with alpha";
size_t out_size; size_t out_size;
r = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size); r = spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &out_size);
if (r != SPNG_SUCCESS){ if (r != SPNG_SUCCESS){
delete[] pngbuf; delete[] pngbuf;
spng_ctx_free(ctx); spng_ctx_free(ctx);
std::cerr << "spng_decoded_image_size() error: " << spng_strerror(r) << std::endl; logger.error() << "spng_decoded_image_size(): " << spng_strerror(r);
return nullptr; return nullptr;
} }
out = new unsigned char[out_size]; out = new unsigned char[out_size];
r = spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0); r = spng_decode_image(ctx, out, out_size, SPNG_FMT_RGBA8, 0);
if (r != SPNG_SUCCESS){ if (r != SPNG_SUCCESS){
delete[] out; delete[] out;
delete[] pngbuf; delete[] pngbuf;
spng_ctx_free(ctx); spng_ctx_free(ctx);
std::cerr << "spng_decode_image() error: " << spng_strerror(r) << std::endl; logger.error() << "spng_decode_image(): " << spng_strerror(r);
return nullptr; return nullptr;
} }
unsigned char* flipped = new unsigned char[out_size]; unsigned char* flipped = new unsigned char[out_size];
for (size_t i = 0; i < ihdr.height; i+=1){ for (size_t i = 0; i < ihdr.height; i+=1){
size_t rowsize = ihdr.width*4; size_t rowsize = ihdr.width*4;
for (size_t j = 0; j < rowsize; j++){ for (size_t j = 0; j < rowsize; j++){
flipped[(ihdr.height-i-1)*rowsize+j] = out[i*rowsize+j]; flipped[(ihdr.height-i-1)*rowsize+j] = out[i*rowsize+j];
} }
} }
delete[] out; // <- finally delete out // no, delete spng usage delete[] out; // <- finally delete out // no, delete spng usage
ImageData* image = new ImageData(ImageFormat::rgba8888, ihdr.width, ihdr.height, (void*)flipped); auto image = std::make_unique<ImageData>(ImageFormat::rgba8888, ihdr.width, ihdr.height, (void*)flipped);
delete[] pngbuf; delete[] pngbuf;
spng_ctx_free(ctx); spng_ctx_free(ctx);
return image; return image;
} }
#endif #endif
ImageData* png::load_image(std::string filename) { std::unique_ptr<ImageData> png::load_image(const std::string& filename) {
ImageData* image (_png_load(filename.c_str())); auto image = _png_load(filename.c_str());
if (image == nullptr) { if (image == nullptr) {
std::cerr << "Could not load image " << filename << std::endl; throw std::runtime_error("could not load image "+filename);
return nullptr; }
}
return image; return image;
} }
Texture* png::load_texture(std::string filename) { std::unique_ptr<Texture> png::load_texture(const std::string& filename) {
std::unique_ptr<ImageData> image (_png_load(filename.c_str())); auto image = load_image(filename);
if (image == nullptr){ auto texture = Texture::from(image.get());
std::cerr << "Could not load texture " << filename << std::endl;
return nullptr;
}
auto texture = Texture::from(image.get());
texture->setNearestFilter(); texture->setNearestFilter();
return texture; return texture;
} }
void png::write_image(std::string filename, const ImageData* image) { void png::write_image(const std::string& filename, const ImageData* image) {
_png_write(filename.c_str(), image->getWidth(), image->getHeight(), (const ubyte*)image->getData(), image->getFormat() == ImageFormat::rgba8888); _png_write(
filename.c_str(),
image->getWidth(),
image->getHeight(),
(const ubyte*)image->getData(),
image->getFormat() == ImageFormat::rgba8888
);
} }

View File

@ -1,16 +0,0 @@
#ifndef CODERS_PNG_H_
#define CODERS_PNG_H_
#include <string>
#include "../typedefs.h"
class Texture;
class ImageData;
namespace png {
extern ImageData* load_image(std::string filename);
extern void write_image(std::string filename, const ImageData* image);
extern Texture* load_texture(std::string filename);
}
#endif /* CODERS_PNG_H_ */

16
src/coders/png.hpp Normal file
View File

@ -0,0 +1,16 @@
#ifndef CODERS_PNG_HPP_
#define CODERS_PNG_HPP_
#include <memory>
#include <string>
class Texture;
class ImageData;
namespace png {
std::unique_ptr<ImageData> load_image(const std::string& filename);
void write_image(const std::string& filename, const ImageData* image);
std::unique_ptr<Texture> load_texture(const std::string& filename);
}
#endif // CODERS_PNG_HPP_

90
src/coders/rle.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "rle.hpp"
size_t rle::decode(const ubyte* src, size_t srclen, ubyte* dst) {
size_t offset = 0;
for (size_t i = 0; i < srclen;) {
ubyte len = src[i++];
ubyte c = src[i++];
for (size_t j = 0; j <= len; j++) {
dst[offset++] = c;
}
}
return offset;
}
size_t rle::encode(const ubyte* src, size_t srclen, ubyte* dst) {
if (srclen == 0) {
return 0;
}
size_t offset = 0;
ubyte counter = 0;
ubyte c = src[0];
for (size_t i = 1; i < srclen; i++) {
ubyte cnext = src[i];
if (cnext != c || counter == 255) {
dst[offset++] = counter;
dst[offset++] = c;
c = cnext;
counter = 0;
}
else {
counter++;
}
}
dst[offset++] = counter;
dst[offset++] = c;
return offset;
}
size_t extrle::decode(const ubyte* src, size_t srclen, ubyte* dst) {
size_t offset = 0;
for (size_t i = 0; i < srclen;) {
uint len = src[i++];
if (len & 0x80) {
len &= 0x7F;
len |= ((uint)src[i++]) << 7;
}
ubyte c = src[i++];
for (size_t j = 0; j <= len; j++) {
dst[offset++] = c;
}
}
return offset;
}
size_t extrle::encode(const ubyte* src, size_t srclen, ubyte* dst) {
if (srclen == 0) {
return 0;
}
size_t offset = 0;
uint counter = 0;
ubyte c = src[0];
for (size_t i = 1; i < srclen; i++) {
ubyte cnext = src[i];
if (cnext != c || counter == max_sequence) {
if (counter >= 0x80) {
dst[offset++] = 0x80 | (counter & 0x7F);
dst[offset++] = counter >> 7;
}
else {
dst[offset++] = counter;
}
dst[offset++] = c;
c = cnext;
counter = 0;
}
else {
counter++;
}
}
if (counter >= 0x80) {
dst[offset++] = 0x80 | (counter & 0x7F);
dst[offset++] = counter >> 7;
}
else {
dst[offset++] = counter;
}
dst[offset++] = c;
return offset;
}

17
src/coders/rle.hpp Normal file
View File

@ -0,0 +1,17 @@
#ifndef CODERS_RLE_HPP_
#define CODERS_RLE_HPP_
#include "../typedefs.hpp"
namespace rle {
size_t encode(const ubyte* src, size_t length, ubyte* dst);
size_t decode(const ubyte* src, size_t length, ubyte* dst);
}
namespace extrle {
constexpr uint max_sequence = 0x7FFF;
size_t encode(const ubyte* src, size_t length, ubyte* dst);
size_t decode(const ubyte* src, size_t length, ubyte* dst);
}
#endif // CODERS_RLE_HPP_

View File

@ -1,5 +1,10 @@
#include "toml.h" #include "toml.hpp"
#include "commons.h"
#include "commons.hpp"
#include "../data/setting.hpp"
#include "../data/dynamic.hpp"
#include "../util/stringutil.hpp"
#include "../files/settings_io.hpp"
#include <math.h> #include <math.h>
#include <iostream> #include <iostream>
@ -7,100 +12,118 @@
#include <sstream> #include <sstream>
#include <assert.h> #include <assert.h>
using std::string;
using namespace toml; using namespace toml;
Section::Section(string name) : name(name) { class Reader : BasicParser {
} SettingsHandler& handler;
void Section::add(std::string name, Field field) { void skipWhitespace() override {
if (fields.find(name) != fields.end()) { BasicParser::skipWhitespace();
throw std::runtime_error("field duplication"); if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
} }
fields[name] = field; void readSection(const std::string& section) {
keyOrder.push_back(name); while (hasNext()) {
} skipWhitespace();
if (!hasNext()) {
void Section::add(string name, bool* ptr) { break;
add(name, {fieldtype::ftbool, ptr}); }
} char c = nextChar();
if (c == '[') {
void Section::add(string name, int* ptr) { std::string name = parseName();
add(name, {fieldtype::ftint, ptr}); pos++;
} readSection(name);
return;
void Section::add(string name, uint* ptr) { }
add(name, {fieldtype::ftuint, ptr}); pos--;
} std::string name = section+"."+parseName();
expect('=');
void Section::add(string name, float* ptr) { c = peek();
add(name, {fieldtype::ftfloat, ptr}); if (is_digit(c)) {
} auto num = parseNumber(1);
if (handler.has(name)) {
void Section::add(string name, string* ptr) { handler.setValue(name, num);
add(name, {fieldtype::ftstring, ptr}); }
} } else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
string Section::getName() const { pos++;
return name; auto num = parseNumber(sign);
} if (handler.has(name)) {
handler.setValue(name, num);
const Field* Section::field(std::string name) const { }
auto found = fields.find(name); } else if (is_identifier_start(c)) {
if (found == fields.end()) { std::string identifier = parseName();
return nullptr; if (handler.has(name)) {
if (identifier == "true" || identifier == "false") {
bool flag = identifier == "true";
handler.setValue(name, flag);
} else if (identifier == "inf") {
handler.setValue(name, INFINITY);
} else if (identifier == "nan") {
handler.setValue(name, NAN);
}
}
} else if (c == '"' || c == '\'') {
pos++;
std::string str = parseString(c);
if (handler.has(name)) {
handler.setValue(name, str);
}
} else {
throw error("feature is not supported");
}
expectNewLine();
}
} }
return &found->second;
}
const std::vector<std::string>& Section::keys() const { public:
return keyOrder; Reader(
} SettingsHandler& handler,
std::string_view file,
Wrapper::~Wrapper() { std::string_view source)
for (auto entry : sections) { : BasicParser(file, source), handler(handler) {
delete entry.second;
} }
}
Section& Wrapper::add(std::string name) { void read() {
if (sections.find(name) != sections.end()) { skipWhitespace();
throw std::runtime_error("section duplication"); if (!hasNext()) {
return;
}
readSection("");
} }
Section* section = new Section(name); };
sections[name] = section;
keyOrder.push_back(name); void toml::parse(
return *section; SettingsHandler& handler,
const std::string& file,
const std::string& source
) {
Reader reader(handler, file, source);
reader.read();
} }
Section* Wrapper::section(std::string name) { std::string toml::stringify(SettingsHandler& handler) {
auto found = sections.find(name); auto& sections = handler.getSections();
if (found == sections.end()) {
return nullptr;
}
return found->second;
}
std::string Wrapper::write() const {
std::stringstream ss; std::stringstream ss;
for (string key : keyOrder) { for (auto& section : sections) {
const Section* section = sections.at(key); ss << "[" << section.name << "]\n";
ss << "[" << key << "]\n"; for (const std::string& key : section.keys) {
for (const string& key : section->keys()) {
ss << key << " = "; ss << key << " = ";
const Field* field = section->field(key); auto setting = handler.getSetting(section.name+"."+key);
assert(field != nullptr); assert(setting != nullptr);
switch (field->type) { if (auto integer = dynamic_cast<IntegerSetting*>(setting)) {
case fieldtype::ftbool: ss << integer->get();
ss << (*((bool*)field->ptr) ? "true" : "false"); } else if (auto number = dynamic_cast<NumberSetting*>(setting)) {
break; ss << number->get();
case fieldtype::ftint: ss << *((int*)field->ptr); break; } else if (auto flag = dynamic_cast<FlagSetting*>(setting)) {
case fieldtype::ftuint: ss << *((uint*)field->ptr); break; ss << (flag->get() ? "true" : "false");
case fieldtype::ftfloat: ss << *((float*)field->ptr); break; } else if (auto string = dynamic_cast<StringSetting*>(setting)) {
case fieldtype::ftstring: ss << util::escape(string->get());
ss << escape_string(*((const string*)field->ptr));
break;
} }
ss << "\n"; ss << "\n";
} }
@ -108,142 +131,3 @@ std::string Wrapper::write() const {
} }
return ss.str(); return ss.str();
} }
Reader::Reader(Wrapper* wrapper, string file, string source) : BasicParser(file, source), wrapper(wrapper) {
}
void Reader::skipWhitespace() {
BasicParser::skipWhitespace();
if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
}
void Reader::read() {
skipWhitespace();
if (!hasNext()) {
return;
}
readSection(nullptr);
}
inline bool is_numeric_type(fieldtype type) {
return type == fieldtype::ftint || type == fieldtype::ftfloat;
}
void Section::set(string name, double value) {
const Field* field = this->field(name);
if (field == nullptr) {
std::cerr << "warning: unknown key '" << name << "'" << std::endl;
} else {
switch (field->type) {
case fieldtype::ftbool: *(bool*)(field->ptr) = fabs(value) > 0.0; break;
case fieldtype::ftint: *(int*)(field->ptr) = value; break;
case fieldtype::ftuint: *(uint*)(field->ptr) = value; break;
case fieldtype::ftfloat: *(float*)(field->ptr) = value; break;
case fieldtype::ftstring: *(string*)(field->ptr) = std::to_string(value); break;
default:
std::cerr << "error: type error for key '" << name << "'" << std::endl;
}
}
}
void Section::set(std::string name, bool value) {
const Field* field = this->field(name);
if (field == nullptr) {
std::cerr << "warning: unknown key '" << name << "'" << std::endl;
} else {
switch (field->type) {
case fieldtype::ftbool: *(bool*)(field->ptr) = value; break;
case fieldtype::ftint: *(int*)(field->ptr) = (int)value; break;
case fieldtype::ftuint: *(uint*)(field->ptr) = (uint)value; break;
case fieldtype::ftfloat: *(float*)(field->ptr) = (float)value; break;
case fieldtype::ftstring: *(string*)(field->ptr) = value ? "true" : "false"; break;
default:
std::cerr << "error: type error for key '" << name << "'" << std::endl;
}
}
}
void Section::set(std::string name, std::string value) {
const Field* field = this->field(name);
if (field == nullptr) {
std::cerr << "warning: unknown key '" << name << "'" << std::endl;
} else {
switch (field->type) {
case fieldtype::ftstring: *(string*)(field->ptr) = value; break;
default:
std::cerr << "error: type error for key '" << name << "'" << std::endl;
}
}
}
void Reader::readSection(Section* section /*nullable*/) {
while (hasNext()) {
skipWhitespace();
if (!hasNext()) {
break;
}
char c = nextChar();
if (c == '[') {
string name = parseName();
Section* section = wrapper->section(name);
pos++;
readSection(section);
return;
}
pos--;
string name = parseName();
expect('=');
c = peek();
if (is_digit(c)) {
number_u num;
if (parseNumber(1, num)) {
if (section)
section->set(name, (double)num.ival);
} else {
if (section)
section->set(name, num.fval);
}
} else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
pos++;
number_u num;
if (parseNumber(sign, num)) {
if (section)
section->set(name, (double)num.ival);
} else {
if (section)
section->set(name, num.fval);
}
} else if (is_identifier_start(c)) {
string identifier = parseName();
if (identifier == "true" || identifier == "false") {
bool flag = identifier == "true";
if (section) {
section->set(name, flag);
}
} else if (identifier == "inf") {
if (section) {
section->set(name, INFINITY);
}
} else if (identifier == "nan") {
if (section) {
section->set(name, NAN);
}
}
} else if (c == '"' || c == '\'') {
pos++;
string str = parseString(c);
if (section) {
section->set(name, str);
}
} else {
throw error("feature is not supported");
}
expectNewLine();
}
}

View File

@ -1,68 +0,0 @@
#ifndef CODERS_TOML_H_
#define CODERS_TOML_H_
#include <string>
#include <vector>
#include <unordered_map>
#include "commons.h"
namespace toml {
enum class fieldtype {
ftbool,
ftint,
ftuint,
ftfloat,
ftstring,
};
struct Field {
fieldtype type;
void* ptr;
};
class Section {
std::unordered_map<std::string, Field> fields;
std::vector<std::string> keyOrder;
std::string name;
void add(std::string name, Field field);
public:
Section(std::string name);
void add(std::string name, bool* ptr);
void add(std::string name, int* ptr);
void add(std::string name, uint* ptr);
void add(std::string name, float* ptr);
void add(std::string name, std::string* ptr);
const Field* field(std::string name) const;
void set(std::string name, double value);
void set(std::string name, bool value);
void set(std::string name, std::string value);
std::string getName() const;
const std::vector<std::string>& keys() const;
};
class Wrapper {
std::unordered_map<std::string, Section*> sections;
std::vector<std::string> keyOrder;
public:
~Wrapper();
Section& add(std::string section);
Section* section(std::string name);
std::string write() const;
};
class Reader : public BasicParser {
Wrapper* wrapper;
void skipWhitespace() override;
void readSection(Section* section);
public:
Reader(Wrapper* wrapper, std::string file, std::string source);
void read();
};
}
#endif // CODERS_TOML_H_

20
src/coders/toml.hpp Normal file
View File

@ -0,0 +1,20 @@
#ifndef CODERS_TOML_HPP_
#define CODERS_TOML_HPP_
#include "commons.hpp"
#include <string>
class SettingsHandler;
namespace toml {
std::string stringify(SettingsHandler& handler);
void parse(
SettingsHandler& handler,
const std::string& file,
const std::string& source
);
}
#endif // CODERS_TOML_HPP_

Some files were not shown because too many files have changed in this diff Show More