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

3
.gitignore vendored
View File

@ -8,6 +8,7 @@ Debug/voxel_engine
/screenshots
/out
/misc
/world
/worlds/**/*
/settings.toml
@ -41,4 +42,4 @@ appimage-build/
*~
/res/content/*
!/res/content/base
!/res/content/base

View File

@ -101,20 +101,19 @@ if (WIN32)
else()
find_package(PkgConfig)
pkg_check_modules(LUAJIT REQUIRED luajit)
pkg_check_modules(VORBIS REQUIRED vorbis vorbisfile)
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(Lua REQUIRED)
set(PNGLIB PNG::PNG)
set(VORBISLIB vorbis vorbisfile)
set(VORBISLIB ${VORBIS_LDFLAGS})
endif()
if (APPLE)
find_package(glfw3 3.3 REQUIRED)
endif ()
set(LIBS "")
if(UNIX)
find_package(glfw3 3.3 REQUIRED)
find_package(Threads REQUIRED)
set(LIBS ${LIBS} Threads::Threads)
endif()

View File

@ -1,6 +1,6 @@
# 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:
@ -42,7 +42,7 @@ sudo ln -s /usr/include/luajit-2.1 /usr/include/lua
#### RHEL-based distro:
```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:

View File

@ -26,9 +26,7 @@ AppDir:
- libglew2.1
- libpng16-16
- libopenal1
- libopengl0
- libasound2
- libglx0
- libogg0
- libvorbis0a
- 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
```
## *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
@ -71,6 +95,26 @@ world.get_seed() -> int
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
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.
```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
```python
@ -313,6 +364,13 @@ hud.close(layoutid: str)
Remove an element from the screen
```python
hud.get_block_inventory() -> int
```
Get open block inventory ID or 0
## Block events
```lua

View File

@ -60,10 +60,6 @@
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
## Изображение `image`
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
# Текстовое поле `textbox`
Внутренний текст - изначально введенный текст
@ -110,4 +106,4 @@
- `padding` - отступ вокруг решетки слотов. Тип: число. (*атрибут будет удален*)
- `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота
- `updatefunc` - lua событие вызываемое при изменении содержимого слота
- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота
- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота

View File

@ -10,6 +10,29 @@ load_script("контентпак:scripts/имя_скрипта.lua", true) --
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
```python
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
Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex:
@ -132,6 +175,14 @@ inventory.clone(invid: int) -> int
Создает копию инвентаря и возвращает id копии. Если копируемого инвентаря не существует, возвращает 0.
```python
inventory.move(invA: int, slotA: int, invB: int, slotB: int)
```
Перемещает предмет из slotA инвентаря invA в slotB инвентаря invB.
invA и invB могут указывать на один инвентарь.
slotB будет выбран автоматически, если не указывать явно.
## Библиотека block
```python
@ -303,6 +354,13 @@ hud.close(layoutid: str)
```
Удаляет элемент с экрана
```python
hud.get_block_inventory() -> int
```
Получить ID инвентаря открытого блока или 0
## События блоков
```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=Базальт
blue_lamp=Синяя Лампа
brick=Кирпич
dirt=Земля
flower=Цветок
glass=Стекло
grass_block=Дёрн
grass=Высокая Трава
green_lamp=Зелёная Лампа
lamp=Лампа
leaves=Листва
lightbulb=Лампочка
metal=Металл
pane=Панель
pipe=Труба
planks=Доски
red_lamp=Красная Лампа
rust=Ржавчина
sand=Песок
stone=Камень
water=Вода
wood=Бревно
bazalt=базальт
blue lamp=синяя лампа
brick=кирпич
dirt=земля
flower=цветок
glass=стекло
grass block=дёрн
tall grass=высокая трава
green lamp=зелёная лампа
lamp=лампа
leaves=листва
light bulb=лампочка
metal=металл
pane=панель
pipe=труба
planks=доски
red lamp=красная лампа
rust=ржавчина
sand=песок
stone=камень
water=вода
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)
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

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"
]
}

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)
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)
end
@ -175,11 +177,114 @@ Document = {}
function Document.new(docname)
return setmetatable({name=docname}, {
__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
_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
block_index = block.index
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.missing-content=Missing Content!
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.dependency-not-found=Dependency pack is not found
world.delete-confirm=Do you want to delete world forever?
@ -9,6 +9,7 @@ world.generators.default=Default
world.generators.flat=Flat
# Bindings
devtools.console=Console
movement.forward=Forward
movement.back=Back
movement.left=Left
@ -24,4 +25,4 @@ player.build=Place Block
player.flight=Flight
player.noclip=No-clip
camera.zoom=Zoom
camera.mode=Switch Camera Mode
camera.mode=Switch Camera Mode

View File

@ -1,33 +1,39 @@
# Общее
Yes=Joo
No=Ei
Ok = Kyllä
Cancel= Hylää
Back = Takaisin
Ok=Ok
Cancel=Hylää
Back=Takaisin
Continue=Jatka
Add=lisätä
Converting world...=Maailman muuttuminen on käynnissä...
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.Continue=Jatka
menu.Save and Quit to Menu=Tallenna ja poistu valikkoon
menu.missing-content=Puuttuu lisäosia!
menu.Content Error=Lisäosa virhe!
menu.missing-content=Ei ole sisältöä!
menu.Content Error=Sisältövirhe
menu.Controls=Ohjaus
menu.Back to Main Menu=Takaisin Valikoon
menu.Settings=Asetukset
menu.Content=Lisäosat
menu.Seed=Siemen
menu.Name=Nimi
menu.Content=Sisältö
menu.Audio=Audio
world.Seed=Siemen
world.Name=Nimi
world.World generator=Maailman generaattori
world.generators.default=Tavallinen
world.generators.flat=Tasainen
menu.Create World=Luo Maailma
world.World generator=Valon generaattori
world.generators.default=Äärimmäinen
world.generators.flat=Tasainen
world.convert-request=Indeksit vaihdettu! Lataa maailma uudeleen?
world.delete-confirm=Poistetaanko maailma ikuisesti?
world.convert-request=Indeksit ovat muuttuneet! Luodaanko maailma uudeleen?
world.delete-confirm=Poistetaanko maailma peruuttamattomasti?
# Настройки
settings.Load Distance=Latausalue
@ -35,6 +41,12 @@ settings.Load Speed=Latausnopeus
settings.Fog Curve=Sumun tiheys
settings.Backlight=Taustavalo
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.Mouse Sensitivity=Hiiren nopeus

View File

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

View File

@ -5,38 +5,53 @@ Ok=OK
Cancel=Anulowanie
Back=Powrót
Continue=Kontynuacja
Add=Dodać
Converting world...=Konwersja świata w toku...
error.pack-not-found=Nie udało się znaleźć pakietu
# Menu
menu.New World=Nowy Świat
menu.Quit=Wyjście
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.Apply=Zastosować
menu.Audio=Dźwięki
menu.Back to Main Menu=Powrót do menu
menu.Settings=Ustawienia
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.Name=Nazwa
menu.Create World=Stwórz świat
world.World generator=Generator światła
world.generators.default=Ekstremalne
world.generators.flat=Mieszkanie
world.Create World=Stwórz świat
world.convert-request=Szykują się zmiany w indeksach! Przekształcić świat?
world.delete-confirm=Trwale usunąć świat?
# 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 Speed=Prędkość pobierania
settings.Fog Curve=Krzywa mgły
settings.Backlight=Podświetlenie
settings.V-Sync=Synchronizacja pionowa
settings.FOV=Wzrok
settings.Master Volume=Głośność główna
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
movement.forward=Naprzód

View File

@ -10,49 +10,53 @@ Converting world...=Выполняется конвертация мира...
error.pack-not-found=Не удалось найти пакет
error.dependency-not-found=Используемая зависимость не найдена
pack.remove-confirm=Удалить весь поставляемый паком контент из мира (безвозвратно)?
pack.remove-confirm=Удалить весь поставляемый паком/паками контент из мира (безвозвратно)?
# Меню
menu.New World=Новый Мир
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.Apply=Применить
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.Name=Название
world.World generator=Генератор мира
world.generators.default=Обычный
world.generators.flat=Плоский
menu.Create World=Создать Мир
world.Create World=Создать Мир
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 Speed=Скорость Загрузки
settings.Fog Curve=Кривая Тумана
settings.Backlight=Подсветка
settings.V-Sync=Вертикальная Синхронизация
settings.Camera Shaking=Тряска Камеры
settings.Master Volume=Общая Громкость
settings.Mouse Sensitivity=Чувствительность Мыши
settings.Music=Музыка
settings.Regular Sounds=Обычные Звуки
settings.UI Sounds=Звуки Интерфейса
settings.Ambient=Фон
settings.Music=Музыка
settings.FOV=Поле Зрения
settings.Mouse Sensitivity=Чувствительность Мыши
settings.Language=Язык
settings.V-Sync=Вертикальная Синхронизация
# Управление
devtools.console=Консоль
movement.forward=Вперёд
movement.back=Назад
movement.left=Влево
@ -67,4 +71,4 @@ player.attack=Атаковать / Сломать
player.build=Поставить Блок
player.flight=Полёт
camera.zoom=Приближение
camera.mode=Сменить Режим Камеры
camera.mode=Сменить Режим Камеры

View File

@ -9,46 +9,48 @@ Add=Додати
Converting world...=Виконується конвертація світу...
error.pack-not-found=Не вдалося знайти пакет
error.dependency-not-found=Використовувана залежність не знайдена
pack.remove-confirm=Видалити назавжди весь контент, що постачається паком, зі світу?
# Меню
menu.New World=Новий Світ
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.Apply=Застосувати
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.Name=Назва
menu.Create World=Створити Світ
world.World generator=Генератор світу
world.generators.default=Звичайний
world.generators.flat=Плоский
world.generators.default=Звичайні
world.generators.flat=Плоскі
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 Speed=Швидкість Завантаження
settings.Fog Curve=Крива Туману
settings.Backlight=Підсвічування
settings.V-Sync=Вертикальна Синхронізація
settings.Camera Shaking=Коливання Камери
settings.Master Volume=Загальна Гучність
settings.Regular Sounds=Звичайні Звуки
settings.UI Sounds=Звуки Інтерфейсу
settings.Ambient=Середовище
settings.Music=Музика
settings.FOV=Поле зору
settings.Mouse Sensitivity=Чутливість Миші
settings.Language=Мова
settings.Music=Музика
settings.Regular Sounds=Звичайні Звуки
settings.UI Sounds=Звуки інтерфейсу
settings.V-Sync=Вертикальна Синхронізація
# Керування
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 "../graphics/Texture.h"
#include "../graphics/Shader.h"
#include "../graphics/Atlas.h"
#include "../graphics/Font.h"
#include "../frontend/UiDocument.h"
#include "../logic/scripting/scripting.h"
#include "../audio/audio.hpp"
#include "../graphics/core/Texture.hpp"
#include "../graphics/core/Shader.hpp"
#include "../graphics/core/Atlas.hpp"
#include "../graphics/core/Font.hpp"
#include "../frontend/UiDocument.hpp"
#include "../logic/scripting/scripting.hpp"
Assets::~Assets() {
}
Texture* Assets::getTexture(std::string name) const {
auto found = textures.find(name);
if (found == textures.end())
return nullptr;
return found->second.get();
auto found = textures.find(name);
if (found == textures.end())
return nullptr;
return found->second.get();
}
void Assets::store(Texture* texture, std::string name){
textures.emplace(name, texture);
textures.emplace(name, texture);
}
Shader* Assets::getShader(std::string name) const{
auto found = shaders.find(name);
if (found == shaders.end())
return nullptr;
return found->second.get();
auto found = shaders.find(name);
if (found == shaders.end())
return nullptr;
return found->second.get();
}
void Assets::store(Shader* shader, std::string name){
shaders.emplace(name, shader);
shaders.emplace(name, shader);
}
Font* Assets::getFont(std::string name) const {
auto found = fonts.find(name);
if (found == fonts.end())
return nullptr;
return found->second.get();
auto found = fonts.find(name);
if (found == fonts.end())
return nullptr;
return found->second.get();
}
void Assets::store(Font* font, std::string name){
fonts.emplace(name, font);
fonts.emplace(name, font);
}
Atlas* Assets::getAtlas(std::string name) const {
auto found = atlases.find(name);
if (found == atlases.end())
return nullptr;
return found->second.get();
auto found = atlases.find(name);
if (found == atlases.end())
return nullptr;
return found->second.get();
}
void Assets::store(Atlas* atlas, std::string name){
atlases.emplace(name, atlas);
atlases.emplace(name, atlas);
}
audio::Sound* Assets::getSound(std::string name) const {
auto found = sounds.find(name);
if (found == sounds.end())
return nullptr;
return found->second.get();
auto found = sounds.find(name);
if (found == sounds.end())
return nullptr;
return found->second.get();
}
void Assets::store(audio::Sound* sound, std::string name) {
sounds.emplace(name, sound);
sounds.emplace(name, sound);
}
const std::vector<TextureAnimation>& Assets::getAnimations() {
return animations;
return animations;
}
void Assets::store(const TextureAnimation& animation) {
animations.emplace_back(animation);
animations.emplace_back(animation);
}
UiDocument* Assets::getLayout(std::string name) const {
auto found = layouts.find(name);
if (found == layouts.end())
return nullptr;
return found->second.get();
auto found = layouts.find(name);
if (found == layouts.end())
return nullptr;
return found->second.get();
}
void Assets::store(UiDocument* layout, std::string name) {
layouts.emplace(name, 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);
}
layouts[name] = std::shared_ptr<UiDocument>(layout);
}

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 "Assets.h"
#include "AssetsLoader.hpp"
#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 <memory>
#include "../constants.h"
#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"
static debug::Logger logger("assets-loader");
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
: assets(assets), paths(paths)
{
addLoader(AssetType::shader, assetload::shader);
addLoader(AssetType::texture, assetload::texture);
addLoader(AssetType::font, assetload::font);
addLoader(AssetType::atlas, assetload::atlas);
addLoader(AssetType::shader, assetload::shader);
addLoader(AssetType::texture, assetload::texture);
addLoader(AssetType::font, assetload::font);
addLoader(AssetType::atlas, assetload::atlas);
addLoader(AssetType::layout, assetload::layout);
addLoader(AssetType::sound, assetload::sound);
}
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) {
entries.push(aloader_entry{tag, filename, alias, settings});
entries.push(aloader_entry{tag, filename, alias, settings});
}
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() {
const aloader_entry& entry = entries.front();
std::cout << " loading " << entry.filename << " as " << entry.alias << std::endl;
std::cout.flush();
auto found = loaders.find(entry.tag);
if (found == loaders.end()) {
std::cerr << "unknown asset tag " << static_cast<int>(entry.tag) << std::endl;
return false;
}
aloader_func loader = found->second;
bool status = loader(*this, assets, paths, entry.filename, entry.alias, entry.config);
entries.pop();
return status;
const aloader_entry& entry = entries.front();
logger.info() << "loading " << entry.filename << " as " << entry.alias;
try {
aloader_func loader = getLoader(entry.tag);
auto postfunc = loader(this, paths, entry.filename, entry.alias, entry.config);
postfunc(assets);
entries.pop();
return true;
} catch (std::runtime_error& err) {
logger.error() << err.what();
entries.pop();
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)) {
return;
}
@ -100,7 +116,7 @@ void AssetsLoader::processPreload(
switch (tag) {
case AssetType::sound:
add(tag, path, name, std::make_shared<SoundCfg>(
map->getBool("keep-pcm", false)
map->get("keep-pcm", false)
));
break;
default:
@ -115,13 +131,14 @@ void AssetsLoader::processPreloadList(AssetType tag, dynamic::List* list) {
}
for (uint i = 0; i < list->size(); i++) {
auto value = list->get(i);
switch (value->type) {
case dynamic::valtype::string:
processPreload(tag, *value->value.str, nullptr);
switch (static_cast<dynamic::Type>(value.index())) {
case dynamic::Type::string:
processPreload(tag, std::get<std::string>(value), nullptr);
break;
case dynamic::valtype::map: {
auto name = value->value.map->getStr("name");
processPreload(tag, name, value->value.map);
case dynamic::Type::map: {
auto map = std::get<dynamic::Map_sptr>(value);
auto name = map->get<std::string>("name");
processPreload(tag, name, map.get());
break;
}
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/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_world_icon", "gui/no_world_icon");
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/cross", "gui/cross");
loader.add(AssetType::texture, TEXTURES_FOLDER+"/gui/refresh", "gui/refresh");
if (content) {
loader.processPreloadConfigs(content);
@ -179,13 +198,68 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
auto pack = entry.second.get();
auto& info = pack->getInfo();
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+"/items", "items");
}
const ResPaths* AssetsLoader::getPaths() const {
return paths;
bool AssetsLoader::loadExternalTexture(
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 <stdexcept>
#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;
static bool animation(
Assets* assets,
const ResPaths* paths,
const std::string directory,
const std::string name,
Assets* assets,
const ResPaths* paths,
const std::string& atlasName,
const std::string& directory,
const std::string& name,
Atlas* dstAtlas
);
bool assetload::texture(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::texture(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg>
) {
std::unique_ptr<Texture> texture(
png::load_texture(paths->find(filename+".png").u8string())
std::shared_ptr<ImageData> image (
imageio::read(paths->find(filename+".png").u8string()).release()
);
if (texture == nullptr) {
std::cerr << "failed to load texture '" << name << "'" << std::endl;
return false;
}
assets->store(texture.release(), name);
return true;
return [name, image](auto assets) {
assets->store(Texture::from(image.get()).release(), name);
};
}
bool assetload::shader(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::shader(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
@ -61,43 +60,32 @@ bool assetload::shader(
std::string vertexSource = files::read_string(vertexFile);
std::string fragmentSource = files::read_string(fragmentFile);
Shader* shader = Shader::create(
vertexFile.string(),
fragmentFile.string(),
vertexSource, fragmentSource
);
vertexSource = Shader::preprocessor->process(vertexFile, vertexSource);
fragmentSource = Shader::preprocessor->process(fragmentFile, fragmentSource);
if (shader == nullptr) {
std::cerr << "failed to load shader '" << name << "'" << std::endl;
return false;
}
assets->store(shader, name);
return true;
return [=](auto assets) {
assets->store(Shader::create(
vertexFile.u8string(),
fragmentFile.u8string(),
vertexSource, fragmentSource
), name);
};
}
static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) {
// png is only supported format
if (file.extension() != ".png")
return false;
static bool append_atlas(AtlasBuilder& atlas, const fs::path& file) {
std::string name = file.stem().string();
// skip duplicates
if (atlas.has(name)) {
return false;
}
std::unique_ptr<ImageData> image(png::load_image(file.string()));
if (image == nullptr) {
std::cerr << "could not to load " << file.string() << std::endl;
return false;
}
auto image = imageio::read(file.string());
image->fixAlphaColor();
atlas.add(name, image.release());
atlas.add(name, std::move(image));
return true;
}
bool assetload::atlas(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::atlas(
AssetsLoader*,
const ResPaths* paths,
const std::string directory,
const std::string name,
@ -105,201 +93,219 @@ bool assetload::atlas(
) {
AtlasBuilder builder;
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);
assets->store(atlas, name);
for (const auto& file : builder.getNames()) {
animation(assets, paths, "textures", file, atlas);
}
return true;
std::set<std::string> names = builder.getNames();
Atlas* atlas = builder.build(2, false).release();
return [=](auto assets) {
atlas->prepare();
assets->store(atlas, name);
for (const auto& file : names) {
animation(assets, paths, name, directory, file, atlas);
}
};
}
bool assetload::font(
AssetsLoader&,
Assets* assets,
assetload::postfunc assetload::font(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
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++) {
std::string name = filename + "_" + std::to_string(i) + ".png";
name = paths->find(name).string();
std::unique_ptr<Texture> texture (png::load_texture(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));
pages->push_back(imageio::read(name));
}
int res = pages[0]->getHeight() / 16;
assets->store(new Font(std::move(pages), res, 4), name);
return true;
return [=](auto assets) {
int res = pages->at(0)->getHeight() / 16;
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(
AssetsLoader& loader,
Assets* assets,
assetload::postfunc assetload::layout(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
std::shared_ptr<AssetCfg> config
) {
try {
auto cfg = dynamic_cast<LayoutCfg*>(config.get());
auto document = UiDocument::read(loader, cfg->env, name, file);
assets->store(document.release(), name);
return true;
} catch (const parsing_error& err) {
std::cerr << "failed to parse layout XML '" << file << "'" << std::endl;
std::cerr << err.errorLog() << std::endl;
return false;
}
return [=](auto assets) {
try {
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
auto document = UiDocument::read(cfg->env, name, file);
assets->store(document.release(), name);
} catch (const parsing_error& err) {
throw std::runtime_error(
"failed to parse layout XML '"+file+"':\n"+err.errorLog()
);
}
};
}
bool assetload::sound(
AssetsLoader& loader,
Assets* assets,
assetload::postfunc assetload::sound(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
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;
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
auto soundFile = paths->find(file+extension);
if (fs::exists(soundFile)) {
baseSound.reset(audio::load_sound(soundFile, keepPCM));
}
// looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file+"_0"+extension);
if (fs::exists(variantFile)) {
baseSound.reset(audio::load_sound(variantFile, keepPCM));
}
// loading sound variants
for (uint i = 1; ; i++) {
auto variantFile = paths->find(file+"_"+std::to_string(i)+extension);
if (!fs::exists(variantFile)) {
break;
}
baseSound->variants.emplace_back(audio::load_sound(variantFile, keepPCM));
}
assets->store(baseSound.release(), name);
}
catch (std::runtime_error& err) {
std::cerr << err.what() << std::endl;
return false;
// looking for 'sound_name' as base sound
auto soundFile = paths->find(file+extension);
if (fs::exists(soundFile)) {
baseSound = audio::load_sound(soundFile, keepPCM);
}
return true;
// looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file+"_0"+extension);
if (fs::exists(variantFile)) {
baseSound = audio::load_sound(variantFile, keepPCM);
}
// loading sound variants
for (uint i = 1; ; i++) {
auto variantFile = paths->find(file+"_"+std::to_string(i)+extension);
if (!fs::exists(variantFile)) {
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);
}
frameList.emplace_back(frameName, frameDuration);
}
}
}
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 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(
Assets* assets,
const ResPaths* paths,
const std::string directory,
const std::string name,
const ResPaths* paths,
const std::string& atlasName,
const std::string& directory,
const std::string& name,
Atlas* dstAtlas
) {
std::string animsDir = directory + "/animations";
std::string blocksDir = directory + "/blocks";
std::string animsDir = directory + "/animation";
for (const auto& folder : paths->listdir(animsDir)) {
if (!fs::is_directory(folder)) continue;
if (folder.filename().string() != name) continue;
if (folder.filename().u8string() != name) continue;
if (fs::is_empty(folder)) continue;
AtlasBuilder builder;
appendAtlas(builder, paths->find(blocksDir + "/" + name + ".png"));
std::string animFile = folder.string() + "/animation.json";
std::vector<std::pair<std::string, float>> frameList;
append_atlas(builder, paths->find(directory + "/" + name + ".png"));
std::vector<std::pair<std::string, int>> frameList;
std::string animFile = folder.u8string() + "/animation.json";
if (fs::exists(animFile)) {
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 = static_cast<float>(currentFrame->integer(1)) / 1000;
frameList.emplace_back(frameName, frameDuration);
}
}
read_anim_file(animFile, frameList);
}
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
if (!frameList.empty()) {
bool contains = false;
for (const auto& elem : frameList) {
if (file.stem() == elem.first) {
contains = true;
break;
}
}
if (!contains) continue;
if (!frameList.empty() && !contains(frameList, file.stem().u8string())) {
continue;
}
if (!appendAtlas(builder, file)) continue;
if (!append_atlas(builder, file))
continue;
}
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;
auto srcAtlas = builder.build(2, true);
if (frameList.empty()) {
for (const auto& elem : builder.getNames()) {
region = srcAtlas->get(elem);
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
for (const auto& frameName : builder.getNames()) {
frameList.emplace_back(frameName, 0);
}
}
else {
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);
frame.duration = elem.second;
frame.srcPos = glm::ivec2(region.u1 * srcWidth, srcHeight - region.v2 * srcHeight);
animation.addFrame(frame);
}
}
assets->store(srcAtlas.release(), name + "_animation");
auto animation = create_animation(
srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList
);
assets->store(srcAtlas.release(), atlasName + "/" + name + "_animation");
assets->store(animation);
return true;
}
return true;

View File

@ -1,5 +1,7 @@
#ifndef ASSETS_ASSET_LOADERS_H_
#define ASSETS_ASSET_LOADERS_H_
#ifndef ASSETS_ASSET_LOADERS_HPP_
#define ASSETS_ASSET_LOADERS_HPP_
#include "Assets.hpp"
#include <string>
#include <memory>
@ -12,50 +14,44 @@ struct AssetCfg;
/// @brief see AssetsLoader.h: aloader_func
namespace assetload {
bool texture(
AssetsLoader&,
Assets*,
postfunc texture(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool shader(
AssetsLoader&,
Assets*,
postfunc shader(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool atlas(
AssetsLoader&,
Assets*,
postfunc atlas(
AssetsLoader*,
const ResPaths* paths,
const std::string directory,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool font(
AssetsLoader&,
Assets*,
postfunc font(
AssetsLoader*,
const ResPaths* paths,
const std::string filename,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool layout(
AssetsLoader&,
Assets*,
postfunc layout(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
const std::string name,
std::shared_ptr<AssetCfg> settings
);
bool sound(
AssetsLoader&,
Assets*,
postfunc sound(
AssetsLoader*,
const ResPaths* paths,
const std::string file,
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 "alutil.h"
#include "ALAudio.hpp"
#include "alutil.hpp"
#include "../../debug/Logger.hpp"
#include <string>
#include <iostream>
static debug::Logger logger("al-audio");
using namespace audio;
@ -19,14 +23,14 @@ ALSound::~ALSound() {
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();
if (source == 0) {
return nullptr;
}
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;
return speaker;
}
@ -62,7 +66,7 @@ bool ALStream::preloadBuffer(uint buffer, bool loop) {
return true;
}
Speaker* ALStream::createSpeaker(bool loop, int channel) {
std::unique_ptr<Speaker> ALStream::createSpeaker(bool loop, int channel) {
this->loop = loop;
uint source = al->getFreeSource();
if (source == 0) {
@ -75,7 +79,7 @@ Speaker* ALStream::createSpeaker(bool loop, int channel) {
}
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)
return;
float gain = this->volume * channel->getVolume()*masterVolume;
float gain = this->volume * channel->getVolume();
AL_CHECK(alSourcef(source, AL_GAIN, gain));
if (!paused) {
@ -259,7 +263,7 @@ void ALSpeaker::play() {
paused = false;
stopped = false;
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));
}
@ -341,14 +345,14 @@ ALAudio::ALAudio(ALCdevice* device, ALCcontext* context)
alcGetIntegerv(device, ALC_ALL_ATTRIBUTES, size, &attrs[0]);
for (size_t i = 0; i < attrs.size(); ++i){
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];
}
}
auto devices = getAvailableDevices();
std::cout << "AL devices:" << std::endl;
logger.info() << "devices:";
for (auto& name : devices) {
std::cout << " " << name << std::endl;
logger.info() << " " << name;
}
}
@ -368,21 +372,21 @@ ALAudio::~ALAudio() {
AL_CHECK(alcMakeContextCurrent(context));
alcDestroyContext(context);
if (!alcCloseDevice(device)) {
std::cerr << "AL: device not closed!" << std::endl;
logger.error() << "device not closed!";
}
device = 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);
uint buffer = getFreeBuffer();
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) {
return new ALStream(this, stream, keepSource);
std::unique_ptr<Stream> ALAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return std::make_unique<ALStream>(this, stream, keepSource);
}
ALAudio* ALAudio::create() {
@ -395,7 +399,7 @@ ALAudio* ALAudio::create() {
return nullptr;
}
AL_CHECK();
std::cout << "AL: initialized" << std::endl;
logger.info() << "initialized";
return new ALAudio(device, context);
}
@ -406,7 +410,7 @@ uint ALAudio::getFreeSource(){
return source;
}
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;
}
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_VELOCITY, velocity.x, velocity.y, velocity.z));
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_
#define SRC_AUDIO_AUDIO_H_
#ifndef SRC_AUDIO_AUDIO_HPP_
#define SRC_AUDIO_AUDIO_HPP_
#include "../audio.hpp"
#include "../../typedefs.hpp"
#include <queue>
#include <vector>
@ -15,9 +18,6 @@
#include <AL/alc.h>
#endif
#include "../audio.h"
#include "../../typedefs.h"
namespace audio {
struct ALBuffer;
class ALAudio;
@ -40,7 +40,7 @@ namespace audio {
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 {
@ -65,7 +65,7 @@ namespace audio {
std::shared_ptr<PCMStream> getSource() const 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;
void update(double delta) override;
@ -91,7 +91,7 @@ namespace audio {
ALSpeaker(ALAudio* al, uint source, int priority, int channel);
~ALSpeaker();
void update(const Channel* channel, float masterVolume) override;
void update(const Channel* channel) override;
int getChannel() const override;
State getState() const override;
@ -135,7 +135,7 @@ namespace audio {
std::vector<uint> allbuffers;
std::vector<uint> freebuffers;
uint maxSources;
uint maxSources = 256;
ALAudio(ALCdevice* device, ALCcontext* context);
public:
@ -148,8 +148,8 @@ namespace audio {
std::vector<std::string> getAvailableDevices() const;
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener(
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 <cstring>
#include <memory>
@ -14,30 +15,31 @@
#include <AL/alc.h>
#endif
static debug::Logger logger("open-al");
bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line){
ALenum error = alGetError();
if(error != AL_NO_ERROR){
std::cerr << "OpenAL ERROR (" << filename << ": " << line << ")\n" ;
logger.error() << filename << ": " << line;
switch(error){
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;
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;
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;
case AL_INVALID_OPERATION:
std::cerr << "AL_INVALID_OPERATION: the requested operation is not valid";
logger.error() << "the requested operation is not valid";
break;
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;
default:
std::cerr << "UNKNOWN AL ERROR: " << error;
logger.error() << "UNKNOWN AL ERROR: " << error;
}
std::cerr << std::endl;
return false;
}
return true;

View File

@ -1,5 +1,7 @@
#ifndef SRC_AUDIO_AUDIOUTIL_H_
#define SRC_AUDIO_AUDIOUTIL_H_
#ifndef AUDIO_AUDIOUTIL_HPP_
#define AUDIO_AUDIOUTIL_HPP_
#include "../../typedefs.hpp"
#include <string>
#include <type_traits>
@ -12,7 +14,6 @@
#endif
#include <glm/glm.hpp>
#include "../../typedefs.h"
#define AL_CHECK(STATEMENT) STATEMENT; 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;
@ -9,12 +9,12 @@ NoSound::NoSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
}
}
Sound* NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return new NoSound(pcm, keepPCM);
std::unique_ptr<Sound> NoAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
return std::make_unique<NoSound>(pcm, keepPCM);
}
Stream* NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return new NoStream(stream, keepSource);
std::unique_ptr<Stream> NoAudio::openStream(std::shared_ptr<PCMStream> stream, bool keepSource) {
return std::make_unique<NoStream>(stream, keepSource);
}
NoAudio* NoAudio::create() {

View File

@ -1,7 +1,7 @@
#ifndef AUDIO_NOAUDIO_H_
#define AUDIO_NOAUDIO_H_
#ifndef AUDIO_NOAUDIO_HPP_
#define AUDIO_NOAUDIO_HPP_
#include "audio.h"
#include "audio.hpp"
namespace audio {
class NoSound : public Sound {
@ -19,7 +19,7 @@ namespace audio {
return pcm;
}
Speaker* newInstance(int priority, int channel) const override {
std::unique_ptr<Speaker> newInstance(int priority, int channel) const override {
return nullptr;
}
};
@ -42,7 +42,7 @@ namespace audio {
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;
}
@ -65,8 +65,8 @@ namespace audio {
public:
~NoAudio() {}
Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
Stream* openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
std::unique_ptr<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) override;
std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) override;
void setListener(
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 <stdexcept>
#include "NoAudio.h"
#include "AL/ALAudio.h"
#include "../coders/wav.h"
#include "../coders/ogg.h"
namespace audio {
static speakerid_t nextId = 1;
static Backend* backend;
@ -72,9 +72,6 @@ size_t PCMStream::readFully(char* buffer, size_t bufferSize, bool loop) {
if (loop) {
seek(0);
}
if (bufferSize == 0) {
return size;
}
} while (loop);
return size;
}
@ -94,7 +91,7 @@ public:
seekable(seekable)
{}
size_t read(char* buffer, size_t bufferSize) override {
size_t read(char*, size_t bufferSize) override {
if (closed) {
return 0;
}
@ -159,7 +156,7 @@ void audio::initialize(bool enabled) {
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)) {
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");
}
Sound* audio::load_sound(const fs::path& file, bool keepPCM) {
std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy()));
std::unique_ptr<Sound> audio::load_sound(const fs::path& file, bool keepPCM) {
std::shared_ptr<PCM> pcm(load_PCM(file, !keepPCM && backend->isDummy()).release());
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);
}
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();
if (ext == ".wav" || ext == ".WAV") {
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");
}
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()) {
auto header = load_PCM(file, true);
// 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);
}
@ -258,16 +255,17 @@ speakerid_t audio::play(
sound = sound->variants.at(index).get();
}
}
Speaker* speaker = sound->newInstance(priority, channel);
if (speaker == nullptr) {
auto speaker_ptr = sound->newInstance(priority, channel);
if (speaker_ptr == nullptr) {
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;
}
auto speaker = speaker_ptr.get();
speakerid_t id = nextId++;
speakers.emplace(id, speaker);
speakers.emplace(id, std::move(speaker_ptr));
speaker->setPosition(position);
speaker->setVolume(volume);
speaker->setPitch(pitch);
@ -286,17 +284,18 @@ speakerid_t audio::play(
bool loop,
int channel
) {
Speaker* speaker = stream->createSpeaker(loop, channel);
if (speaker == nullptr) {
auto speaker_ptr = stream->createSpeaker(loop, channel);
if (speaker_ptr == nullptr) {
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;
}
auto speaker = speaker_ptr.get();
speakerid_t id = nextId++;
streams.emplace(id, stream);
speakers.emplace(id, speaker);
speakers.emplace(id, std::move(speaker_ptr));
stream->bindSpeaker(id);
speaker->setPosition(position);
@ -383,13 +382,12 @@ void audio::update(double delta) {
entry.second->update(delta);
}
float masterVolume = channels.at(0)->getVolume();
for (auto it = speakers.begin(); it != speakers.end();) {
auto speaker = it->second.get();
int speakerChannel = speaker->getChannel();
auto channel = get_channel(speakerChannel);
if (channel != nullptr) {
speaker->update(channel, speakerChannel == 0 ? 1.0f : masterVolume);
speaker->update(channel);
}
if (speaker->isStopped()) {
streams.erase(it->first);

View File

@ -1,11 +1,12 @@
#ifndef AUDIO_AUDIO_H_
#define AUDIO_AUDIO_H_
#ifndef AUDIO_AUDIO_HPP_
#define AUDIO_AUDIO_HPP_
#include "../typedefs.hpp"
#include <vector>
#include <memory>
#include <filesystem>
#include <glm/glm.hpp>
#include "../typedefs.h"
namespace fs = std::filesystem;
@ -178,7 +179,7 @@ namespace audio {
/// @param loop is stream looped (required for correct buffers preload)
/// @param channel channel index
/// @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
/// @param speaker speaker id or 0 if all you need is unbind speaker
@ -224,7 +225,7 @@ namespace audio {
/// @param channel channel index
/// @return new speaker with sound bound or nullptr
/// 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.
@ -235,8 +236,7 @@ namespace audio {
/// @brief Synchronize the speaker with channel settings
/// @param channel speaker channel
/// @param masterVolume volume of the master channel
virtual void update(const Channel* channel, float masterVolume) = 0;
virtual void update(const Channel* channel) = 0;
/// @brief Check speaker channel index
virtual int getChannel() const = 0;
@ -336,8 +336,8 @@ namespace audio {
public:
virtual ~Backend() {};
virtual 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<Sound> createSound(std::shared_ptr<PCM> pcm, bool keepPCM) = 0;
virtual std::unique_ptr<Stream> openStream(std::shared_ptr<PCMStream> stream, bool keepSource) = 0;
virtual void setListener(
glm::vec3 position,
glm::vec3 velocity,
@ -353,52 +353,52 @@ namespace audio {
/// @brief Initialize audio system or use no audio mode
/// @param enabled try to initialize actual audio
extern void initialize(bool enabled);
void initialize(bool enabled);
/// @brief Load audio file info and PCM data
/// @param file audio file
/// @param headerOnly read header only
/// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @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
/// @param file audio file path
/// @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
/// @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
/// @param pcm PCM data
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
/// @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
/// @param file audio file path
/// @throws std::runtime_error if I/O error ocurred or format is unknown
/// @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
/// @param file audio file path
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @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
/// @param stream PCM data source
/// @param keepSource store PCMStream in stream to make it accessible with Stream::getSource
/// @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
/// @param position listener position
/// @param velocity listener velocity (used for Doppler effect)
/// @param lookAt point the listener look at
/// @param up camera up vector
extern void set_listener(
void set_listener(
glm::vec3 position,
glm::vec3 velocity,
glm::vec3 lookAt,
@ -416,7 +416,7 @@ namespace audio {
/// (PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH)
/// @param channel channel index
/// @return speaker id or 0
extern speakerid_t play(
speakerid_t play(
Sound* sound,
glm::vec3 position,
bool relative,
@ -436,7 +436,7 @@ namespace audio {
/// @param loop loop stream
/// @param channel channel index
/// @return speaker id or 0
extern speakerid_t play(
speakerid_t play(
std::shared_ptr<Stream> stream,
glm::vec3 position,
bool relative,
@ -455,7 +455,7 @@ namespace audio {
/// @param loop loop stream
/// @param channel channel index
/// @return speaker id or 0
extern speakerid_t play_stream(
speakerid_t play_stream(
const fs::path& file,
glm::vec3 position,
bool relative,
@ -468,49 +468,49 @@ namespace audio {
/// @brief Get speaker by id
/// @param id speaker id
/// @return speaker or nullptr
extern Speaker* get_speaker(speakerid_t id);
Speaker* get_speaker(speakerid_t id);
/// @brief Create new channel.
/// All non-builtin channels will be destroyed on audio::reset() call
/// @param name channel name
/// @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
/// @param name channel name
/// @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
/// @param index channel index
/// @return channel or nullptr
extern Channel* get_channel(int index);
Channel* get_channel(int index);
/// @brief Get channel by name.
/// @param name channel name
/// @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
/// @param id speaker id
/// @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)
extern size_t count_speakers();
size_t count_speakers();
/// @brief Get playing streams number (including paused)
extern size_t count_streams();
size_t count_streams();
/// @brief Update audio streams and sound instanced
/// @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
extern void reset_channel(int channel);
void reset_channel(int channel);
/// @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 <sstream>
#include <stdexcept>
#include "../util/stringutil.h"
#include "../typedefs.h"
#include "../files/files.h"
#include "../files/engine_paths.h"
namespace fs = std::filesystem;
@ -81,7 +83,7 @@ inline void source_line(std::stringstream& ss, uint linenum) {
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;
size_t pos = 0;
uint linenum = 1;

View File

@ -1,5 +1,5 @@
#ifndef CODERS_GLSL_EXTESION_H_
#define CODERS_GLSL_EXTESION_H_
#ifndef CODERS_GLSL_EXTESION_HPP_
#define CODERS_GLSL_EXTESION_HPP_
#include <string>
#include <vector>
@ -29,7 +29,10 @@ public:
bool hasHeader(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 "gzip.h"
#include "byte_utils.h"
using namespace json;
using namespace dynamic;
static void to_binary(ByteBuilder& builder, const Value* value) {
switch (value->type) {
case valtype::map: {
std::vector<ubyte> bytes = to_binary(value->value.map);
static void to_binary(ByteBuilder& builder, const Value& value) {
switch (static_cast<Type>(value.index())) {
case Type::none:
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());
break;
}
case valtype::list:
case Type::list:
builder.put(BJSON_TYPE_LIST);
for (auto& element : value->value.list->values) {
to_binary(builder, element.get());
for (auto& element : std::get<List_sptr>(value)->values) {
to_binary(builder, element);
}
builder.put(BJSON_END);
break;
case valtype::integer: {
int64_t val = value->value.integer;
case Type::integer: {
auto val = std::get<integer_t>(value);
if (val >= 0 && val <= 255) {
builder.put(BJSON_TYPE_BYTE);
builder.put(val);
@ -39,22 +42,22 @@ static void to_binary(ByteBuilder& builder, const Value* value) {
}
break;
}
case valtype::number:
case Type::number:
builder.put(BJSON_TYPE_NUMBER);
builder.putFloat64(value->value.decimal);
builder.putFloat64(std::get<number_t>(value));
break;
case valtype::boolean:
builder.put(BJSON_TYPE_FALSE + value->value.boolean);
case Type::boolean:
builder.put(BJSON_TYPE_FALSE + std::get<bool>(value));
break;
case valtype::string:
case Type::string:
builder.put(BJSON_TYPE_STRING);
builder.put(*value->value.str);
builder.put(std::get<std::string>(value));
break;
}
}
static List* array_from_binary(ByteReader& reader);
static Map* object_from_binary(ByteReader& reader);
static std::unique_ptr<List> array_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) {
if (compress) {
@ -70,7 +73,7 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
// writing entries
for (auto& entry : obj->values) {
builder.putCStr(entry.first.c_str());
to_binary(builder, entry.second.get());
to_binary(builder, entry.second);
}
// terminating byte
builder.put(BJSON_END);
@ -80,79 +83,56 @@ std::vector<ubyte> json::to_binary(const Map* obj, bool compress) {
return builder.build();
}
static Value* value_from_binary(ByteReader& reader) {
static Value value_from_binary(ByteReader& reader) {
ubyte typecode = reader.get();
valtype type;
valvalue val;
switch (typecode) {
case BJSON_TYPE_DOCUMENT:
type = valtype::map;
reader.getInt32();
val.map = object_from_binary(reader);
break;
return Map_sptr(object_from_binary(reader).release());
case BJSON_TYPE_LIST:
type = valtype::list;
val.list = array_from_binary(reader);
break;
return List_sptr(array_from_binary(reader).release());
case BJSON_TYPE_BYTE:
type = valtype::integer;
val.integer = reader.get();
break;
return static_cast<integer_t>(reader.get());
case BJSON_TYPE_INT16:
type = valtype::integer;
val.integer = reader.getInt16();
break;
return static_cast<integer_t>(reader.getInt16());
case BJSON_TYPE_INT32:
type = valtype::integer;
val.integer = reader.getInt32();
break;
return static_cast<integer_t>(reader.getInt32());
case BJSON_TYPE_INT64:
type = valtype::integer;
val.integer = reader.getInt64();
break;
return reader.getInt64();
case BJSON_TYPE_NUMBER:
type = valtype::number;
val.decimal = reader.getFloat64();
break;
return reader.getFloat64();
case BJSON_TYPE_FALSE:
case BJSON_TYPE_TRUE:
type = valtype::boolean;
val.boolean = typecode - BJSON_TYPE_FALSE;
break;
return (typecode - BJSON_TYPE_FALSE) != 0;
case BJSON_TYPE_STRING:
type = valtype::string;
val.str = new std::string(reader.getString());
break;
return reader.getString();
default:
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& items = array->values;
while (reader.peek() != BJSON_END) {
items.push_back(std::unique_ptr<Value>(value_from_binary(reader)));
array->put(value_from_binary(reader));
}
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& map = obj->values;
while (reader.peek() != BJSON_END) {
const char* key = reader.getCString();
Value* value = value_from_binary(reader);
map.insert(std::make_pair(key, value));
obj->put(key, value_from_binary(reader));
}
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) {
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());
} else {
ByteReader reader(src, size);
std::unique_ptr<Value> value (value_from_binary(reader));
if (value->type != valtype::map) {
Value value = value_from_binary(reader);
if (auto map = std::get_if<Map_sptr>(&value)) {
return *map;
} else {
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_
#define CODERS_BINARY_JSON_H_
#ifndef CODERS_BINARY_JSON_HPP_
#define CODERS_BINARY_JSON_HPP_
#include "../typedefs.hpp"
#include <vector>
#include <memory>
#include "../data/dynamic.h"
namespace dynamic {
class Map;
}
namespace json {
const int BJSON_END = 0x0;
@ -22,7 +27,7 @@ namespace json {
const int BJSON_TYPE_CDOCUMENT = 0x1F;
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 <limits>

View File

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

View File

@ -1,344 +1,337 @@
#include "commons.h"
#include <sstream>
#include <stdexcept>
#include <math.h>
inline double power(double base, int64_t power) {
double result = 1.0;
for (int64_t i = 0; i < power; i++) {
result *= base;
}
return result;
}
parsing_error::parsing_error(
std::string message,
std::string filename,
std::string source,
uint pos,
uint line,
uint linestart)
: std::runtime_error(message), filename(filename), source(source),
pos(pos), line(line), linestart(linestart) {
}
std::string parsing_error::errorLog() const {
std::stringstream ss;
uint linepos = pos - linestart;
ss << "parsing error in file '" << filename;
ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n";
size_t end = source.find("\n", linestart);
if (end == std::string::npos) {
end = source.length();
}
ss << source.substr(linestart, end-linestart) << "\n";
for (uint i = 0; i < linepos; i++) {
ss << " ";
}
ss << "^";
return ss.str();
}
std::string escape_string(std::string s) {
std::stringstream ss;
ss << '"';
for (char c : s) {
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() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
continue;
}
if (is_whitespace(next)) {
pos++;
} else {
break;
}
}
}
void BasicParser::skip(size_t n) {
n = std::min(n, source.length()-pos);
for (size_t i = 0; i < n; i++) {
char next = source[pos++];
if (next == '\n') {
line++;
linestart = pos;
}
}
}
void BasicParser::skipLine() {
while (hasNext()) {
if (source[pos] == '\n') {
pos++;
linestart = pos;
line++;
break;
}
pos++;
}
}
bool BasicParser::skipTo(const std::string& substring) {
size_t idx = source.find(substring, pos);
if (idx == std::string::npos) {
skip(source.length()-pos);
return false;
} else {
skip(idx-pos);
return true;
}
}
bool BasicParser::hasNext() {
return pos < source.length();
}
bool BasicParser::isNext(const std::string& substring) {
if (source.length() - pos < substring.length()) {
return false;
}
return source.substr(pos, substring.length()) == substring;
}
char BasicParser::nextChar() {
if (!hasNext()) {
throw error("unexpected end");
}
return source[pos++];
}
void BasicParser::expect(char expected) {
char c = peek();
if (c != expected) {
throw error("'"+std::string({expected})+"' expected");
}
pos++;
}
void BasicParser::expect(const std::string& substring) {
if (substring.empty())
return;
for (uint i = 0; i < substring.length(); i++) {
if (source.length() <= pos + i || source[pos+i] != substring[i]) {
throw error(escape_string(substring)+" expected");
}
}
pos += substring.length();
}
void BasicParser::expectNewLine() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
return;
}
if (is_whitespace(next)) {
pos++;
} else {
throw error("line separator expected");
}
}
}
void BasicParser::goBack() {
if (pos) pos--;
}
char BasicParser::peek() {
skipWhitespace();
if (pos >= source.length()) {
throw error("unexpected end");
}
return source[pos];
}
std::string BasicParser::parseName() {
char c = peek();
if (!is_identifier_start(c)) {
if (c == '"') {
pos++;
return parseString(c);
}
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_identifier_part(source[pos])) {
pos++;
}
return source.substr(start, pos-start);
}
int64_t BasicParser::parseSimpleInt(int base) {
char c = peek();
int index = hexchar2int(c);
if (index == -1 || index >= base) {
throw error("invalid number literal");
}
int64_t value = index;
pos++;
while (hasNext()) {
c = source[pos];
while (c == '_') {
c = source[++pos];
}
index = hexchar2int(c);
if (index == -1 || index >= base) {
return value;
}
value *= base;
value += index;
pos++;
}
return value;
}
bool BasicParser::parseNumber(int sign, number_u& out) {
char c = peek();
int base = 10;
if (c == '0' && pos + 1 < source.length() &&
(base = is_box(source[pos+1])) != 10) {
pos += 2;
out.ival = parseSimpleInt(base);
return true;
} else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') {
pos += 3;
out.fval = INFINITY * sign;
return false;
} else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') {
pos += 3;
out.fval = NAN * sign;
return false;
}
int64_t value = parseSimpleInt(base);
if (!hasNext()) {
out.ival = value * sign;
return true;
}
c = source[pos];
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
out.fval = sign * value * power(10.0, s * parseSimpleInt(10));
return false;
}
if (c == '.') {
pos++;
int64_t expo = 1;
while (hasNext() && source[pos] == '0') {
expo *= 10;
pos++;
}
int64_t afterdot = 0;
if (hasNext() && is_digit(source[pos])) {
afterdot = parseSimpleInt(10);
}
expo *= power(10, fmax(0, log10(afterdot) + 1));
c = source[pos];
double dvalue = (value + (afterdot / (double)expo));
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
out.fval = sign * dvalue * power(10.0, s * parseSimpleInt(10));
return false;
}
out.fval = sign * dvalue;
return false;
}
out.ival = sign * value;
return true;
}
std::string BasicParser::parseString(char quote, bool closeRequired) {
std::stringstream ss;
while (hasNext()) {
char c = source[pos];
if (c == quote) {
pos++;
return ss.str();
}
if (c == '\\') {
pos++;
c = nextChar();
if (c >= '0' && c <= '7') {
pos--;
ss << (char)parseSimpleInt(8);
continue;
}
switch (c) {
case 'n': ss << '\n'; break;
case 'r': ss << '\r'; break;
case 'b': ss << '\b'; break;
case 't': ss << '\t'; break;
case 'f': ss << '\f'; break;
case '\'': ss << '\\'; break;
case '"': ss << '"'; break;
case '\\': ss << '\\'; break;
case '/': ss << '/'; break;
case '\n': pos++; continue;
default:
throw error("'\\" + std::string({c}) +
"' is an illegal escape");
}
continue;
}
if (c == '\n' && closeRequired) {
throw error("non-closed string literal");
}
ss << c;
pos++;
}
if (closeRequired) {
throw error("unexpected end");
}
return ss.str();
}
parsing_error BasicParser::error(std::string message) {
return parsing_error(message, filename, source, pos, line, linestart);
}
#include "commons.hpp"
#include "../util/stringutil.hpp"
#include <sstream>
#include <stdexcept>
#include <math.h>
inline double power(double base, int64_t power) {
double result = 1.0;
for (int64_t i = 0; i < power; i++) {
result *= base;
}
return result;
}
parsing_error::parsing_error(
std::string message,
std::string_view filename,
std::string_view source,
uint pos,
uint line,
uint linestart
) : std::runtime_error(message), filename(filename),
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::stringstream ss;
uint linepos = pos - linestart;
ss << "parsing error in file '" << filename;
ss << "' at " << (line+1) << ":" << linepos << ": " << this->what() << "\n";
ss << source << "\n";
for (uint i = 0; i < linepos; i++) {
ss << " ";
}
ss << "^";
return ss.str();
}
BasicParser::BasicParser(
std::string_view file,
std::string_view source
) : filename(file), source(source) {
}
void BasicParser::skipWhitespace() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
continue;
}
if (is_whitespace(next)) {
pos++;
} else {
break;
}
}
}
void BasicParser::skip(size_t n) {
n = std::min(n, source.length()-pos);
for (size_t i = 0; i < n; i++) {
char next = source[pos++];
if (next == '\n') {
line++;
linestart = pos;
}
}
}
void BasicParser::skipLine() {
while (hasNext()) {
if (source[pos] == '\n') {
pos++;
linestart = pos;
line++;
break;
}
pos++;
}
}
bool BasicParser::skipTo(const std::string& substring) {
size_t idx = source.find(substring, pos);
if (idx == std::string::npos) {
skip(source.length()-pos);
return false;
} else {
skip(idx-pos);
return true;
}
}
bool BasicParser::hasNext() {
return pos < source.length();
}
bool BasicParser::isNext(const std::string& substring) {
if (source.length() - pos < substring.length()) {
return false;
}
return source.substr(pos, substring.length()) == substring;
}
char BasicParser::nextChar() {
if (!hasNext()) {
throw error("unexpected end");
}
return source[pos++];
}
void BasicParser::expect(char expected) {
char c = peek();
if (c != expected) {
throw error("'"+std::string({expected})+"' expected");
}
pos++;
}
void BasicParser::expect(const std::string& substring) {
if (substring.empty())
return;
for (uint i = 0; i < substring.length(); i++) {
if (source.length() <= pos + i || source[pos+i] != substring[i]) {
throw error(util::quote(substring)+" expected");
}
}
pos += substring.length();
}
void BasicParser::expectNewLine() {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
line++;
linestart = ++pos;
return;
}
if (is_whitespace(next)) {
pos++;
} else {
throw error("line separator expected");
}
}
}
void BasicParser::goBack(size_t count) {
if (pos < count) {
throw std::runtime_error("pos < jump");
}
if (pos) {
pos -= count;
}
}
char BasicParser::peek() {
skipWhitespace();
if (pos >= source.length()) {
throw error("unexpected end");
}
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() {
char c = peek();
if (!is_identifier_start(c)) {
if (c == '"') {
pos++;
return parseString(c);
}
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_identifier_part(source[pos])) {
pos++;
}
return std::string(source.substr(start, pos-start));
}
int64_t BasicParser::parseSimpleInt(int base) {
char c = peek();
int index = hexchar2int(c);
if (index == -1 || index >= base) {
throw error("invalid number literal");
}
int64_t value = index;
pos++;
while (hasNext()) {
c = source[pos];
while (c == '_') {
c = source[++pos];
}
index = hexchar2int(c);
if (index == -1 || index >= base) {
return value;
}
value *= base;
value += index;
pos++;
}
return value;
}
dynamic::Value BasicParser::parseNumber(int sign) {
char c = peek();
int base = 10;
if (c == '0' && pos + 1 < source.length() &&
(base = is_box(source[pos+1])) != 10) {
pos += 2;
return parseSimpleInt(base);
} else if (c == 'i' && pos + 2 < source.length() && source[pos+1] == 'n' && source[pos+2] == 'f') {
pos += 3;
return INFINITY * sign;
} else if (c == 'n' && pos + 2 < source.length() && source[pos+1] == 'a' && source[pos+2] == 'n') {
pos += 3;
return NAN * sign;
}
int64_t value = parseSimpleInt(base);
if (!hasNext()) {
return value * sign;
}
c = source[pos];
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
return sign * value * power(10.0, s * parseSimpleInt(10));
}
if (c == '.') {
pos++;
int64_t expo = 1;
while (hasNext() && source[pos] == '0') {
expo *= 10;
pos++;
}
int64_t afterdot = 0;
if (hasNext() && is_digit(source[pos])) {
afterdot = parseSimpleInt(10);
}
expo *= power(10, fmax(0, log10(afterdot) + 1));
c = source[pos];
double dvalue = (value + (afterdot / (double)expo));
if (c == 'e' || c == 'E') {
pos++;
int s = 1;
if (peek() == '-') {
s = -1;
pos++;
} else if (peek() == '+'){
pos++;
}
return sign * dvalue * power(10.0, s * parseSimpleInt(10));
}
return sign * dvalue;
}
return sign * value;
}
std::string BasicParser::parseString(char quote, bool closeRequired) {
std::stringstream ss;
while (hasNext()) {
char c = source[pos];
if (c == quote) {
pos++;
return ss.str();
}
if (c == '\\') {
pos++;
c = nextChar();
if (c >= '0' && c <= '7') {
pos--;
ss << (char)parseSimpleInt(8);
continue;
}
switch (c) {
case 'n': ss << '\n'; break;
case 'r': ss << '\r'; break;
case 'b': ss << '\b'; break;
case 't': ss << '\t'; break;
case 'f': ss << '\f'; break;
case '\'': ss << '\\'; break;
case '"': ss << '"'; break;
case '\\': ss << '\\'; break;
case '/': ss << '/'; break;
case '\n': pos++; continue;
default:
throw error("'\\" + std::string({c}) +
"' is an illegal escape");
}
continue;
}
if (c == '\n' && closeRequired) {
throw error("non-closed string literal");
}
ss << c;
pos++;
}
if (closeRequired) {
throw error("unexpected end");
}
return ss.str();
}
parsing_error BasicParser::error(std::string message) {
return parsing_error(message, filename, source, pos, line, linestart);
}

View File

@ -1,14 +1,11 @@
#ifndef CODERS_COMMONS_H_
#define CODERS_COMMONS_H_
#ifndef CODERS_COMMONS_HPP_
#define CODERS_COMMONS_HPP_
#include "../data/dynamic.hpp"
#include "../typedefs.hpp"
#include <string>
#include <stdexcept>
#include "../typedefs.h"
union number_u {
double fval;
int64_t ival;
};
inline int is_box(int c) {
switch (c) {
@ -34,11 +31,11 @@ inline bool is_whitespace(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) {
return is_identifier_start(c) || is_digit(c);
return is_identifier_start(c) || is_digit(c) || c == '-';
}
inline int hexchar2int(int c) {
@ -54,8 +51,6 @@ inline int hexchar2int(int c) {
return -1;
}
extern std::string escape_string(std::string s);
class parsing_error : public std::runtime_error {
public:
std::string filename;
@ -64,20 +59,21 @@ public:
uint line;
uint linestart;
parsing_error(std::string message,
std::string filename,
std::string source,
uint pos,
uint line,
uint linestart);
parsing_error(
std::string message,
std::string_view filename,
std::string_view source,
uint pos,
uint line,
uint linestart
);
std::string errorLog() const;
};
class BasicParser {
protected:
std::string filename;
std::string source;
std::string_view filename;
std::string_view source;
uint pos = 0;
uint line = 1;
uint linestart = 0;
@ -88,21 +84,25 @@ protected:
bool skipTo(const std::string& substring);
void expect(char expected);
void expect(const std::string& substring);
char peek();
char nextChar();
bool hasNext();
bool isNext(const std::string& substring);
void expectNewLine();
void goBack();
void goBack(size_t count=1);
std::string parseName();
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);
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
#include <zlib.h>
#include <math.h>
#include <memory>
#include "byte_utils.h"
std::vector<ubyte> gzip::compress(const ubyte* src, size_t size) {
size_t buffer_size = 23+size*1.01;

View File

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

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

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,249 +1,133 @@
#include "toml.h"
#include "commons.h"
#include <math.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <assert.h>
using std::string;
using namespace toml;
Section::Section(string name) : name(name) {
}
void Section::add(std::string name, Field field) {
if (fields.find(name) != fields.end()) {
throw std::runtime_error("field duplication");
}
fields[name] = field;
keyOrder.push_back(name);
}
void Section::add(string name, bool* ptr) {
add(name, {fieldtype::ftbool, ptr});
}
void Section::add(string name, int* ptr) {
add(name, {fieldtype::ftint, ptr});
}
void Section::add(string name, uint* ptr) {
add(name, {fieldtype::ftuint, ptr});
}
void Section::add(string name, float* ptr) {
add(name, {fieldtype::ftfloat, ptr});
}
void Section::add(string name, string* ptr) {
add(name, {fieldtype::ftstring, ptr});
}
string Section::getName() const {
return name;
}
const Field* Section::field(std::string name) const {
auto found = fields.find(name);
if (found == fields.end()) {
return nullptr;
}
return &found->second;
}
const std::vector<std::string>& Section::keys() const {
return keyOrder;
}
Wrapper::~Wrapper() {
for (auto entry : sections) {
delete entry.second;
}
}
Section& Wrapper::add(std::string name) {
if (sections.find(name) != sections.end()) {
throw std::runtime_error("section duplication");
}
Section* section = new Section(name);
sections[name] = section;
keyOrder.push_back(name);
return *section;
}
Section* Wrapper::section(std::string name) {
auto found = sections.find(name);
if (found == sections.end()) {
return nullptr;
}
return found->second;
}
std::string Wrapper::write() const {
std::stringstream ss;
for (string key : keyOrder) {
const Section* section = sections.at(key);
ss << "[" << key << "]\n";
for (const string& key : section->keys()) {
ss << key << " = ";
const Field* field = section->field(key);
assert(field != nullptr);
switch (field->type) {
case fieldtype::ftbool:
ss << (*((bool*)field->ptr) ? "true" : "false");
break;
case fieldtype::ftint: ss << *((int*)field->ptr); break;
case fieldtype::ftuint: ss << *((uint*)field->ptr); break;
case fieldtype::ftfloat: ss << *((float*)field->ptr); break;
case fieldtype::ftstring:
ss << escape_string(*((const string*)field->ptr));
break;
}
ss << "\n";
}
ss << "\n";
}
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();
}
}
#include "toml.hpp"
#include "commons.hpp"
#include "../data/setting.hpp"
#include "../data/dynamic.hpp"
#include "../util/stringutil.hpp"
#include "../files/settings_io.hpp"
#include <math.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <assert.h>
using namespace toml;
class Reader : BasicParser {
SettingsHandler& handler;
void skipWhitespace() override {
BasicParser::skipWhitespace();
if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
}
void readSection(const std::string& section) {
while (hasNext()) {
skipWhitespace();
if (!hasNext()) {
break;
}
char c = nextChar();
if (c == '[') {
std::string name = parseName();
pos++;
readSection(name);
return;
}
pos--;
std::string name = section+"."+parseName();
expect('=');
c = peek();
if (is_digit(c)) {
auto num = parseNumber(1);
if (handler.has(name)) {
handler.setValue(name, num);
}
} else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
pos++;
auto num = parseNumber(sign);
if (handler.has(name)) {
handler.setValue(name, num);
}
} else if (is_identifier_start(c)) {
std::string identifier = parseName();
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();
}
}
public:
Reader(
SettingsHandler& handler,
std::string_view file,
std::string_view source)
: BasicParser(file, source), handler(handler) {
}
void read() {
skipWhitespace();
if (!hasNext()) {
return;
}
readSection("");
}
};
void toml::parse(
SettingsHandler& handler,
const std::string& file,
const std::string& source
) {
Reader reader(handler, file, source);
reader.read();
}
std::string toml::stringify(SettingsHandler& handler) {
auto& sections = handler.getSections();
std::stringstream ss;
for (auto& section : sections) {
ss << "[" << section.name << "]\n";
for (const std::string& key : section.keys) {
ss << key << " = ";
auto setting = handler.getSetting(section.name+"."+key);
assert(setting != nullptr);
if (auto integer = dynamic_cast<IntegerSetting*>(setting)) {
ss << integer->get();
} else if (auto number = dynamic_cast<NumberSetting*>(setting)) {
ss << number->get();
} else if (auto flag = dynamic_cast<FlagSetting*>(setting)) {
ss << (flag->get() ? "true" : "false");
} else if (auto string = dynamic_cast<StringSetting*>(setting)) {
ss << util::escape(string->get());
}
ss << "\n";
}
ss << "\n";
}
return ss.str();
}

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