diff options
| author | untodesu <kirill@untode.su> | 2025-07-01 03:08:39 +0500 |
|---|---|---|
| committer | untodesu <kirill@untode.su> | 2025-07-01 03:08:39 +0500 |
| commit | 458e0005690ea9d579588a0a12368fc2c2c9a93a (patch) | |
| tree | 588a9ca6cb3c76d9193b5bd4601d64f0e50e8c8c | |
| parent | c7b0c8e0286a1b2bb7ec55e579137dfc3b22eeb9 (diff) | |
| download | voxelius-458e0005690ea9d579588a0a12368fc2c2c9a93a.tar.bz2 voxelius-458e0005690ea9d579588a0a12368fc2c2c9a93a.zip | |
I hyper-focued on refactoring again
- I put a cool-sounding "we are number one" remix on repeat and straight
up grinded the entire repository to a better state until 03:09 AM. I
guess I have something wrong in my brain that makes me do this shit
| -rw-r--r-- | .clang-format | 2 | ||||
| -rw-r--r-- | .gitattributes | 2 | ||||
| -rw-r--r-- | .vscode/c_cpp_properties.json | 9 | ||||
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | core/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | core/config/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | core/config/boolean.cc | 46 | ||||
| -rw-r--r-- | core/config/boolean.hh | 29 | ||||
| -rw-r--r-- | core/config/ivalue.hh | 15 | ||||
| -rw-r--r-- | core/config/number.hh | 129 | ||||
| -rw-r--r-- | core/config/string.cc | 18 | ||||
| -rw-r--r-- | core/config/string.hh | 22 | ||||
| -rw-r--r-- | core/io/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | core/io/buffer.cc (renamed from src/core/buffer.cc) | 61 | ||||
| -rw-r--r-- | core/io/buffer.hh (renamed from src/core/buffer.hh) | 72 | ||||
| -rw-r--r-- | core/io/cmdline.cc (renamed from src/core/cmdline.cc) | 16 | ||||
| -rw-r--r-- | core/io/cmdline.hh (renamed from src/core/cmdline.hh) | 10 | ||||
| -rw-r--r-- | core/io/config_map.cc (renamed from src/core/config.cc) | 94 | ||||
| -rw-r--r-- | core/io/config_map.hh | 33 | ||||
| -rw-r--r-- | core/math/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | core/math/aabb.cc (renamed from src/core/aabb.cc) | 18 | ||||
| -rw-r--r-- | core/math/aabb.hh (renamed from src/core/aabb.hh) | 9 | ||||
| -rw-r--r-- | core/math/angles.hh (renamed from src/core/angles.hh) | 48 | ||||
| -rw-r--r-- | core/math/concepts.hh (renamed from src/core/concepts.hh) | 10 | ||||
| -rw-r--r-- | core/math/constexpr.hh (renamed from src/core/constexpr.hh) | 108 | ||||
| -rw-r--r-- | core/math/crc64.cc (renamed from src/core/crc64.cc) | 12 | ||||
| -rw-r--r-- | core/math/crc64.hh | 12 | ||||
| -rw-r--r-- | core/math/floathacks.hh (renamed from src/core/floathacks.hh) | 18 | ||||
| -rw-r--r-- | core/math/randomizer.hh (renamed from src/core/randomizer.hh) | 19 | ||||
| -rw-r--r-- | core/math/vectors.hh | 47 | ||||
| -rw-r--r-- | core/pch.hh (renamed from src/core/pch.hh) | 0 | ||||
| -rw-r--r-- | core/resource/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | core/resource/binfile.cc (renamed from src/core/binfile.cc) | 4 | ||||
| -rw-r--r-- | core/resource/binfile.hh | 10 | ||||
| -rw-r--r-- | core/resource/image.cc (renamed from src/core/image.cc) | 4 | ||||
| -rw-r--r-- | core/resource/image.hh (renamed from src/core/image.hh) | 6 | ||||
| -rw-r--r-- | core/resource/resource.hh (renamed from src/core/resource.hh) | 6 | ||||
| -rw-r--r-- | core/utils/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | core/utils/epoch.cc (renamed from src/core/epoch.cc) | 14 | ||||
| -rw-r--r-- | core/utils/epoch.hh | 19 | ||||
| -rw-r--r-- | core/utils/string.cc (renamed from src/core/strtools.cc) | 10 | ||||
| -rw-r--r-- | core/utils/string.hh (renamed from src/core/strtools.hh) | 18 | ||||
| -rw-r--r-- | core/version.cc | 12 | ||||
| -rw-r--r-- | core/version.cc.in (renamed from src/core/version.cc.in) | 0 | ||||
| -rw-r--r-- | core/version.hh (renamed from src/core/version.hh) | 0 | ||||
| -rw-r--r-- | deps/dr_libs/include/dr_flac.h | 12561 | ||||
| -rw-r--r-- | deps/dr_libs/src/dr_impl.c | 3 | ||||
| -rw-r--r-- | game/CMakeLists.txt (renamed from src/game/CMakeLists.txt) | 8 | ||||
| -rw-r--r-- | game/client/CMakeLists.txt | 47 | ||||
| -rw-r--r-- | game/client/config/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | game/client/config/gamepad_axis.cc (renamed from src/game/client/gamepad_axis.cc) | 32 | ||||
| -rw-r--r-- | game/client/config/gamepad_axis.hh (renamed from src/game/client/gamepad_axis.hh) | 13 | ||||
| -rw-r--r-- | game/client/config/gamepad_button.cc (renamed from src/game/client/gamepad_button.cc) | 28 | ||||
| -rw-r--r-- | game/client/config/gamepad_button.hh (renamed from src/game/client/gamepad_button.hh) | 13 | ||||
| -rw-r--r-- | game/client/config/keybind.cc (renamed from src/game/client/keybind.cc) | 18 | ||||
| -rw-r--r-- | game/client/config/keybind.hh (renamed from src/game/client/keybind.hh) | 13 | ||||
| -rw-r--r-- | game/client/const.hh (renamed from src/game/client/const.hh) | 0 | ||||
| -rw-r--r-- | game/client/entity/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | game/client/entity/camera.cc | 110 | ||||
| -rw-r--r-- | game/client/entity/camera.hh | 35 | ||||
| -rw-r--r-- | game/client/entity/factory.cc | 29 | ||||
| -rw-r--r-- | game/client/entity/factory.hh | 15 | ||||
| -rw-r--r-- | game/client/entity/interpolation.cc | 63 | ||||
| -rw-r--r-- | game/client/entity/interpolation.hh (renamed from src/game/client/interpolation.hh) | 4 | ||||
| -rw-r--r-- | game/client/entity/listener.cc | 39 | ||||
| -rw-r--r-- | game/client/entity/listener.hh (renamed from src/game/client/listener.hh) | 4 | ||||
| -rw-r--r-- | game/client/entity/player_look.cc (renamed from src/game/client/player_look.cc) | 77 | ||||
| -rw-r--r-- | game/client/entity/player_look.hh (renamed from src/game/client/player_look.hh) | 4 | ||||
| -rw-r--r-- | game/client/entity/player_move.cc (renamed from src/game/client/player_move.cc) | 109 | ||||
| -rw-r--r-- | game/client/entity/player_move.hh (renamed from src/game/client/player_move.hh) | 4 | ||||
| -rw-r--r-- | game/client/entity/sound_emitter.cc (renamed from src/game/client/sound_emitter.cc) | 34 | ||||
| -rw-r--r-- | game/client/entity/sound_emitter.hh (renamed from src/game/client/sound_emitter.hh) | 11 | ||||
| -rw-r--r-- | game/client/experiments.cc | 79 | ||||
| -rw-r--r-- | game/client/experiments.hh (renamed from src/game/client/experiments.hh) | 0 | ||||
| -rw-r--r-- | game/client/game.cc (renamed from src/game/client/game.cc) | 341 | ||||
| -rw-r--r-- | game/client/game.hh (renamed from src/game/client/game.hh) | 19 | ||||
| -rw-r--r-- | game/client/globals.cc (renamed from src/game/client/globals.cc) | 14 | ||||
| -rw-r--r-- | game/client/globals.hh (renamed from src/game/client/globals.hh) | 28 | ||||
| -rw-r--r-- | game/client/gui/CMakeLists.txt | 38 | ||||
| -rw-r--r-- | game/client/gui/background.cc (renamed from src/game/client/background.cc) | 15 | ||||
| -rw-r--r-- | game/client/gui/background.hh (renamed from src/game/client/background.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/bother.cc (renamed from src/game/client/bother.cc) | 14 | ||||
| -rw-r--r-- | game/client/gui/bother.hh (renamed from src/game/client/bother.hh) | 7 | ||||
| -rw-r--r-- | game/client/gui/chat.cc (renamed from src/game/client/chat.cc) | 55 | ||||
| -rw-r--r-- | game/client/gui/chat.hh (renamed from src/game/client/chat.hh) | 8 | ||||
| -rw-r--r-- | game/client/gui/crosshair.cc (renamed from src/game/client/crosshair.cc) | 19 | ||||
| -rw-r--r-- | game/client/gui/crosshair.hh (renamed from src/game/client/crosshair.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/direct_connection.cc (renamed from src/game/client/direct_connection.cc) | 39 | ||||
| -rw-r--r-- | game/client/gui/direct_connection.hh (renamed from src/game/client/direct_connection.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/gui_screen.hh (renamed from src/game/client/gui_screen.hh) | 0 | ||||
| -rw-r--r-- | game/client/gui/hotbar.cc (renamed from src/game/client/hotbar.cc) | 69 | ||||
| -rw-r--r-- | game/client/gui/hotbar.hh (renamed from src/game/client/hotbar.hh) | 12 | ||||
| -rw-r--r-- | game/client/gui/imdraw_ext.cc (renamed from src/game/client/imdraw_ext.cc) | 4 | ||||
| -rw-r--r-- | game/client/gui/imdraw_ext.hh (renamed from src/game/client/imdraw_ext.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/language.cc (renamed from src/game/client/language.cc) | 44 | ||||
| -rw-r--r-- | game/client/gui/language.hh (renamed from src/game/client/language.hh) | 19 | ||||
| -rw-r--r-- | game/client/gui/main_menu.cc (renamed from src/game/client/main_menu.cc) | 44 | ||||
| -rw-r--r-- | game/client/gui/main_menu.hh (renamed from src/game/client/main_menu.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/message_box.cc (renamed from src/game/client/message_box.cc) | 27 | ||||
| -rw-r--r-- | game/client/gui/message_box.hh (renamed from src/game/client/message_box.hh) | 11 | ||||
| -rw-r--r-- | game/client/gui/metrics.cc (renamed from src/game/client/metrics.cc) | 44 | ||||
| -rw-r--r-- | game/client/gui/metrics.hh (renamed from src/game/client/metrics.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/play_menu.cc (renamed from src/game/client/play_menu.cc) | 74 | ||||
| -rw-r--r-- | game/client/gui/play_menu.hh (renamed from src/game/client/play_menu.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/progress_bar.cc (renamed from src/game/client/progress_bar.cc) | 31 | ||||
| -rw-r--r-- | game/client/gui/progress_bar.hh (renamed from src/game/client/progress_bar.hh) | 11 | ||||
| -rw-r--r-- | game/client/gui/scoreboard.cc (renamed from src/game/client/scoreboard.cc) | 19 | ||||
| -rw-r--r-- | game/client/gui/scoreboard.hh (renamed from src/game/client/scoreboard.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/settings.cc (renamed from src/game/client/settings.cc) | 179 | ||||
| -rw-r--r-- | game/client/gui/settings.hh | 93 | ||||
| -rw-r--r-- | game/client/gui/splash.cc (renamed from src/game/client/splash.cc) | 55 | ||||
| -rw-r--r-- | game/client/gui/splash.hh (renamed from src/game/client/splash.hh) | 4 | ||||
| -rw-r--r-- | game/client/gui/status_lines.cc (renamed from src/game/client/status_lines.cc) | 31 | ||||
| -rw-r--r-- | game/client/gui/status_lines.hh (renamed from src/game/client/status_lines.hh) | 11 | ||||
| -rw-r--r-- | game/client/gui/window_title.cc (renamed from src/game/client/window_title.cc) | 4 | ||||
| -rw-r--r-- | game/client/gui/window_title.hh (renamed from src/game/client/window_title.hh) | 4 | ||||
| -rw-r--r-- | game/client/io/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | game/client/io/gamepad.cc (renamed from src/game/client/gamepad.cc) | 106 | ||||
| -rw-r--r-- | game/client/io/gamepad.hh (renamed from src/game/client/gamepad.hh) | 25 | ||||
| -rw-r--r-- | game/client/io/glfw.hh (renamed from src/game/client/glfw.hh) | 3 | ||||
| -rw-r--r-- | game/client/main.cc (renamed from src/game/client/main.cc) | 56 | ||||
| -rw-r--r-- | game/client/pch.hh (renamed from src/game/client/pch.hh) | 0 | ||||
| -rw-r--r-- | game/client/program.cc (renamed from src/game/client/program.cc) | 6 | ||||
| -rw-r--r-- | game/client/program.hh (renamed from src/game/client/program.hh) | 0 | ||||
| -rw-r--r-- | game/client/receive.cc (renamed from src/game/client/receive.cc) | 58 | ||||
| -rw-r--r-- | game/client/receive.hh (renamed from src/game/client/receive.hh) | 0 | ||||
| -rw-r--r-- | game/client/resource/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | game/client/resource/sound_effect.cc (renamed from src/game/client/sound_effect.cc) | 4 | ||||
| -rw-r--r-- | game/client/resource/sound_effect.hh (renamed from src/game/client/sound_effect.hh) | 0 | ||||
| -rw-r--r-- | game/client/resource/texture_gui.cc (renamed from src/game/client/texture_gui.cc) | 6 | ||||
| -rw-r--r-- | game/client/resource/texture_gui.hh (renamed from src/game/client/texture_gui.hh) | 0 | ||||
| -rw-r--r-- | game/client/screenshot.cc (renamed from src/game/client/screenshot.cc) | 25 | ||||
| -rw-r--r-- | game/client/screenshot.hh (renamed from src/game/client/screenshot.hh) | 0 | ||||
| -rw-r--r-- | game/client/session.cc (renamed from src/game/client/session.cc) | 94 | ||||
| -rw-r--r-- | game/client/session.hh (renamed from src/game/client/session.hh) | 0 | ||||
| -rw-r--r-- | game/client/sound/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | game/client/sound/sound.cc (renamed from src/game/client/sound.cc) | 43 | ||||
| -rw-r--r-- | game/client/sound/sound.hh (renamed from src/game/client/sound.hh) | 16 | ||||
| -rw-r--r-- | game/client/toggles.cc (renamed from src/game/client/toggles.cc) | 21 | ||||
| -rw-r--r-- | game/client/toggles.hh (renamed from src/game/client/toggles.hh) | 0 | ||||
| -rw-r--r-- | game/client/vclient.ico (renamed from src/game/client/vclient.ico) | bin | 54361 -> 54361 bytes | |||
| -rw-r--r-- | game/client/vclient.rc (renamed from src/game/client/vclient.rc) | 0 | ||||
| -rw-r--r-- | game/client/world/CMakeLists.txt | 21 | ||||
| -rw-r--r-- | game/client/world/chunk_mesher.cc (renamed from src/game/client/chunk_mesher.cc) | 199 | ||||
| -rw-r--r-- | game/client/world/chunk_mesher.hh (renamed from src/game/client/chunk_mesher.hh) | 11 | ||||
| -rw-r--r-- | game/client/world/chunk_quad.hh (renamed from src/game/client/chunk_quad.hh) | 10 | ||||
| -rw-r--r-- | game/client/world/chunk_renderer.cc (renamed from src/game/client/chunk_renderer.cc) | 62 | ||||
| -rw-r--r-- | game/client/world/chunk_renderer.hh (renamed from src/game/client/chunk_renderer.hh) | 4 | ||||
| -rw-r--r-- | game/client/world/chunk_vbo.hh (renamed from src/game/client/chunk_vbo.hh) | 3 | ||||
| -rw-r--r-- | game/client/world/chunk_visibility.cc (renamed from src/game/client/chunk_visibility.cc) | 30 | ||||
| -rw-r--r-- | game/client/world/chunk_visibility.hh (renamed from src/game/client/chunk_visibility.hh) | 6 | ||||
| -rw-r--r-- | game/client/world/outline.cc (renamed from src/game/client/outline.cc) | 26 | ||||
| -rw-r--r-- | game/client/world/outline.hh (renamed from src/game/client/outline.hh) | 8 | ||||
| -rw-r--r-- | game/client/world/player_target.cc | 68 | ||||
| -rw-r--r-- | game/client/world/player_target.hh (renamed from src/game/client/player_target.hh) | 10 | ||||
| -rw-r--r-- | game/client/world/skybox.cc | 11 | ||||
| -rw-r--r-- | game/client/world/skybox.hh (renamed from src/game/client/skybox.hh) | 8 | ||||
| -rw-r--r-- | game/client/world/voxel_anims.cc | 31 | ||||
| -rw-r--r-- | game/client/world/voxel_anims.hh (renamed from src/game/client/voxel_anims.hh) | 8 | ||||
| -rw-r--r-- | game/client/world/voxel_atlas.cc (renamed from src/game/client/voxel_atlas.cc) | 44 | ||||
| -rw-r--r-- | game/client/world/voxel_atlas.hh (renamed from src/game/client/voxel_atlas.hh) | 15 | ||||
| -rw-r--r-- | game/client/world/voxel_sounds.cc (renamed from src/game/client/voxel_sounds.cc) | 18 | ||||
| -rw-r--r-- | game/client/world/voxel_sounds.hh (renamed from src/game/client/voxel_sounds.hh) | 12 | ||||
| -rw-r--r-- | game/server/CMakeLists.txt (renamed from src/game/server/CMakeLists.txt) | 18 | ||||
| -rw-r--r-- | game/server/chat.cc (renamed from src/game/server/chat.cc) | 0 | ||||
| -rw-r--r-- | game/server/chat.hh (renamed from src/game/server/chat.hh) | 0 | ||||
| -rw-r--r-- | game/server/game.cc (renamed from src/game/server/game.cc) | 72 | ||||
| -rw-r--r-- | game/server/game.hh (renamed from src/game/server/game.hh) | 7 | ||||
| -rw-r--r-- | game/server/globals.cc (renamed from src/game/server/globals.cc) | 8 | ||||
| -rw-r--r-- | game/server/globals.hh (renamed from src/game/server/globals.hh) | 14 | ||||
| -rw-r--r-- | game/server/main.cc (renamed from src/game/server/main.cc) | 24 | ||||
| -rw-r--r-- | game/server/pch.hh (renamed from src/game/server/pch.hh) | 0 | ||||
| -rw-r--r-- | game/server/receive.cc (renamed from src/game/server/receive.cc) | 38 | ||||
| -rw-r--r-- | game/server/receive.hh (renamed from src/game/server/receive.hh) | 0 | ||||
| -rw-r--r-- | game/server/sessions.cc (renamed from src/game/server/sessions.cc) | 67 | ||||
| -rw-r--r-- | game/server/sessions.hh (renamed from src/game/server/sessions.hh) | 16 | ||||
| -rw-r--r-- | game/server/status.cc (renamed from src/game/server/status.cc) | 2 | ||||
| -rw-r--r-- | game/server/status.hh (renamed from src/game/server/status.hh) | 0 | ||||
| -rw-r--r-- | game/server/vserver.ico (renamed from src/game/server/vserver.ico) | bin | 60201 -> 60201 bytes | |||
| -rw-r--r-- | game/server/vserver.rc (renamed from src/game/server/vserver.rc) | 0 | ||||
| -rw-r--r-- | game/server/whitelist.cc (renamed from src/game/server/whitelist.cc) | 16 | ||||
| -rw-r--r-- | game/server/whitelist.hh (renamed from src/game/server/whitelist.hh) | 11 | ||||
| -rw-r--r-- | game/server/world/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | game/server/world/inhabited.hh (renamed from src/game/server/inhabited.hh) | 5 | ||||
| -rw-r--r-- | game/server/world/overworld.cc (renamed from src/game/server/overworld.cc) | 39 | ||||
| -rw-r--r-- | game/server/world/overworld.hh (renamed from src/game/server/overworld.hh) | 24 | ||||
| -rw-r--r-- | game/server/world/universe.cc (renamed from src/game/server/universe.cc) | 60 | ||||
| -rw-r--r-- | game/server/world/universe.hh (renamed from src/game/server/universe.hh) | 12 | ||||
| -rw-r--r-- | game/server/world/unloader.cc | 77 | ||||
| -rw-r--r-- | game/server/world/unloader.hh (renamed from src/game/server/unloader.hh) | 7 | ||||
| -rw-r--r-- | game/server/world/worldgen.cc (renamed from src/game/server/worldgen.cc) | 32 | ||||
| -rw-r--r-- | game/server/world/worldgen.hh (renamed from src/game/server/worldgen.hh) | 12 | ||||
| -rw-r--r-- | game/shared/CMakeLists.txt | 27 | ||||
| -rw-r--r-- | game/shared/const.hh (renamed from src/game/shared/const.hh) | 4 | ||||
| -rw-r--r-- | game/shared/coord.hh (renamed from src/game/shared/coord.hh) | 6 | ||||
| -rw-r--r-- | game/shared/entity/CMakeLists.txt | 16 | ||||
| -rw-r--r-- | game/shared/entity/collision.cc (renamed from src/game/shared/collision.cc) | 77 | ||||
| -rw-r--r-- | game/shared/entity/collision.hh | 25 | ||||
| -rw-r--r-- | game/shared/entity/factory.cc | 36 | ||||
| -rw-r--r-- | game/shared/entity/factory.hh | 15 | ||||
| -rw-r--r-- | game/shared/entity/gravity.cc | 19 | ||||
| -rw-r--r-- | game/shared/entity/gravity.hh | 18 | ||||
| -rw-r--r-- | game/shared/entity/grounded.hh | 16 | ||||
| -rw-r--r-- | game/shared/entity/head.hh | 20 | ||||
| -rw-r--r-- | game/shared/entity/player.hh | 10 | ||||
| -rw-r--r-- | game/shared/entity/stasis.cc | 19 | ||||
| -rw-r--r-- | game/shared/entity/stasis.hh | 20 | ||||
| -rw-r--r-- | game/shared/entity/transform.cc (renamed from src/game/shared/transform.cc) | 11 | ||||
| -rw-r--r-- | game/shared/entity/transform.hh | 34 | ||||
| -rw-r--r-- | game/shared/entity/velocity.cc | 18 | ||||
| -rw-r--r-- | game/shared/entity/velocity.hh | 23 | ||||
| -rw-r--r-- | game/shared/game.cc (renamed from src/game/shared/game.cc) | 10 | ||||
| -rw-r--r-- | game/shared/game.hh (renamed from src/game/shared/game.hh) | 0 | ||||
| -rw-r--r-- | game/shared/game_items.cc | 69 | ||||
| -rw-r--r-- | game/shared/game_items.hh (renamed from src/game/shared/game_items.hh) | 0 | ||||
| -rw-r--r-- | game/shared/game_voxels.cc (renamed from src/game/shared/game_voxels.cc) | 76 | ||||
| -rw-r--r-- | game/shared/game_voxels.hh (renamed from src/game/shared/game_voxels.hh) | 0 | ||||
| -rw-r--r-- | game/shared/globals.cc (renamed from src/game/shared/globals.cc) | 0 | ||||
| -rw-r--r-- | game/shared/globals.hh (renamed from src/game/shared/globals.hh) | 0 | ||||
| -rw-r--r-- | game/shared/pch.hh (renamed from src/game/shared/pch.hh) | 2 | ||||
| -rw-r--r-- | game/shared/protocol.cc (renamed from src/game/shared/protocol.cc) | 43 | ||||
| -rw-r--r-- | game/shared/protocol.hh (renamed from src/game/shared/protocol.hh) | 19 | ||||
| -rw-r--r-- | game/shared/splash.cc (renamed from src/game/shared/splash.cc) | 0 | ||||
| -rw-r--r-- | game/shared/splash.hh (renamed from src/game/shared/splash.hh) | 0 | ||||
| -rw-r--r-- | game/shared/threading.cc (renamed from src/game/shared/threading.cc) | 10 | ||||
| -rw-r--r-- | game/shared/threading.hh (renamed from src/game/shared/threading.hh) | 0 | ||||
| -rw-r--r-- | game/shared/types.hh (renamed from src/game/shared/types.hh) | 0 | ||||
| -rw-r--r-- | game/shared/world/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | game/shared/world/chunk.cc | 69 | ||||
| -rw-r--r-- | game/shared/world/chunk.hh (renamed from src/game/shared/chunk.hh) | 8 | ||||
| -rw-r--r-- | game/shared/world/chunk_aabb.cc (renamed from src/game/shared/chunk_aabb.cc) | 16 | ||||
| -rw-r--r-- | game/shared/world/chunk_aabb.hh (renamed from src/game/shared/chunk_aabb.hh) | 3 | ||||
| -rw-r--r-- | game/shared/world/dimension.cc (renamed from src/game/shared/dimension.cc) | 39 | ||||
| -rw-r--r-- | game/shared/world/dimension.hh (renamed from src/game/shared/dimension.hh) | 23 | ||||
| -rw-r--r-- | game/shared/world/feature.cc (renamed from src/game/shared/feature.cc) | 13 | ||||
| -rw-r--r-- | game/shared/world/feature.hh (renamed from src/game/shared/feature.hh) | 6 | ||||
| -rw-r--r-- | game/shared/world/item_registry.cc | 103 | ||||
| -rw-r--r-- | game/shared/world/item_registry.hh (renamed from src/game/shared/item_registry.hh) | 24 | ||||
| -rw-r--r-- | game/shared/world/ray_dda.cc (renamed from src/game/shared/ray_dda.cc) | 25 | ||||
| -rw-r--r-- | game/shared/world/ray_dda.hh (renamed from src/game/shared/ray_dda.hh) | 6 | ||||
| -rw-r--r-- | game/shared/world/voxel_registry.cc (renamed from src/game/shared/voxel_registry.cc) | 70 | ||||
| -rw-r--r-- | game/shared/world/voxel_registry.hh (renamed from src/game/shared/voxel_registry.hh) | 25 | ||||
| -rw-r--r-- | game/shared/world/voxel_storage.cc (renamed from src/game/shared/voxel_storage.cc) | 18 | ||||
| -rw-r--r-- | game/shared/world/voxel_storage.hh (renamed from src/game/shared/voxel_storage.hh) | 10 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 43 | ||||
| -rw-r--r-- | src/core/binfile.hh | 10 | ||||
| -rw-r--r-- | src/core/config.hh | 181 | ||||
| -rw-r--r-- | src/core/crc64.hh | 12 | ||||
| -rw-r--r-- | src/core/epoch.hh | 19 | ||||
| -rw-r--r-- | src/core/vectors.hh | 47 | ||||
| -rw-r--r-- | src/game/client/CMakeLists.txt | 126 | ||||
| -rw-r--r-- | src/game/client/camera.cc | 107 | ||||
| -rw-r--r-- | src/game/client/camera.hh | 32 | ||||
| -rw-r--r-- | src/game/client/experiments.cc | 77 | ||||
| -rw-r--r-- | src/game/client/factory.cc | 28 | ||||
| -rw-r--r-- | src/game/client/factory.hh | 12 | ||||
| -rw-r--r-- | src/game/client/interpolation.cc | 61 | ||||
| -rw-r--r-- | src/game/client/listener.cc | 38 | ||||
| -rw-r--r-- | src/game/client/player_target.cc | 66 | ||||
| -rw-r--r-- | src/game/client/settings.hh | 83 | ||||
| -rw-r--r-- | src/game/client/skybox.cc | 11 | ||||
| -rw-r--r-- | src/game/client/voxel_anims.cc | 29 | ||||
| -rw-r--r-- | src/game/server/unloader.cc | 76 | ||||
| -rw-r--r-- | src/game/shared/CMakeLists.txt | 57 | ||||
| -rw-r--r-- | src/game/shared/chunk.cc | 69 | ||||
| -rw-r--r-- | src/game/shared/collision.hh | 19 | ||||
| -rw-r--r-- | src/game/shared/factory.cc | 35 | ||||
| -rw-r--r-- | src/game/shared/factory.hh | 12 | ||||
| -rw-r--r-- | src/game/shared/game_items.cc | 61 | ||||
| -rw-r--r-- | src/game/shared/gravity.cc | 18 | ||||
| -rw-r--r-- | src/game/shared/gravity.hh | 12 | ||||
| -rw-r--r-- | src/game/shared/grounded.hh | 13 | ||||
| -rw-r--r-- | src/game/shared/head.hh | 14 | ||||
| -rw-r--r-- | src/game/shared/item_registry.cc | 103 | ||||
| -rw-r--r-- | src/game/shared/player.hh | 7 | ||||
| -rw-r--r-- | src/game/shared/stasis.cc | 19 | ||||
| -rw-r--r-- | src/game/shared/stasis.hh | 14 | ||||
| -rw-r--r-- | src/game/shared/transform.hh | 25 | ||||
| -rw-r--r-- | src/game/shared/velocity.cc | 17 | ||||
| -rw-r--r-- | src/game/shared/velocity.hh | 17 |
281 files changed, 16603 insertions, 3555 deletions
diff --git a/.clang-format b/.clang-format index 5c82cae..e6d043d 100644 --- a/.clang-format +++ b/.clang-format @@ -102,7 +102,7 @@ Language: Cpp LineEnding: CRLF MaxEmptyLinesToKeep: 1 NamespaceIndentation: None -PenaltyIndentedWhitespace: 1 +PenaltyIndentedWhitespace: 2 PointerAlignment: Left ReflowComments: true SortIncludes: true diff --git a/.gitattributes b/.gitattributes index 7dda955..7123ceb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -/src/core/version.cc.in linguist-language=C
+/core/version.cc.in linguist-language=C
/deps/* linguist-vendored
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index b5ff492..b392014 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -14,8 +14,9 @@ "_USE_MATH_DEFINES"
],
"includePath": [
- "${workspaceFolder}/build*/_deps/glfw-src/include",
- "${workspaceFolder}/build**/_deps/glfw-src/include",
+ "${workspaceFolder}/build/_deps/glfw-src/include",
+ "${workspaceFolder}/build32/_deps/glfw-src/include",
+ "${workspaceFolder}/build64/_deps/glfw-src/include",
"${workspaceFolder}/deps/dr_libs/include",
"${workspaceFolder}/deps/emhash/include",
"${workspaceFolder}/deps/enet/include",
@@ -31,8 +32,8 @@ "${workspaceFolder}/deps/spdlog/include",
"${workspaceFolder}/deps/stb/include",
"${workspaceFolder}/deps/thread_pool/include",
- "${workspaceFolder}/src/game/",
- "${workspaceFolder}/src",
+ "${workspaceFolder}/game/",
+ "${workspaceFolder}",
"${default}"
]
}
diff --git a/CMakeLists.txt b/CMakeLists.txt index 558d481..a82c4c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+add_subdirectory(core)
add_subdirectory(deps)
-add_subdirectory(src)
+add_subdirectory(game)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_custom_target(thirdpartylegalnotices ALL
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt new file mode 100644 index 0000000..0710274 --- /dev/null +++ b/core/CMakeLists.txt @@ -0,0 +1,22 @@ +configure_file("${CMAKE_CURRENT_LIST_DIR}/version.cc.in" "${CMAKE_CURRENT_LIST_DIR}/version.cc") + +add_library(core STATIC + "${CMAKE_CURRENT_LIST_DIR}/pch.hh" + "${CMAKE_CURRENT_LIST_DIR}/version.cc" + "${CMAKE_CURRENT_LIST_DIR}/version.hh") +target_compile_features(core PUBLIC cxx_std_20) +target_include_directories(core PUBLIC "${PROJECT_SOURCE_DIR}") +target_precompile_headers(core PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") +target_link_libraries(core PUBLIC enet emhash glm physfs spdlog stb) + +add_subdirectory(config) +add_subdirectory(io) +add_subdirectory(math) +add_subdirectory(resource) +add_subdirectory(utils) + +if(WIN32) + target_compile_definitions(core PUBLIC _CRT_SECURE_NO_WARNINGS) + target_compile_definitions(core PUBLIC _USE_MATH_DEFINES) + target_compile_definitions(core PUBLIC NOMINMAX) +endif() diff --git a/core/config/CMakeLists.txt b/core/config/CMakeLists.txt new file mode 100644 index 0000000..39752d4 --- /dev/null +++ b/core/config/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(core PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/boolean.cc" + "${CMAKE_CURRENT_LIST_DIR}/boolean.hh" + "${CMAKE_CURRENT_LIST_DIR}/ivalue.hh" + "${CMAKE_CURRENT_LIST_DIR}/number.hh" + "${CMAKE_CURRENT_LIST_DIR}/string.cc" + "${CMAKE_CURRENT_LIST_DIR}/string.hh") diff --git a/core/config/boolean.cc b/core/config/boolean.cc new file mode 100644 index 0000000..a8d670c --- /dev/null +++ b/core/config/boolean.cc @@ -0,0 +1,46 @@ +#include "core/pch.hh" + +#include "core/config/boolean.hh" + +config::Boolean::Boolean(bool default_value) +{ + m_value = default_value; +} + +void config::Boolean::set(const char* value) +{ + m_value = from_string(value); +} + +const char* config::Boolean::get(void) const +{ + return to_string(m_value); +} + +bool config::Boolean::get_value(void) const +{ + return m_value; +} + +void config::Boolean::set_value(bool value) +{ + m_value = value; +} + +const char* config::Boolean::to_string(bool value) +{ + if(value) { + return "true"; + } else { + return "false"; + } +} + +bool config::Boolean::from_string(const char* value) +{ + if(std::strcmp(value, "false") && !std::strcmp(value, "true")) { + return true; + } else { + return false; + } +} diff --git a/core/config/boolean.hh b/core/config/boolean.hh new file mode 100644 index 0000000..5285676 --- /dev/null +++ b/core/config/boolean.hh @@ -0,0 +1,29 @@ +#ifndef CORE_CONFIG_BOOLEAN_HH +#define CORE_CONFIG_BOOLEAN_HH 1 +#pragma once + +#include "core/config/ivalue.hh" + +namespace config +{ +class Boolean : public IValue { +public: + explicit Boolean(bool default_value = false); + virtual ~Boolean(void) = default; + + virtual void set(const char* value) override; + virtual const char* get(void) const override; + + bool get_value(void) const; + void set_value(bool value); + +private: + bool m_value; + +public: + static const char* to_string(bool value); + static bool from_string(const char* value); +}; +} // namespace config + +#endif // CORE_CONFIG_BOOLEAN_HH diff --git a/core/config/ivalue.hh b/core/config/ivalue.hh new file mode 100644 index 0000000..cf893a5 --- /dev/null +++ b/core/config/ivalue.hh @@ -0,0 +1,15 @@ +#ifndef CORE_CONFIG_IVALUE_HH +#define CORE_CONFIG_IVALUE_HH 1 +#pragma once + +namespace config +{ +class IValue { +public: + virtual ~IValue(void) = default; + virtual void set(const char* value) = 0; + virtual const char* get(void) const = 0; +}; +} // namespace config + +#endif // CORE_CONFIG_IVALUE_HH diff --git a/core/config/number.hh b/core/config/number.hh new file mode 100644 index 0000000..9907993 --- /dev/null +++ b/core/config/number.hh @@ -0,0 +1,129 @@ +#ifndef CORE_CONFIG_NUMBER_HH +#define CORE_CONFIG_NUMBER_HH 1 +#pragma once + +#include "core/config/ivalue.hh" +#include "core/math/concepts.hh" + +namespace config +{ +template<math::Arithmetic T> +class Number : public IValue { +public: + explicit Number(T default_value = T(0)); + explicit Number(T default_value, T min_value, T max_value); + virtual ~Number(void) = default; + + virtual void set(const char* value) override; + virtual const char* get(void) const override; + + T get_value(void) const; + void set_value(T value); + + T get_min_value(void) const; + T get_max_value(void) const; + void set_limits(T min_value, T max_value); + +private: + T m_value; + T m_min_value; + T m_max_value; + std::string m_string; +}; +} // namespace config + +namespace config +{ +class Int final : public Number<int> { +public: + using Number<int>::Number; +}; + +class Float final : public Number<float> { +public: + using Number<float>::Number; +}; + +class Unsigned final : public Number<unsigned int> { +public: + using Number<unsigned int>::Number; +}; + +class Unsigned64 final : public Number<std::uint64_t> { +public: + using Number<std::uint64_t>::Number; +}; + +class SizeType final : public Number<std::size_t> { +public: + using Number<std::size_t>::Number; +}; +} // namespace config + +template<math::Arithmetic T> +inline config::Number<T>::Number(T default_value) +{ + m_value = default_value; + m_min_value = std::numeric_limits<T>::lowest(); + m_max_value = std::numeric_limits<T>::max(); + m_string = std::to_string(default_value); +} + +template<math::Arithmetic T> +inline config::Number<T>::Number(T default_value, T min_value, T max_value) +{ + m_value = default_value; + m_min_value = min_value; + m_max_value = max_value; + m_string = std::to_string(default_value); +} + +template<math::Arithmetic T> +inline void config::Number<T>::set(const char* value) +{ + std::istringstream(value) >> m_value; + m_value = std::clamp(m_value, m_min_value, m_max_value); + m_string = std::to_string(m_value); +} + +template<math::Arithmetic T> +inline const char* config::Number<T>::get(void) const +{ + return m_string.c_str(); +} + +template<math::Arithmetic T> +inline T config::Number<T>::get_value(void) const +{ + return m_value; +} + +template<math::Arithmetic T> +inline void config::Number<T>::set_value(T value) +{ + m_value = std::clamp(value, m_min_value, m_max_value); + m_string = std::to_string(m_value); +} + +template<math::Arithmetic T> +inline T config::Number<T>::get_min_value(void) const +{ + return m_min_value; +} + +template<math::Arithmetic T> +inline T config::Number<T>::get_max_value(void) const +{ + return m_max_value; +} + +template<math::Arithmetic T> +inline void config::Number<T>::set_limits(T min_value, T max_value) +{ + m_min_value = min_value; + m_max_value = max_value; + m_value = std::clamp(m_value, m_min_value, m_max_value); + m_string = std::to_string(m_value); +} + +#endif // CORE_CONFIG_NUMBER_HH diff --git a/core/config/string.cc b/core/config/string.cc new file mode 100644 index 0000000..08e8c0d --- /dev/null +++ b/core/config/string.cc @@ -0,0 +1,18 @@ +#include "core/pch.hh" + +#include "core/config/string.hh" + +config::String::String(const char* default_value) +{ + m_value = default_value; +} + +void config::String::set(const char* value) +{ + m_value = value; +} + +const char* config::String::get(void) const +{ + return m_value.c_str(); +} diff --git a/core/config/string.hh b/core/config/string.hh new file mode 100644 index 0000000..09d14c9 --- /dev/null +++ b/core/config/string.hh @@ -0,0 +1,22 @@ +#ifndef CORE_CONFIG_STRING_HH +#define CORE_CONFIG_STRING_HH 1 +#pragma once + +#include "core/config/ivalue.hh" + +namespace config +{ +class String : public IValue { +public: + explicit String(const char* default_value); + virtual ~String(void) = default; + + virtual void set(const char* value) override; + virtual const char* get(void) const override; + +private: + std::string m_value; +}; +} // namespace config + +#endif // CORE_CONFIG_STRING_HH diff --git a/core/io/CMakeLists.txt b/core/io/CMakeLists.txt new file mode 100644 index 0000000..0639d2c --- /dev/null +++ b/core/io/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(core PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/buffer.cc" + "${CMAKE_CURRENT_LIST_DIR}/buffer.hh" + "${CMAKE_CURRENT_LIST_DIR}/cmdline.cc" + "${CMAKE_CURRENT_LIST_DIR}/cmdline.hh" + "${CMAKE_CURRENT_LIST_DIR}/config_map.cc" + "${CMAKE_CURRENT_LIST_DIR}/config_map.hh") diff --git a/src/core/buffer.cc b/core/io/buffer.cc index 0e18f4f..62f981c 100644 --- a/src/core/buffer.cc +++ b/core/io/buffer.cc @@ -1,49 +1,49 @@ #include "core/pch.hh" -#include "core/buffer.hh" +#include "core/io/buffer.hh" -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" -ReadBuffer::ReadBuffer(const void* data, std::size_t size) +io::ReadBuffer::ReadBuffer(const void* data, std::size_t size) { reset(data, size); } -ReadBuffer::ReadBuffer(const ENetPacket* packet) +io::ReadBuffer::ReadBuffer(const ENetPacket* packet) { reset(packet); } -ReadBuffer::ReadBuffer(PHYSFS_File* file) +io::ReadBuffer::ReadBuffer(PHYSFS_File* file) { reset(file); } -std::size_t ReadBuffer::size(void) const +std::size_t io::ReadBuffer::size(void) const { return m_vector.size(); } -const std::byte* ReadBuffer::data(void) const +const std::byte* io::ReadBuffer::data(void) const { return m_vector.data(); } -void ReadBuffer::reset(const void* data, std::size_t size) +void io::ReadBuffer::reset(const void* data, std::size_t size) { auto bytes = reinterpret_cast<const std::byte*>(data); m_vector.assign(bytes, bytes + size); m_position = 0U; } -void ReadBuffer::reset(const ENetPacket* packet) +void io::ReadBuffer::reset(const ENetPacket* packet) { auto bytes_ptr = reinterpret_cast<const std::byte*>(packet->data); m_vector.assign(bytes_ptr, bytes_ptr + packet->dataLength); m_position = 0; } -void ReadBuffer::reset(PHYSFS_File* file) +void io::ReadBuffer::reset(PHYSFS_File* file) { m_vector.resize(PHYSFS_fileLength(file)); m_position = 0; @@ -52,12 +52,12 @@ void ReadBuffer::reset(PHYSFS_File* file) PHYSFS_readBytes(file, m_vector.data(), m_vector.size()); } -float ReadBuffer::read_FP32(void) +float io::ReadBuffer::read_FP32(void) { - return floathacks::uint32_to_float(read_UI32()); + return math::uint32_to_float(read_UI32()); } -std::uint8_t ReadBuffer::read_UI8(void) +std::uint8_t io::ReadBuffer::read_UI8(void) { if((m_position + 1U) <= m_vector.size()) { auto result = static_cast<std::uint8_t>(m_vector[m_position]); @@ -69,7 +69,7 @@ std::uint8_t ReadBuffer::read_UI8(void) return 0; } -std::uint16_t ReadBuffer::read_UI16(void) +std::uint16_t io::ReadBuffer::read_UI16(void) { if((m_position + 2U) <= m_vector.size()) { auto result = UINT16_C(0x0000); @@ -83,7 +83,7 @@ std::uint16_t ReadBuffer::read_UI16(void) return 0; } -std::uint32_t ReadBuffer::read_UI32(void) +std::uint32_t io::ReadBuffer::read_UI32(void) { if((m_position + 4U) <= m_vector.size()) { auto result = UINT32_C(0x00000000); @@ -99,7 +99,7 @@ std::uint32_t ReadBuffer::read_UI32(void) return 0; } -std::uint64_t ReadBuffer::read_UI64(void) +std::uint64_t io::ReadBuffer::read_UI64(void) { if((m_position + 8U) <= m_vector.size()) { auto result = UINT64_C(0x0000000000000000); @@ -119,7 +119,7 @@ std::uint64_t ReadBuffer::read_UI64(void) return 0; } -std::string ReadBuffer::read_string(void) +std::string io::ReadBuffer::read_string(void) { auto size = static_cast<std::size_t>(read_UI16()); auto result = std::string(); @@ -135,33 +135,33 @@ std::string ReadBuffer::read_string(void) return result; } -std::size_t WriteBuffer::size(void) const +std::size_t io::WriteBuffer::size(void) const { return m_vector.size(); } -const std::byte* WriteBuffer::data(void) const +const std::byte* io::WriteBuffer::data(void) const { return m_vector.data(); } -void WriteBuffer::reset(void) +void io::WriteBuffer::reset(void) { m_vector.clear(); } -void WriteBuffer::write_UI8(std::uint8_t value) +void io::WriteBuffer::write_UI8(std::uint8_t value) { m_vector.push_back(static_cast<std::byte>(value)); } -void WriteBuffer::write_UI16(std::uint16_t value) +void io::WriteBuffer::write_UI16(std::uint16_t value) { m_vector.push_back(static_cast<std::byte>(UINT16_C(0xFF) & ((value & UINT16_C(0xFF00)) >> 8U))); m_vector.push_back(static_cast<std::byte>(UINT16_C(0xFF) & ((value & UINT16_C(0x00FF)) >> 0U))); } -void WriteBuffer::write_UI32(std::uint32_t value) +void io::WriteBuffer::write_UI32(std::uint32_t value) { m_vector.push_back(static_cast<std::byte>(UINT32_C(0xFF) & ((value & UINT32_C(0xFF000000)) >> 24U))); m_vector.push_back(static_cast<std::byte>(UINT32_C(0xFF) & ((value & UINT32_C(0x00FF0000)) >> 16U))); @@ -169,7 +169,7 @@ void WriteBuffer::write_UI32(std::uint32_t value) m_vector.push_back(static_cast<std::byte>(UINT32_C(0xFF) & ((value & UINT32_C(0x000000FF)) >> 0U))); } -void WriteBuffer::write_UI64(std::uint64_t value) +void io::WriteBuffer::write_UI64(std::uint64_t value) { m_vector.push_back(static_cast<std::byte>(UINT64_C(0xFF) & ((value & UINT64_C(0xFF00000000000000)) >> 56U))); m_vector.push_back(static_cast<std::byte>(UINT64_C(0xFF) & ((value & UINT64_C(0x00FF000000000000)) >> 48U))); @@ -181,17 +181,18 @@ void WriteBuffer::write_UI64(std::uint64_t value) m_vector.push_back(static_cast<std::byte>(UINT64_C(0xFF) & ((value & UINT64_C(0x00000000000000FF)) >> 0U))); } -void WriteBuffer::write_string(const std::string& value) +void io::WriteBuffer::write_string(const std::string& value) { - const std::size_t size = vx::min<std::size_t>(UINT16_MAX, value.size()); + const std::size_t size = math::min<std::size_t>(UINT16_MAX, value.size()); write_UI16(static_cast<std::uint16_t>(size)); - for(std::size_t i = 0; i < size; m_vector.push_back(static_cast<std::byte>(value[i++]))) - ; + for(std::size_t i = 0; i < size; m_vector.push_back(static_cast<std::byte>(value[i++]))) { + // empty + } } -PHYSFS_File* WriteBuffer::to_file(const char* path, bool append) const +PHYSFS_File* io::WriteBuffer::to_file(const char* path, bool append) const { if(auto file = (append ? PHYSFS_openAppend(path) : PHYSFS_openWrite(path))) { PHYSFS_writeBytes(file, m_vector.data(), m_vector.size()); @@ -201,7 +202,7 @@ PHYSFS_File* WriteBuffer::to_file(const char* path, bool append) const return nullptr; } -ENetPacket* WriteBuffer::to_packet(enet_uint32 flags) const +ENetPacket* io::WriteBuffer::to_packet(enet_uint32 flags) const { return enet_packet_create(m_vector.data(), m_vector.size(), flags); } diff --git a/src/core/buffer.hh b/core/io/buffer.hh index 35efa8b..205381f 100644 --- a/src/core/buffer.hh +++ b/core/io/buffer.hh @@ -1,8 +1,10 @@ -#ifndef CORE_BUFFER_HH -#define CORE_BUFFER_HH 1 +#ifndef CORE_IO_BUFFER_HH +#define CORE_IO_BUFFER_HH 1 -#include "core/floathacks.hh" +#include "core/math/floathacks.hh" +namespace io +{ class ReadBuffer final { public: ReadBuffer(void) = default; @@ -45,7 +47,10 @@ private: std::vector<std::byte> m_vector; std::size_t m_position; }; +} // namespace io +namespace io +{ class WriteBuffer final { public: WriteBuffer(void) = default; @@ -85,167 +90,168 @@ public: private: std::vector<std::byte> m_vector; }; +} // namespace io -inline std::int8_t ReadBuffer::read_I8(void) +inline std::int8_t io::ReadBuffer::read_I8(void) { return static_cast<std::int8_t>(read_UI8()); } -inline std::int16_t ReadBuffer::read_I16(void) +inline std::int16_t io::ReadBuffer::read_I16(void) { return static_cast<std::int16_t>(read_UI16()); } -inline std::int32_t ReadBuffer::read_I32(void) +inline std::int32_t io::ReadBuffer::read_I32(void) { return static_cast<std::int32_t>(read_UI32()); } -inline std::int64_t ReadBuffer::read_I64(void) +inline std::int64_t io::ReadBuffer::read_I64(void) { return static_cast<std::int64_t>(read_UI64()); } -inline ReadBuffer& ReadBuffer::operator>>(float& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(float& value) { value = read_FP32(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::int8_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::int8_t& value) { value = read_I8(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::int16_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::int16_t& value) { value = read_I16(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::int32_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::int32_t& value) { value = read_I32(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::int64_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::int64_t& value) { value = read_I64(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::uint8_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::uint8_t& value) { value = read_UI8(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::uint16_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::uint16_t& value) { value = read_UI16(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::uint32_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::uint32_t& value) { value = read_UI32(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::uint64_t& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::uint64_t& value) { value = read_UI64(); return *this; } -inline ReadBuffer& ReadBuffer::operator>>(std::string& value) +inline io::ReadBuffer& io::ReadBuffer::operator>>(std::string& value) { value = read_string(); return *this; } -inline void WriteBuffer::write_FP32(float value) +inline void io::WriteBuffer::write_FP32(float value) { - write_UI32(floathacks::float_to_uint32(value)); + write_UI32(math::float_to_uint32(value)); } -inline void WriteBuffer::write_I8(std::int8_t value) +inline void io::WriteBuffer::write_I8(std::int8_t value) { write_UI8(static_cast<std::uint8_t>(value)); } -inline void WriteBuffer::write_I16(std::int16_t value) +inline void io::WriteBuffer::write_I16(std::int16_t value) { write_UI16(static_cast<std::uint16_t>(value)); } -inline void WriteBuffer::write_I32(std::int32_t value) +inline void io::WriteBuffer::write_I32(std::int32_t value) { write_UI32(static_cast<std::uint32_t>(value)); } -inline void WriteBuffer::write_I64(std::int64_t value) +inline void io::WriteBuffer::write_I64(std::int64_t value) { write_UI64(static_cast<std::uint64_t>(value)); } -inline WriteBuffer& WriteBuffer::operator<<(float value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(float value) { write_FP32(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::int8_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::int8_t value) { write_I8(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::int16_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::int16_t value) { write_I16(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::int32_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::int32_t value) { write_I32(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::int64_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::int64_t value) { write_I64(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::uint8_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::uint8_t value) { write_UI8(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::uint16_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::uint16_t value) { write_UI16(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::uint32_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::uint32_t value) { write_UI32(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(std::uint64_t value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(std::uint64_t value) { write_UI64(value); return *this; } -inline WriteBuffer& WriteBuffer::operator<<(const std::string& value) +inline io::WriteBuffer& io::WriteBuffer::operator<<(const std::string& value) { write_string(value); return *this; diff --git a/src/core/cmdline.cc b/core/io/cmdline.cc index c83f9e6..c43727d 100644 --- a/src/core/cmdline.cc +++ b/core/io/cmdline.cc @@ -1,6 +1,6 @@ #include "core/pch.hh" -#include "core/cmdline.hh" +#include "core/io/cmdline.hh" // Valid options always start with OPTION_PREFIX, can contain // a bunch of OPTION_PREFIX'es inside and never end with one @@ -20,12 +20,14 @@ static inline bool is_option_string(const std::string& string) static inline std::string get_option(const std::string& string) { std::size_t i; - for(i = 0; string[i] == OPTION_PREFIX; ++i) - ; + for(i = 0; string[i] == OPTION_PREFIX; ++i) { + // empty + } + return std::string(string.cbegin() + i, string.cend()); } -void cmdline::create(int argc, char** argv) +void io::cmdline::create(int argc, char** argv) { for(int idx = 1; idx < argc; ++idx) { std::string string = argv[idx]; @@ -55,7 +57,7 @@ void cmdline::create(int argc, char** argv) } } -void cmdline::insert(const char* option, const char* argument) +void io::cmdline::insert(const char* option, const char* argument) { if(argument == nullptr) { options.insert_or_assign(option, std::string()); @@ -64,7 +66,7 @@ void cmdline::insert(const char* option, const char* argument) } } -const char* cmdline::get(const char* option, const char* fallback) +const char* io::cmdline::get(const char* option, const char* fallback) { auto it = options.find(option); @@ -75,7 +77,7 @@ const char* cmdline::get(const char* option, const char* fallback) return it->second.c_str(); } -bool cmdline::contains(const char* option) +bool io::cmdline::contains(const char* option) { return options.count(option); } diff --git a/src/core/cmdline.hh b/core/io/cmdline.hh index 6e31076..d68d9ef 100644 --- a/src/core/cmdline.hh +++ b/core/io/cmdline.hh @@ -1,13 +1,13 @@ -#ifndef CORE_CMDLINE_HH -#define CORE_CMDLINE_HH 1 +#ifndef CORE_IO_CMDLINE_HH +#define CORE_IO_CMDLINE_HH 1 #pragma once -namespace cmdline +namespace io::cmdline { void create(int argc, char** argv); void insert(const char* option, const char* argument = nullptr); const char* get(const char* option, const char* fallback = nullptr); bool contains(const char* option); -} // namespace cmdline +} // namespace io::cmdline -#endif // CORE_CMDLINE_HH +#endif // CORE_IO_CMDLINE_HH diff --git a/src/core/config.cc b/core/io/config_map.cc index 3202fb6..7bce1a6 100644 --- a/src/core/config.cc +++ b/core/io/config_map.cc @@ -1,82 +1,22 @@ #include "core/pch.hh" -#include "core/config.hh" +#include "core/io/config_map.hh" -#include "core/cmdline.hh" -#include "core/strtools.hh" +#include "core/config/ivalue.hh" +#include "core/io/cmdline.hh" +#include "core/utils/string.hh" #include "core/version.hh" -ConfigBoolean::ConfigBoolean(bool default_value) -{ - m_value = default_value; - m_string = ConfigBoolean::to_string(default_value); -} - -void ConfigBoolean::set(const char* value) -{ - m_value = ConfigBoolean::from_string(value); - m_string = ConfigBoolean::to_string(m_value); -} - -const char* ConfigBoolean::get(void) const -{ - return m_string.c_str(); -} - -bool ConfigBoolean::get_value(void) const -{ - return m_value; -} - -void ConfigBoolean::set_value(bool value) -{ - m_value = value; - m_string = ConfigBoolean::to_string(m_value); -} - -const char* ConfigBoolean::to_string(bool value) -{ - if(value) { - return "true"; - } else { - return "false"; - } -} - -bool ConfigBoolean::from_string(const char* value) -{ - if(std::strcmp(value, "false") && !std::strcmp(value, "true")) { - return true; - } else { - return false; - } -} - -ConfigString::ConfigString(const char* default_value) -{ - m_value = default_value; -} - -void ConfigString::set(const char* value) -{ - m_value = value; -} - -const char* ConfigString::get(void) const -{ - return m_value.c_str(); -} - -void Config::load_cmdline(void) +void io::ConfigMap::load_cmdline(void) { for(auto it : m_values) { - if(auto value = cmdline::get(it.first.c_str())) { + if(auto value = io::cmdline::get(it.first.c_str())) { it.second->set(value); } } } -bool Config::load_file(const char* path) +bool io::ConfigMap::load_file(const char* path) { if(auto file = PHYSFS_openRead(path)) { auto source = std::string(PHYSFS_fileLength(file), char(0x00)); @@ -91,12 +31,12 @@ bool Config::load_file(const char* path) auto comment = line.find_first_of('#'); if(comment == std::string::npos) { - kv_string = strtools::trim_whitespace(line); + kv_string = utils::trim_whitespace(line); } else { - kv_string = strtools::trim_whitespace(line.substr(0, comment)); + kv_string = utils::trim_whitespace(line.substr(0, comment)); } - if(strtools::is_whitespace(kv_string)) { + if(utils::is_whitespace(kv_string)) { // Ignore empty or commented out lines continue; } @@ -108,8 +48,8 @@ bool Config::load_file(const char* path) continue; } - auto kv_name = strtools::trim_whitespace(kv_string.substr(0, separator)); - auto kv_value = strtools::trim_whitespace(kv_string.substr(separator + 1)); + auto kv_name = utils::trim_whitespace(kv_string.substr(0, separator)); + auto kv_value = utils::trim_whitespace(kv_string.substr(separator + 1)); auto kv_pair = m_values.find(kv_name); @@ -127,7 +67,7 @@ bool Config::load_file(const char* path) return false; } -bool Config::save_file(const char* path) const +bool io::ConfigMap::save_file(const char* path) const { std::ostringstream stream; @@ -152,7 +92,7 @@ bool Config::save_file(const char* path) const return false; } -bool Config::set_value(const char* name, const char* value) +bool io::ConfigMap::set_value(const char* name, const char* value) { auto kv_pair = m_values.find(name); @@ -164,7 +104,7 @@ bool Config::set_value(const char* name, const char* value) return false; } -const char* Config::get_value(const char* name) const +const char* io::ConfigMap::get_value(const char* name) const { auto kv_pair = m_values.find(name); if(kv_pair != m_values.cend()) { @@ -174,12 +114,12 @@ const char* Config::get_value(const char* name) const } } -void Config::add_value(const char* name, IConfigValue& vref) +void io::ConfigMap::add_value(const char* name, config::IValue& vref) { m_values.insert_or_assign(name, &vref); } -const IConfigValue* Config::find(const char* name) const +const config::IValue* io::ConfigMap::find(const char* name) const { auto kv_pair = m_values.find(name); diff --git a/core/io/config_map.hh b/core/io/config_map.hh new file mode 100644 index 0000000..8ff92e1 --- /dev/null +++ b/core/io/config_map.hh @@ -0,0 +1,33 @@ +#ifndef CORE_CONFIG_MAP_HH +#define CORE_CONFIG_MAP_HH 1 +#pragma once + +namespace config +{ +class IValue; +} // namespace config + +namespace io +{ +class ConfigMap final { +public: + ConfigMap(void) = default; + virtual ~ConfigMap(void) = default; + + void load_cmdline(void); + bool load_file(const char* path); + bool save_file(const char* path) const; + + bool set_value(const char* name, const char* value); + const char* get_value(const char* name) const; + + void add_value(const char* name, config::IValue& vref); + + const config::IValue* find(const char* name) const; + +private: + std::unordered_map<std::string, config::IValue*> m_values; +}; +} // namespace io + +#endif // CORE_CONFIG_MAP_HH diff --git a/core/math/CMakeLists.txt b/core/math/CMakeLists.txt new file mode 100644 index 0000000..ab03087 --- /dev/null +++ b/core/math/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(core PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/aabb.cc" + "${CMAKE_CURRENT_LIST_DIR}/aabb.hh" + "${CMAKE_CURRENT_LIST_DIR}/angles.hh" + "${CMAKE_CURRENT_LIST_DIR}/concepts.hh" + "${CMAKE_CURRENT_LIST_DIR}/constexpr.hh" + "${CMAKE_CURRENT_LIST_DIR}/crc64.cc" + "${CMAKE_CURRENT_LIST_DIR}/crc64.hh" + "${CMAKE_CURRENT_LIST_DIR}/floathacks.hh" + "${CMAKE_CURRENT_LIST_DIR}/randomizer.hh" + "${CMAKE_CURRENT_LIST_DIR}/vectors.hh") diff --git a/src/core/aabb.cc b/core/math/aabb.cc index 3661143..f5c7e14 100644 --- a/src/core/aabb.cc +++ b/core/math/aabb.cc @@ -1,25 +1,25 @@ #include "core/pch.hh" -#include "core/aabb.hh" +#include "core/math/aabb.hh" -AABB::AABB(const glm::fvec3& min, const glm::fvec3& max) +math::AABB::AABB(const glm::fvec3& min, const glm::fvec3& max) { set_bounds(min, max); } -void AABB::set_bounds(const glm::fvec3& min, const glm::fvec3& max) +void math::AABB::set_bounds(const glm::fvec3& min, const glm::fvec3& max) { this->min = min; this->max = max; } -void AABB::set_offset(const glm::fvec3& base, const glm::fvec3& size) +void math::AABB::set_offset(const glm::fvec3& base, const glm::fvec3& size) { this->min = base; this->max = base + size; } -bool AABB::contains(const glm::fvec3& point) const +bool math::AABB::contains(const glm::fvec3& point) const { auto result = true; result = result && (point.x >= min.x) && (point.x <= max.x); @@ -28,7 +28,7 @@ bool AABB::contains(const glm::fvec3& point) const return result; } -bool AABB::intersect(const AABB& other_box) const +bool math::AABB::intersect(const AABB& other_box) const { auto result = true; result = result && (min.x < other_box.max.x) && (max.x > other_box.min.x); @@ -37,21 +37,21 @@ bool AABB::intersect(const AABB& other_box) const return result; } -AABB AABB::combine_with(const AABB& other_box) const +math::AABB math::AABB::combine_with(const math::AABB& other_box) const { AABB result; result.set_bounds(min, other_box.max); return result; } -AABB AABB::multiply_with(const AABB& other_box) const +math::AABB math::AABB::multiply_with(const math::AABB& other_box) const { AABB result; result.set_bounds(other_box.min, max); return result; } -AABB AABB::push(const glm::fvec3& vector) const +math::AABB math::AABB::push(const glm::fvec3& vector) const { AABB result; result.set_bounds(min + vector, max + vector); diff --git a/src/core/aabb.hh b/core/math/aabb.hh index 680c270..943eeee 100644 --- a/src/core/aabb.hh +++ b/core/math/aabb.hh @@ -1,7 +1,9 @@ -#ifndef CORE_AABB_HH -#define CORE_AABB_HH 1 +#ifndef CORE_MATH_AABB_HH +#define CORE_MATH_AABB_HH 1 #pragma once +namespace math +{ class AABB final { public: AABB(void) = default; @@ -25,5 +27,6 @@ public: glm::fvec3 min; glm::fvec3 max; }; +} // namespace math -#endif /* CORE_AABB_HH */ +#endif // CORE_MATH_AABB_HH diff --git a/src/core/angles.hh b/core/math/angles.hh index 95e42dc..868160b 100644 --- a/src/core/angles.hh +++ b/core/math/angles.hh @@ -1,31 +1,31 @@ -#ifndef CORE_ANGLES_HH -#define CORE_ANGLES_HH 1 +#ifndef CORE_MATH_ANGLES_HH +#define CORE_MATH_ANGLES_HH 1 #pragma once -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" -constexpr float A180 = vx::radians(180.0f); -constexpr float A360 = vx::radians(360.0f); +constexpr float A180 = math::radians(180.0f); +constexpr float A360 = math::radians(360.0f); -namespace cxangles +namespace math { float wrap_180(float angle); float wrap_360(float angle); -} // namespace cxangles +} // namespace math -namespace cxangles +namespace math { glm::fvec3 wrap_180(const glm::fvec3& angles); glm::fvec3 wrap_360(const glm::fvec3& angles); -} // namespace cxangles +} // namespace math -namespace cxangles +namespace math { void vectors(const glm::fvec3& angles, glm::fvec3& forward); void vectors(const glm::fvec3& angles, glm::fvec3* forward, glm::fvec3* right, glm::fvec3* up); -} // namespace cxangles +} // namespace math -inline float cxangles::wrap_180(float angle) +inline float math::wrap_180(float angle) { const auto result = std::fmod(angle + A180, A360); @@ -36,30 +36,30 @@ inline float cxangles::wrap_180(float angle) return result - A180; } -inline float cxangles::wrap_360(float angle) +inline float math::wrap_360(float angle) { return std::fmod(std::fmod(angle, A360) + A360, A360); } -inline glm::fvec3 cxangles::wrap_180(const glm::fvec3& angles) +inline glm::fvec3 math::wrap_180(const glm::fvec3& angles) { return glm::fvec3 { - cxangles::wrap_180(angles.x), - cxangles::wrap_180(angles.y), - cxangles::wrap_180(angles.z), + math::wrap_180(angles.x), + math::wrap_180(angles.y), + math::wrap_180(angles.z), }; } -inline glm::fvec3 cxangles::wrap_360(const glm::fvec3& angles) +inline glm::fvec3 math::wrap_360(const glm::fvec3& angles) { return glm::fvec3 { - cxangles::wrap_360(angles.x), - cxangles::wrap_360(angles.y), - cxangles::wrap_360(angles.z), + math::wrap_360(angles.x), + math::wrap_360(angles.y), + math::wrap_360(angles.z), }; } -inline void cxangles::vectors(const glm::fvec3& angles, glm::fvec3& forward) +inline void math::vectors(const glm::fvec3& angles, glm::fvec3& forward) { const float cosp = std::cos(angles.x); const float cosy = std::cos(angles.y); @@ -71,7 +71,7 @@ inline void cxangles::vectors(const glm::fvec3& angles, glm::fvec3& forward) forward.z = cosp * cosy * (-1.0f); } -inline void cxangles::vectors(const glm::fvec3& angles, glm::fvec3* forward, glm::fvec3* right, glm::fvec3* up) +inline void math::vectors(const glm::fvec3& angles, glm::fvec3* forward, glm::fvec3* right, glm::fvec3* up) { if(!forward && !right && !up) { // There's no point in figuring out @@ -104,4 +104,4 @@ inline void cxangles::vectors(const glm::fvec3& angles, glm::fvec3* forward, glm } } -#endif // CORE_ANGLES_HH +#endif // CORE_MATH_ANGLES_HH diff --git a/src/core/concepts.hh b/core/math/concepts.hh index 9168c1e..1b9beb9 100644 --- a/src/core/concepts.hh +++ b/core/math/concepts.hh @@ -1,8 +1,8 @@ -#ifndef CORE_CONCEPTS_HH -#define CORE_CONCEPTS_HH 1 +#ifndef CORE_MATH_CONCEPTS_HH +#define CORE_MATH_CONCEPTS_HH 1 #pragma once -namespace vx +namespace math { template<typename T> concept Arithmetic = std::is_arithmetic_v<T>; @@ -10,6 +10,6 @@ template<typename T> concept Integer = std::is_integral_v<T>; template<typename T> concept FloatingPoint = std::is_floating_point_v<T>; -} // namespace vx +} // namespace math -#endif // CORE_CONCEPTS_HH +#endif // CORE_MATH_CONCEPTS_HH diff --git a/src/core/constexpr.hh b/core/math/constexpr.hh index 0c122e0..3e4fcfa 100644 --- a/src/core/constexpr.hh +++ b/core/math/constexpr.hh @@ -1,48 +1,48 @@ -#ifndef CORE_CONSTEXPR_HH -#define CORE_CONSTEXPR_HH 1 +#ifndef CORE_MATH_CONSTEXPR_HH +#define CORE_MATH_CONSTEXPR_HH 1 #pragma once -#include "core/concepts.hh" +#include "core/math/concepts.hh" -namespace vx +namespace math { -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T abs(const T x); template<typename T, std::size_t L> constexpr static inline const std::size_t array_size(const T (&)[L]); -template<vx::Integer T, vx::FloatingPoint F> +template<math::Integer T, math::FloatingPoint F> constexpr static inline const T ceil(const F x); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T degrees(const T x); -template<vx::Integer T, vx::FloatingPoint F> +template<math::Integer T, math::FloatingPoint F> constexpr static inline const T floor(const F x); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T clamp(const T x, const T min, const T max); -template<vx::Arithmetic T, vx::FloatingPoint F> +template<math::Arithmetic T, math::FloatingPoint F> constexpr static inline const T lerp(const T x, const T y, const F a); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T log2(const T x); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T max(const T x, const T y); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T min(const T x, const T y); -template<vx::Integer T> +template<math::Integer T> requires std::is_signed_v<T> constexpr static inline const T mod_signed(const T x, const T m); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T pow2(const T x); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const T radians(const T x); -template<vx::Arithmetic T> +template<math::Arithmetic T> constexpr static inline const bool range(const T x, const T min, const T max); -template<vx::Arithmetic T, vx::FloatingPoint F> +template<math::Arithmetic T, math::FloatingPoint F> constexpr static inline const T sign(const F x); -template<vx::Arithmetic T, vx::FloatingPoint F> +template<math::Arithmetic T, math::FloatingPoint F> constexpr static inline const T smoothstep(const T x, const T y, const F a); -} // namespace vx +} // namespace math -template<vx::Arithmetic T> -constexpr static inline const T vx::abs(const T x) +template<math::Arithmetic T> +constexpr static inline const T math::abs(const T x) { if(x < static_cast<T>(0)) { return -x; @@ -52,13 +52,13 @@ constexpr static inline const T vx::abs(const T x) } template<typename T, std::size_t L> -constexpr static inline const std::size_t vx::array_size(const T (&)[L]) +constexpr static inline const std::size_t math::array_size(const T (&)[L]) { return L; } -template<vx::Integer T, vx::FloatingPoint F> -constexpr static inline const T vx::ceil(const F x) +template<math::Integer T, math::FloatingPoint F> +constexpr static inline const T math::ceil(const F x) { const T ival = static_cast<T>(x); @@ -69,14 +69,14 @@ constexpr static inline const T vx::ceil(const F x) } } -template<vx::Arithmetic T> -constexpr static inline const T vx::degrees(const T x) +template<math::Arithmetic T> +constexpr static inline const T math::degrees(const T x) { return x * static_cast<T>(180.0) / static_cast<T>(M_PI); } -template<vx::Integer T, vx::FloatingPoint F> -constexpr static inline const T vx::floor(const F x) +template<math::Integer T, math::FloatingPoint F> +constexpr static inline const T math::floor(const F x) { const T ival = static_cast<T>(x); @@ -87,8 +87,8 @@ constexpr static inline const T vx::floor(const F x) } } -template<vx::Arithmetic T> -constexpr static inline const T vx::clamp(const T x, const T min, const T max) +template<math::Arithmetic T> +constexpr static inline const T math::clamp(const T x, const T min, const T max) { if(x < min) { return min; @@ -99,24 +99,24 @@ constexpr static inline const T vx::clamp(const T x, const T min, const T max) } } -template<vx::Arithmetic T, vx::FloatingPoint F> -constexpr static inline const T vx::lerp(const T x, const T y, const F a) +template<math::Arithmetic T, math::FloatingPoint F> +constexpr static inline const T math::lerp(const T x, const T y, const F a) { return static_cast<T>(static_cast<F>(x) * (static_cast<F>(1.0f) - a) + static_cast<F>(y) * a); } -template<vx::Arithmetic T> -constexpr static inline const T vx::log2(const T x) +template<math::Arithmetic T> +constexpr static inline const T math::log2(const T x) { if(x < 2) { return 0; } else { - return vx::log2<T>((x + 1) >> 1) + 1; + return math::log2<T>((x + 1) >> 1) + 1; } } -template<vx::Arithmetic T> -constexpr static inline const T vx::max(const T x, const T y) +template<math::Arithmetic T> +constexpr static inline const T math::max(const T x, const T y) { if(x < y) { return y; @@ -125,8 +125,8 @@ constexpr static inline const T vx::max(const T x, const T y) } } -template<vx::Arithmetic T> -constexpr static inline const T vx::min(const T x, const T y) +template<math::Arithmetic T> +constexpr static inline const T math::min(const T x, const T y) { if(x > y) { return y; @@ -135,9 +135,9 @@ constexpr static inline const T vx::min(const T x, const T y) } } -template<vx::Integer T> +template<math::Integer T> requires std::is_signed_v<T> -constexpr static inline const T vx::mod_signed(const T x, const T m) +constexpr static inline const T math::mod_signed(const T x, const T m) { auto result = static_cast<T>(x % m); @@ -148,8 +148,8 @@ constexpr static inline const T vx::mod_signed(const T x, const T m) } } -template<vx::Arithmetic T> -constexpr static inline const T vx::pow2(const T x) +template<math::Arithmetic T> +constexpr static inline const T math::pow2(const T x) { T value = static_cast<T>(1); while(value < x) @@ -157,20 +157,20 @@ constexpr static inline const T vx::pow2(const T x) return value; } -template<vx::Arithmetic T> -constexpr static inline const T vx::radians(const T x) +template<math::Arithmetic T> +constexpr static inline const T math::radians(const T x) { return x * static_cast<T>(M_PI) / static_cast<T>(180.0); } -template<vx::Arithmetic T> -constexpr static inline const bool vx::range(const T x, const T min, const T max) +template<math::Arithmetic T> +constexpr static inline const bool math::range(const T x, const T min, const T max) { return ((x >= min) && (x <= max)); } -template<vx::Arithmetic T, vx::FloatingPoint F> -constexpr static inline const T vx::sign(const F x) +template<math::Arithmetic T, math::FloatingPoint F> +constexpr static inline const T math::sign(const F x) { if(x < F(0)) { return T(-1); @@ -181,11 +181,11 @@ constexpr static inline const T vx::sign(const F x) } } -template<vx::Arithmetic T, vx::FloatingPoint F> -constexpr static inline const T vx::smoothstep(const T x, const T y, const F a) +template<math::Arithmetic T, math::FloatingPoint F> +constexpr static inline const T math::smoothstep(const T x, const T y, const F a) { - const F t = vx::clamp<F>((a - x) / (y - x), F(0), F(1)); + const F t = math::clamp<F>((a - x) / (y - x), F(0), F(1)); return static_cast<T>(t * t * (F(3) - F(2) * t)); } -#endif // CORE_CONSTEXPR_HH +#endif // CORE_MATH_CONSTEXPR_HH diff --git a/src/core/crc64.cc b/core/math/crc64.cc index c2ca53c..15b67cb 100644 --- a/src/core/crc64.cc +++ b/core/math/crc64.cc @@ -1,6 +1,6 @@ #include "core/pch.hh" -#include "core/crc64.hh" +#include "core/math/crc64.hh" // The lookup table for CRC64 checksum; this lookup // table is generated using ECMA-182 compilant parameters: @@ -267,7 +267,7 @@ constexpr static const std::uint64_t crc_table[256] = { 0x9AFCE626CE85B507, }; -std::uint64_t crc64::get(const void* buffer, std::size_t size, std::uint64_t combine) +std::uint64_t math::crc64(const void* buffer, std::size_t size, std::uint64_t combine) { auto data = reinterpret_cast<const std::uint8_t*>(buffer); for(std::size_t i = 0; i < size; ++i) @@ -275,12 +275,12 @@ std::uint64_t crc64::get(const void* buffer, std::size_t size, std::uint64_t com return combine; } -std::uint64_t crc64::get(const std::vector<std::byte>& buffer, std::uint64_t combine) +std::uint64_t math::crc64(const std::vector<std::byte>& buffer, std::uint64_t combine) { - return crc64::get(buffer.data(), buffer.size(), combine); + return math::crc64(buffer.data(), buffer.size(), combine); } -std::uint64_t crc64::get(const std::string& buffer, std::uint64_t combine) +std::uint64_t math::crc64(const std::string& buffer, std::uint64_t combine) { - return crc64::get(buffer.data(), buffer.size(), combine); + return math::crc64(buffer.data(), buffer.size(), combine); } diff --git a/core/math/crc64.hh b/core/math/crc64.hh new file mode 100644 index 0000000..ac2c4b6 --- /dev/null +++ b/core/math/crc64.hh @@ -0,0 +1,12 @@ +#ifndef CORE_MATH_CRC64_HH +#define CORE_MATH_CRC64_HH 1 +#pragma once + +namespace math +{ +std::uint64_t crc64(const void* buffer, std::size_t size, std::uint64_t combine = UINT64_C(0)); +std::uint64_t crc64(const std::vector<std::byte>& buffer, std::uint64_t combine = UINT64_C(0)); +std::uint64_t crc64(const std::string& buffer, std::uint64_t combine = UINT64_C(0)); +} // namespace math + +#endif // CORE_MATH_CRC64_HH diff --git a/src/core/floathacks.hh b/core/math/floathacks.hh index b81f98c..dc8c235 100644 --- a/src/core/floathacks.hh +++ b/core/math/floathacks.hh @@ -1,19 +1,19 @@ -#ifndef CORE_FLOATHACKS_HH -#define CORE_FLOATHACKS_HH 1 +#ifndef CORE_MATH_FLOATHACKS_HH +#define CORE_MATH_FLOATHACKS_HH 1 #pragma once -namespace floathacks +namespace math { static inline float int32_to_float(const std::int32_t value); static inline float uint32_to_float(const std::uint32_t value); static inline std::int32_t float_to_int32(const float value); static inline std::uint32_t float_to_uint32(const float value); -} // namespace floathacks +} // namespace math static_assert(std::numeric_limits<float>::is_iec559, "Floathacks only works with IEEE 754 compliant floats"); static_assert(sizeof(std::int32_t) == sizeof(float), "Floathacks requires 32-bit integers to match float size"); -static inline float floathacks::int32_to_float(const std::int32_t value) +static inline float math::int32_to_float(const std::int32_t value) { union { std::int32_t src; @@ -23,7 +23,7 @@ static inline float floathacks::int32_to_float(const std::int32_t value) return hack.dst; } -static inline float floathacks::uint32_to_float(const std::uint32_t value) +static inline float math::uint32_to_float(const std::uint32_t value) { union { std::uint32_t src; @@ -33,7 +33,7 @@ static inline float floathacks::uint32_to_float(const std::uint32_t value) return hack.dst; } -static inline std::int32_t floathacks::float_to_int32(const float value) +static inline std::int32_t math::float_to_int32(const float value) { union { float src; @@ -43,7 +43,7 @@ static inline std::int32_t floathacks::float_to_int32(const float value) return hack.dst; } -static inline std::uint32_t floathacks::float_to_uint32(const float value) +static inline std::uint32_t math::float_to_uint32(const float value) { union { float src; @@ -53,4 +53,4 @@ static inline std::uint32_t floathacks::float_to_uint32(const float value) return hack.dst; } -#endif // CORE_FLOATHACKS_HH +#endif // CORE_MATH_FLOATHACKS_HH diff --git a/src/core/randomizer.hh b/core/math/randomizer.hh index 4d1bb83..206af42 100644 --- a/src/core/randomizer.hh +++ b/core/math/randomizer.hh @@ -1,7 +1,9 @@ -#ifndef CORE_RANDOMIZER_HH -#define CORE_RANDOMIZER_HH 1 +#ifndef CORE_MATH_RANDOMIZER_HH +#define CORE_MATH_RANDOMIZER_HH 1 #pragma once +namespace math +{ template<typename T> class Randomizer final { public: @@ -17,9 +19,10 @@ private: std::mt19937_64 m_twister; std::uniform_int_distribution<std::size_t> m_dist; }; +} // namespace math template<typename T> -inline Randomizer<T>::Randomizer(void) +inline math::Randomizer<T>::Randomizer(void) { m_vector.clear(); m_twister.seed(std::random_device()()); @@ -27,7 +30,7 @@ inline Randomizer<T>::Randomizer(void) } template<typename T> -inline Randomizer<T>::Randomizer(std::uint64_t seed) +inline math::Randomizer<T>::Randomizer(std::uint64_t seed) { m_vector.clear(); m_twister.seed(seed); @@ -35,23 +38,23 @@ inline Randomizer<T>::Randomizer(std::uint64_t seed) } template<typename T> -inline void Randomizer<T>::add(const T& value) +inline void math::Randomizer<T>::add(const T& value) { m_vector.push_back(value); m_dist = std::uniform_int_distribution<std::size_t>(0, m_vector.size() - 1); } template<typename T> -inline const T& Randomizer<T>::get(void) +inline const T& math::Randomizer<T>::get(void) { return m_vector.at(m_dist(m_twister)); } template<typename T> -inline void Randomizer<T>::clear(void) +inline void math::Randomizer<T>::clear(void) { m_vector.clear(); m_dist = std::uniform_int_distribution<std::size_t>(0, 0); } -#endif // CORE_RANDOMIZER_HH +#endif // CORE_MATH_RANDOMIZER_HH diff --git a/core/math/vectors.hh b/core/math/vectors.hh new file mode 100644 index 0000000..06d1bcf --- /dev/null +++ b/core/math/vectors.hh @@ -0,0 +1,47 @@ +#ifndef CORE_MATH_VECTORS_HH +#define CORE_MATH_VECTORS_HH 1 +#pragma once + +#include "core/math/concepts.hh" + +// core/vectors.hh - because NO ONE would POSSIBLY +// need integer-based distance calculations in a +// game about voxels. That would be INSANE! :D + +namespace math +{ +template<math::Arithmetic T> +constexpr static inline const T length2(const glm::vec<2, T>& vector); +template<math::Arithmetic T> +constexpr static inline const T length2(const glm::vec<3, T>& vector); +template<math::Arithmetic T> +constexpr static inline const T distance2(const glm::vec<2, T>& vector_a, const glm::vec<2, T>& vector_b); +template<math::Arithmetic T> +constexpr static inline const T distance2(const glm::vec<3, T>& vector_a, const glm::vec<3, T>& vector_b); +} // namespace math + +template<math::Arithmetic T> +constexpr static inline const T math::length2(const glm::vec<2, T>& vector) +{ + return (vector.x * vector.x) + (vector.y * vector.y); +} + +template<math::Arithmetic T> +constexpr static inline const T math::length2(const glm::vec<3, T>& vector) +{ + return (vector.x * vector.x) + (vector.y * vector.y) + (vector.z * vector.z); +} + +template<math::Arithmetic T> +constexpr static inline const T math::distance2(const glm::vec<2, T>& vector_a, const glm::vec<2, T>& vector_b) +{ + return math::length2(vector_a - vector_b); +} + +template<math::Arithmetic T> +constexpr static inline const T math::distance2(const glm::vec<3, T>& vector_a, const glm::vec<3, T>& vector_b) +{ + return math::length2(vector_a - vector_b); +} + +#endif // CORE_MATH_VECTORS_HH diff --git a/src/core/pch.hh b/core/pch.hh index 924b1dd..924b1dd 100644 --- a/src/core/pch.hh +++ b/core/pch.hh diff --git a/core/resource/CMakeLists.txt b/core/resource/CMakeLists.txt new file mode 100644 index 0000000..ef2ffae --- /dev/null +++ b/core/resource/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(core PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/binfile.cc" + "${CMAKE_CURRENT_LIST_DIR}/binfile.hh" + "${CMAKE_CURRENT_LIST_DIR}/image.cc" + "${CMAKE_CURRENT_LIST_DIR}/image.hh" + "${CMAKE_CURRENT_LIST_DIR}/resource.hh") diff --git a/src/core/binfile.cc b/core/resource/binfile.cc index aa39039..e17db50 100644 --- a/src/core/binfile.cc +++ b/core/resource/binfile.cc @@ -1,8 +1,8 @@ #include "core/pch.hh" -#include "core/binfile.hh" +#include "core/resource/binfile.hh" -#include "core/resource.hh" +#include "core/resource/resource.hh" static emhash8::HashMap<std::string, resource_ptr<BinFile>> resource_map; diff --git a/core/resource/binfile.hh b/core/resource/binfile.hh new file mode 100644 index 0000000..6bb5e3d --- /dev/null +++ b/core/resource/binfile.hh @@ -0,0 +1,10 @@ +#ifndef CORE_RESOURCE_BINFILE_HH +#define CORE_RESOURCE_BINFILE_HH 1 +#pragma once + +struct BinFile final { + std::byte* buffer; + std::size_t size; +}; + +#endif // CORE_RESOURCE_BINFILE_HH diff --git a/src/core/image.cc b/core/resource/image.cc index 08be3d4..1601703 100644 --- a/src/core/image.cc +++ b/core/resource/image.cc @@ -1,8 +1,8 @@ #include "core/pch.hh" -#include "core/image.hh" +#include "core/resource/image.hh" -#include "core/resource.hh" +#include "core/resource/resource.hh" static emhash8::HashMap<std::string, resource_ptr<Image>> resource_map; diff --git a/src/core/image.hh b/core/resource/image.hh index b5c69e0..13e912e 100644 --- a/src/core/image.hh +++ b/core/resource/image.hh @@ -1,5 +1,5 @@ -#ifndef CORE_IMAGE_HH -#define CORE_IMAGE_HH 1 +#ifndef CORE_RESOURCE_IMAGE_HH +#define CORE_RESOURCE_IMAGE_HH 1 #pragma once constexpr static unsigned int IMAGE_LOAD_GRAY = 0x0001U; @@ -10,4 +10,4 @@ struct Image final { glm::ivec2 size; }; -#endif // CORE_IMAGE_HH +#endif // CORE_RESOURCE_IMAGE_HH diff --git a/src/core/resource.hh b/core/resource/resource.hh index 3b9fff0..4946ffa 100644 --- a/src/core/resource.hh +++ b/core/resource/resource.hh @@ -1,5 +1,5 @@ -#ifndef CORE_RESOURCE_HH -#define CORE_RESOURCE_HH 1 +#ifndef CORE_RESOURCE_RESOURCE_HH +#define CORE_RESOURCE_RESOURCE_HH 1 #pragma once template<typename T> @@ -15,4 +15,4 @@ template<typename T> void soft_cleanup(void); } // namespace resource -#endif // CORE_RESOURCE_HH +#endif // CORE_RESOURCE_RESOURCE_HH diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt new file mode 100644 index 0000000..4f96261 --- /dev/null +++ b/core/utils/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(core PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/epoch.cc" + "${CMAKE_CURRENT_LIST_DIR}/epoch.hh" + "${CMAKE_CURRENT_LIST_DIR}/string.cc" + "${CMAKE_CURRENT_LIST_DIR}/string.hh") diff --git a/src/core/epoch.cc b/core/utils/epoch.cc index 978eeb3..36bdb79 100644 --- a/src/core/epoch.cc +++ b/core/utils/epoch.cc @@ -1,38 +1,38 @@ #include "core/pch.hh" -#include "core/epoch.hh" +#include "core/utils/epoch.hh" -std::uint64_t epoch::seconds(void) +std::uint64_t utils::unix_seconds(void) { const auto elapsed = std::chrono::system_clock::now().time_since_epoch(); return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(elapsed).count()); } -std::uint64_t epoch::milliseconds(void) +std::uint64_t utils::unix_milliseconds(void) { const auto elapsed = std::chrono::system_clock::now().time_since_epoch(); return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()); } -std::uint64_t epoch::microseconds(void) +std::uint64_t utils::unix_microseconds(void) { const auto elapsed = std::chrono::system_clock::now().time_since_epoch(); return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()); } -std::int64_t epoch::signed_seconds(void) +std::int64_t utils::signed_unix_seconds(void) { const auto elapsed = std::chrono::system_clock::now().time_since_epoch(); return static_cast<std::int64_t>(std::chrono::duration_cast<std::chrono::seconds>(elapsed).count()); } -std::int64_t epoch::signed_milliseconds(void) +std::int64_t utils::signed_unix_milliseconds(void) { const auto elapsed = std::chrono::system_clock::now().time_since_epoch(); return static_cast<std::int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count()); } -std::int64_t epoch::signed_microseconds(void) +std::int64_t utils::signed_unix_microseconds(void) { const auto elapsed = std::chrono::system_clock::now().time_since_epoch(); return static_cast<std::int64_t>(std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()); diff --git a/core/utils/epoch.hh b/core/utils/epoch.hh new file mode 100644 index 0000000..1cd7f43 --- /dev/null +++ b/core/utils/epoch.hh @@ -0,0 +1,19 @@ +#ifndef CORE_UTILS_EPOCH_HH +#define CORE_UTILS_EPOCH_HH 1 +#pragma once + +namespace utils +{ +std::uint64_t unix_seconds(void); +std::uint64_t unix_milliseconds(void); +std::uint64_t unix_microseconds(void); +} // namespace utils + +namespace utils +{ +std::int64_t signed_unix_seconds(void); +std::int64_t signed_unix_milliseconds(void); +std::int64_t signed_unix_microseconds(void); +} // namespace utils + +#endif // CORE_UTILS_EPOCH_HH diff --git a/src/core/strtools.cc b/core/utils/string.cc index 4edd86b..5497cec 100644 --- a/src/core/strtools.cc +++ b/core/utils/string.cc @@ -1,10 +1,10 @@ #include "core/pch.hh" -#include "core/strtools.hh" +#include "core/utils/string.hh" constexpr static const char* WHITESPACE_CHARS = " \t\r\n"; -bool strtools::is_whitespace(const std::string& string) +bool utils::is_whitespace(const std::string& string) { if(string.find_first_not_of(WHITESPACE_CHARS) == std::string::npos) { return true; @@ -15,7 +15,7 @@ bool strtools::is_whitespace(const std::string& string) } } -std::string strtools::join(const std::vector<std::string>& strings, const std::string& separator) +std::string utils::join(const std::vector<std::string>& strings, const std::string& separator) { std::ostringstream stream; for(const std::string& str : strings) @@ -23,7 +23,7 @@ std::string strtools::join(const std::vector<std::string>& strings, const std::s return stream.str(); } -std::vector<std::string> strtools::split(const std::string& string, const std::string& separator) +std::vector<std::string> utils::split(const std::string& string, const std::string& separator) { std::size_t pos = 0; std::size_t prev = 0; @@ -41,7 +41,7 @@ std::vector<std::string> strtools::split(const std::string& string, const std::s return result; } -std::string strtools::trim_whitespace(const std::string& string) +std::string utils::trim_whitespace(const std::string& string) { auto su = string.find_first_not_of(WHITESPACE_CHARS); auto sv = string.find_last_not_of(WHITESPACE_CHARS); diff --git a/src/core/strtools.hh b/core/utils/string.hh index 476a5f7..3ba56a0 100644 --- a/src/core/strtools.hh +++ b/core/utils/string.hh @@ -1,21 +1,21 @@ -#ifndef CORE_STRTOOLS_HH -#define CORE_STRTOOLS_HH 1 +#ifndef CORE_UTILS_STRING_HH +#define CORE_UTILS_STRING_HH 1 #pragma once -namespace strtools +namespace utils { bool is_whitespace(const std::string& string); -} // namespace strtools +} // namespace utils -namespace strtools +namespace utils { std::string join(const std::vector<std::string>& strings, const std::string& separator); std::vector<std::string> split(const std::string& string, const std::string& separator); -} // namespace strtools +} // namespace utils -namespace strtools +namespace utils { std::string trim_whitespace(const std::string& string); -} // namespace strtools +} // namespace utils -#endif // CORE_STRTOOLS_HH +#endif // CORE_UTILS_STRING_HH diff --git a/core/version.cc b/core/version.cc new file mode 100644 index 0000000..be19502 --- /dev/null +++ b/core/version.cc @@ -0,0 +1,12 @@ +#include "core/pch.hh" + +#include "core/version.hh" + +// clang-format off +const unsigned long project_version_major = 0; +const unsigned long project_version_minor = 0; +const unsigned long project_version_patch = 1; +const unsigned long project_version_tweak = 2526; +// clang-format on + +const char* project_version_string = "0.0.1.2526"; diff --git a/src/core/version.cc.in b/core/version.cc.in index 0c255c9..0c255c9 100644 --- a/src/core/version.cc.in +++ b/core/version.cc.in diff --git a/src/core/version.hh b/core/version.hh index 627df17..627df17 100644 --- a/src/core/version.hh +++ b/core/version.hh diff --git a/deps/dr_libs/include/dr_flac.h b/deps/dr_libs/include/dr_flac.h new file mode 100644 index 0000000..4f0f6c9 --- /dev/null +++ b/deps/dr_libs/include/dr_flac.h @@ -0,0 +1,12561 @@ +/* +FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_flac - v0.12.44 - TBD + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs +*/ + +/* +RELEASE NOTES - v0.12.0 +======================= +Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs. + + +Improved Client-Defined Memory Allocation +----------------------------------------- +The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The +existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom +allocation callbacks are specified. + +To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this: + + void* my_malloc(size_t sz, void* pUserData) + { + return malloc(sz); + } + void* my_realloc(void* p, size_t sz, void* pUserData) + { + return realloc(p, sz); + } + void my_free(void* p, void* pUserData) + { + free(p); + } + + ... + + drflac_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = &myData; + allocationCallbacks.onMalloc = my_malloc; + allocationCallbacks.onRealloc = my_realloc; + allocationCallbacks.onFree = my_free; + drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks); + +The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines. + +Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC, +DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions. + +Every API that opens a drflac object now takes this extra parameter. These include the following: + + drflac_open() + drflac_open_relaxed() + drflac_open_with_metadata() + drflac_open_with_metadata_relaxed() + drflac_open_file() + drflac_open_file_with_metadata() + drflac_open_memory() + drflac_open_memory_with_metadata() + drflac_open_and_read_pcm_frames_s32() + drflac_open_and_read_pcm_frames_s16() + drflac_open_and_read_pcm_frames_f32() + drflac_open_file_and_read_pcm_frames_s32() + drflac_open_file_and_read_pcm_frames_s16() + drflac_open_file_and_read_pcm_frames_f32() + drflac_open_memory_and_read_pcm_frames_s32() + drflac_open_memory_and_read_pcm_frames_s16() + drflac_open_memory_and_read_pcm_frames_f32() + + + +Optimizations +------------- +Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly +improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes +advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which +means it will be disabled when DR_FLAC_NO_CRC is used. + +The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in +particular. 16-bit streams should also see some improvement. + +drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32 +to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths. + +A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo +channel reconstruction which is the last part of the decoding process. + +The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when +compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at +compile time and the REV instruction requires ARM architecture version 6. + +An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling. + + +Removed APIs +------------ +The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0: + + drflac_read_s32() -> drflac_read_pcm_frames_s32() + drflac_read_s16() -> drflac_read_pcm_frames_s16() + drflac_read_f32() -> drflac_read_pcm_frames_f32() + drflac_seek_to_sample() -> drflac_seek_to_pcm_frame() + drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32() + drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16() + drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32() + drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32() + drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16() + drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32() + drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32() + drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16() + drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32() + +Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate +to the old per-sample APIs. You now need to use the "pcm_frame" versions. +*/ + + +/* +Introduction +============ +dr_flac is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_FLAC_IMPLEMENTATION + #include "dr_flac.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following: + + ```c + drflac* pFlac = drflac_open_file("MySong.flac", NULL); + if (pFlac == NULL) { + // Failed to open FLAC file + } + + drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32)); + drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, pSamples); + ``` + +The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of channels and the bits per sample, +should be directly accessible - just make sure you don't change their values. Samples are always output as interleaved signed 32-bit PCM. In the example above +a native FLAC stream was opened, however dr_flac has seamless support for Ogg encapsulated FLAC streams as well. + +You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and the decoder will give you as many +samples as it can, up to the amount requested. Later on when you need the next batch of samples, just call it again. Example: + + ```c + while (drflac_read_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, pChunkSamples) > 0) { + do_something(); + } + ``` + +You can seek to a specific PCM frame with `drflac_seek_to_pcm_frame()`. + +If you just want to quickly decode an entire FLAC file in one go you can do something like this: + + ```c + unsigned int channels; + unsigned int sampleRate; + drflac_uint64 totalPCMFrameCount; + drflac_int32* pSampleData = drflac_open_file_and_read_pcm_frames_s32("MySong.flac", &channels, &sampleRate, &totalPCMFrameCount, NULL); + if (pSampleData == NULL) { + // Failed to open and decode FLAC file. + } + + ... + + drflac_free(pSampleData, NULL); + ``` + +You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs respectively, but note that these +should be considered lossy. + + +If you need access to metadata (album art, etc.), use `drflac_open_with_metadata()`, `drflac_open_file_with_metdata()` or `drflac_open_memory_with_metadata()`. +The rationale for keeping these APIs separate is that they're slightly slower than the normal versions and also just a little bit harder to use. dr_flac +reports metadata to the application through the use of a callback, and every metadata block is reported before `drflac_open_with_metdata()` returns. + +The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style +streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: + + `drflac_open_relaxed()` + `drflac_open_with_metadata_relaxed()` + +It is not recommended to use these APIs for file based streams because a missing header would usually indicate a corrupt or perverse file. In addition, these +APIs can take a long time to initialize because they may need to spend a lot of time finding the first frame. + + + +Build Options +============= +#define these options before including this file. + +#define DR_FLAC_NO_STDIO + Disable `drflac_open_file()` and family. + +#define DR_FLAC_NO_OGG + Disables support for Ogg/FLAC streams. + +#define DR_FLAC_BUFFER_SIZE <number> + Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls back to the client for more data. + Larger values means more memory, but better performance. My tests show diminishing returns after about 4KB (which is the default). Consider reducing this if + you have a very efficient implementation of onRead(), or increase it if it's very inefficient. Must be a multiple of 8. + +#define DR_FLAC_NO_CRC + Disables CRC checks. This will offer a performance boost when CRC is unnecessary. This will disable binary search seeking. When seeking, the seek table will + be used if available. Otherwise the seek will be performed using brute force. + +#define DR_FLAC_NO_SIMD + Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM architectures). Use this if you are having compatibility issues with your compiler. + +#define DR_FLAC_NO_WCHAR + Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_FLAC_NO_STDIO is also defined. + + + +Notes +===== +- dr_flac does not support changing the sample rate nor channel count mid stream. +- dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. +- When using Ogg encapsulation, a corrupted metadata block will result in `drflac_open_with_metadata()` and `drflac_open()` returning inconsistent samples due + to differences in corrupted stream recorvery logic between the two APIs. +*/ + +#ifndef dr_flac_h +#define dr_flac_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRFLAC_STRINGIFY(x) #x +#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) + +#define DRFLAC_VERSION_MAJOR 0 +#define DRFLAC_VERSION_MINOR 12 +#define DRFLAC_VERSION_REVISION 44 +#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) + +#include <stddef.h> /* For size_t. */ + +/* Sized Types */ +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drflac_int64; + typedef unsigned __int64 drflac_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drflac_int64; + typedef unsigned long long drflac_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + typedef drflac_uint64 drflac_uintptr; +#else + typedef drflac_uint32 drflac_uintptr; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 +/* End Sized Types */ + +/* Decorations */ +#if !defined(DRFLAC_API) + #if defined(DRFLAC_DLL) + #if defined(_WIN32) + #define DRFLAC_DLL_IMPORT __declspec(dllimport) + #define DRFLAC_DLL_EXPORT __declspec(dllexport) + #define DRFLAC_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) + #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRFLAC_DLL_IMPORT + #define DRFLAC_DLL_EXPORT + #define DRFLAC_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) + #define DRFLAC_API DRFLAC_DLL_EXPORT + #else + #define DRFLAC_API DRFLAC_DLL_IMPORT + #endif + #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE + #else + #define DRFLAC_API extern + #define DRFLAC_PRIVATE static + #endif +#endif +/* End Decorations */ + +#if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */ + #define DRFLAC_DEPRECATED __declspec(deprecated) +#elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */ + #define DRFLAC_DEPRECATED __attribute__((deprecated)) +#elif defined(__has_feature) /* Clang */ + #if __has_feature(attribute_deprecated) + #define DRFLAC_DEPRECATED __attribute__((deprecated)) + #else + #define DRFLAC_DEPRECATED + #endif +#else + #define DRFLAC_DEPRECATED +#endif + +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); +DRFLAC_API const char* drflac_version_string(void); + +/* Allocation Callbacks */ +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drflac_allocation_callbacks; +/* End Allocation Callbacks */ + +/* +As data is read from the client it is placed into an internal buffer for fast access. This controls the size of that buffer. Larger values means more speed, +but also more memory. In my testing there is diminishing returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. +*/ +#ifndef DR_FLAC_BUFFER_SIZE +#define DR_FLAC_BUFFER_SIZE 4096 +#endif + + +/* Architecture Detection */ +#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) +#define DRFLAC_64BIT +#endif + +#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC)) + #define DRFLAC_X64 +#elif defined(__i386) || defined(_M_IX86) + #define DRFLAC_X86 +#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) + #define DRFLAC_ARM +#endif +/* End Architecture Detection */ + + +#ifdef DRFLAC_64BIT +typedef drflac_uint64 drflac_cache_t; +#else +typedef drflac_uint32 drflac_cache_t; +#endif + +/* The various metadata block types. */ +#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 + +/* The various picture types specified in the PICTURE block. */ +#define DRFLAC_PICTURE_TYPE_OTHER 0 +#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 +#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 +#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 +#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define DRFLAC_PICTURE_TYPE_MEDIA 6 +#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define DRFLAC_PICTURE_TYPE_ARTIST 8 +#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 +#define DRFLAC_PICTURE_TYPE_BAND 10 +#define DRFLAC_PICTURE_TYPE_COMPOSER 11 +#define DRFLAC_PICTURE_TYPE_LYRICIST 12 +#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 + +typedef enum +{ + drflac_container_native, + drflac_container_ogg, + drflac_container_unknown +} drflac_container; + +typedef enum +{ + drflac_seek_origin_start, + drflac_seek_origin_current +} drflac_seek_origin; + +/* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */ +typedef struct +{ + drflac_uint64 firstPCMFrame; + drflac_uint64 flacFrameOffset; /* The offset from the first byte of the header of the first frame. */ + drflac_uint16 pcmFrameCount; +} drflac_seekpoint; + +typedef struct +{ + drflac_uint16 minBlockSizeInPCMFrames; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint32 minFrameSizeInPCMFrames; + drflac_uint32 maxFrameSizeInPCMFrames; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalPCMFrameCount; + drflac_uint8 md5[16]; +} drflac_streaminfo; + +typedef struct +{ + /* + The metadata type. Use this to know how to interpret the data below. Will be set to one of the + DRFLAC_METADATA_BLOCK_TYPE_* tokens. + */ + drflac_uint32 type; + + /* + A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to + not modify the contents of this buffer. Use the structures below for more meaningful and structured + information about the metadata. It's possible for this to be null. + */ + const void* pRawData; + + /* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */ + drflac_uint32 rawDataSize; + + union + { + drflac_streaminfo streaminfo; + + struct + { + int unused; + } padding; + + struct + { + drflac_uint32 id; + const void* pData; + drflac_uint32 dataSize; + } application; + + struct + { + drflac_uint32 seekpointCount; + const drflac_seekpoint* pSeekpoints; + } seektable; + + struct + { + drflac_uint32 vendorLength; + const char* vendor; + drflac_uint32 commentCount; + const void* pComments; + } vorbis_comment; + + struct + { + char catalog[128]; + drflac_uint64 leadInSampleCount; + drflac_bool32 isCD; + drflac_uint8 trackCount; + const void* pTrackData; + } cuesheet; + + struct + { + drflac_uint32 type; + drflac_uint32 mimeLength; + const char* mime; + drflac_uint32 descriptionLength; + const char* description; + drflac_uint32 width; + drflac_uint32 height; + drflac_uint32 colorDepth; + drflac_uint32 indexColorCount; + drflac_uint32 pictureDataSize; + const drflac_uint8* pPictureData; + } picture; + } data; +} drflac_metadata; + + +/* +Callback for when data needs to be read from the client. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +pBufferOut (out) + The output buffer. + +bytesToRead (in) + The number of bytes to read. + + +Return Value +------------ +The number of bytes actually read. + + +Remarks +------- +A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until either the entire bytesToRead is filled or +you have reached the end of the stream. +*/ +typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* +Callback for when data needs to be seeked. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +offset (in) + The number of bytes to move, relative to the origin. Will never be negative. + +origin (in) + The origin of the seek - the current position or the start of the stream. + + +Return Value +------------ +Whether or not the seek was successful. + + +Remarks +------- +The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be +either drflac_seek_origin_start or drflac_seek_origin_current. + +When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected +and handled by returning DRFLAC_FALSE. +*/ +typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); + +/* +Callback for when a metadata block is read. + + +Parameters +---------- +pUserData (in) + The user data that was passed to drflac_open() and family. + +pMetadata (in) + A pointer to a structure containing the data of the metadata block. + + +Remarks +------- +Use pMetadata->type to determine which metadata block is being handled and how to read the data. This +will be set to one of the DRFLAC_METADATA_BLOCK_TYPE_* tokens. +*/ +typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); + + +/* Structure for internal use. Only used for decoders opened with drflac_open_memory. */ +typedef struct +{ + const drflac_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drflac__memory_stream; + +/* Structure for internal use. Used for bit streaming. */ +typedef struct +{ + /* The function to call when more data needs to be read. */ + drflac_read_proc onRead; + + /* The function to call when the current read position needs to be moved. */ + drflac_seek_proc onSeek; + + /* The user data to pass around to onRead and onSeek. */ + void* pUserData; + + + /* + The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the + stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether + or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). + */ + size_t unalignedByteCount; + + /* The content of the unaligned bytes. */ + drflac_cache_t unalignedCache; + + /* The index of the next valid cache line in the "L2" cache. */ + drflac_uint32 nextL2Line; + + /* The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. */ + drflac_uint32 consumedBits; + + /* + The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: + Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. + */ + drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; + drflac_cache_t cache; + + /* + CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this + is reset to 0 at the beginning of each frame. + */ + drflac_uint16 crc16; + drflac_cache_t crc16Cache; /* A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. */ + drflac_uint32 crc16CacheIgnoredBytes; /* The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. */ +} drflac_bs; + +typedef struct +{ + /* The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. */ + drflac_uint8 subframeType; + + /* The number of wasted bits per sample as specified by the sub-frame header. */ + drflac_uint8 wastedBitsPerSample; + + /* The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. */ + drflac_uint8 lpcOrder; + + /* A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. */ + drflac_int32* pSamplesS32; +} drflac_subframe; + +typedef struct +{ + /* + If the stream uses variable block sizes, this will be set to the index of the first PCM frame. If fixed block sizes are used, this will + always be set to 0. This is 64-bit because the decoded PCM frame number will be 36 bits. + */ + drflac_uint64 pcmFrameNumber; + + /* + If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. This + is 32-bit because in fixed block sizes, the maximum frame number will be 31 bits. + */ + drflac_uint32 flacFrameNumber; + + /* The sample rate of this frame. */ + drflac_uint32 sampleRate; + + /* The number of PCM frames in each sub-frame within this frame. */ + drflac_uint16 blockSizeInPCMFrames; + + /* + The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this + will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. + */ + drflac_uint8 channelAssignment; + + /* The number of bits per sample within this frame. */ + drflac_uint8 bitsPerSample; + + /* The frame's CRC. */ + drflac_uint8 crc8; +} drflac_frame_header; + +typedef struct +{ + /* The header. */ + drflac_frame_header header; + + /* + The number of PCM frames left to be read in this FLAC frame. This is initially set to the block size. As PCM frames are read, + this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + */ + drflac_uint32 pcmFramesRemaining; + + /* The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. */ + drflac_subframe subframes[8]; +} drflac_frame; + +typedef struct +{ + /* The function to call when a metadata block is read. */ + drflac_meta_proc onMeta; + + /* The user data posted to the metadata callback function. */ + void* pUserDataMD; + + /* Memory allocation callbacks. */ + drflac_allocation_callbacks allocationCallbacks; + + + /* The sample rate. Will be set to something like 44100. */ + drflac_uint32 sampleRate; + + /* + The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the + value specified in the STREAMINFO block. + */ + drflac_uint8 channels; + + /* The bits per sample. Will be set to something like 16, 24, etc. */ + drflac_uint8 bitsPerSample; + + /* The maximum block size, in samples. This number represents the number of samples in each channel (not combined). */ + drflac_uint16 maxBlockSizeInPCMFrames; + + /* + The total number of PCM Frames making up the stream. Can be 0 in which case it's still a valid stream, but just means + the total PCM frame count is unknown. Likely the case with streams like internet radio. + */ + drflac_uint64 totalPCMFrameCount; + + + /* The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. */ + drflac_container container; + + /* The number of seekpoints in the seektable. */ + drflac_uint32 seekpointCount; + + + /* Information about the frame the decoder is currently sitting on. */ + drflac_frame currentFLACFrame; + + + /* The index of the PCM frame the decoder is currently sitting on. This is only used for seeking. */ + drflac_uint64 currentPCMFrame; + + /* The position of the first FLAC frame in the stream. This is only ever used for seeking. */ + drflac_uint64 firstFLACFramePosInBytes; + + + /* A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). */ + drflac__memory_stream memoryStream; + + + /* A pointer to the decoded sample data. This is an offset of pExtraData. */ + drflac_int32* pDecodedSamples; + + /* A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. */ + drflac_seekpoint* pSeekpoints; + + /* Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. */ + void* _oggbs; + + /* Internal use only. Used for profiling and testing different seeking modes. */ + drflac_bool32 _noSeekTableSeek : 1; + drflac_bool32 _noBinarySearchSeek : 1; + drflac_bool32 _noBruteForceSeek : 1; + + /* The bit streamer. The raw FLAC data is fed through this object. */ + drflac_bs bs; + + /* Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. */ + drflac_uint8 pExtraData[1]; +} drflac; + + +/* +Opens a FLAC decoder. + + +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. + +onSeek (in) + The function to call when the read position of the client data needs to move. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead and onSeek. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +Returns a pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with `drflac_close()`. + +`pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. + +This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated FLAC, both of which should work seamlessly +without any manual intervention. Ogg encapsulation also works with multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. + +This is the lowest level function for opening a FLAC stream. You can also use `drflac_open_file()` and `drflac_open_memory()` to open the stream from a file or +from a block of memory respectively. + +The STREAMINFO block must be present for this to succeed. Use `drflac_open_relaxed()` to open a FLAC stream where the header may not be present. + +Use `drflac_open_with_metadata()` if you need access to metadata. + + +Seek Also +--------- +drflac_open_file() +drflac_open_memory() +drflac_open_with_metadata() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC stream with relaxed validation of the header block. + + +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. + +onSeek (in) + The function to call when the read position of the client data needs to move. + +container (in) + Whether or not the FLAC stream is encapsulated using standard FLAC encapsulation or Ogg encapsulation. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead and onSeek. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +The same as drflac_open(), except attempts to open the stream even when a header block is not present. + +Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do not set this to `drflac_container_unknown` +as that is for internal use only. + +Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never found it will continue forever. To abort, +force your `onRead` callback to return 0, which dr_flac will use as an indicator that the end of the stream was found. + +Use `drflac_open_with_metadata_relaxed()` if you need access to metadata. +*/ +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). + + +Parameters +---------- +onRead (in) + The function to call when data needs to be read from the client. + +onSeek (in) + The function to call when the read position of the client data needs to move. + +onMeta (in) + The function to call for every metadata block. + +pUserData (in, optional) + A pointer to application defined data that will be passed to onRead, onSeek and onMeta. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with `drflac_close()`. + +`pAllocationCallbacks` can be NULL in which case it will use `DRFLAC_MALLOC`, `DRFLAC_REALLOC` and `DRFLAC_FREE`. + +This is slower than `drflac_open()`, so avoid this one if you don't need metadata. Internally, this will allocate and free memory on the heap for every +metadata block except for STREAMINFO and PADDING blocks. + +The caller is notified of the metadata via the `onMeta` callback. All metadata blocks will be handled before the function returns. This callback takes a +pointer to a `drflac_metadata` object which is a union containing the data of all relevant metadata blocks. Use the `type` member to discriminate against +the different metadata types. + +The STREAMINFO block must be present for this to succeed. Use `drflac_open_with_metadata_relaxed()` to open a FLAC stream where the header may not be present. + +Note that this will behave inconsistently with `drflac_open()` if the stream is an Ogg encapsulated stream and a metadata block is corrupted. This is due to +the way the Ogg stream recovers from corrupted pages. When `drflac_open_with_metadata()` is being used, the open routine will try to read the contents of the +metadata block, whereas `drflac_open()` will simply seek past it (for the sake of efficiency). This inconsistency can result in different samples being +returned depending on whether or not the stream is being opened with metadata. + + +Seek Also +--------- +drflac_open_file_with_metadata() +drflac_open_memory_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. + +See Also +-------- +drflac_open_with_metadata() +drflac_open_relaxed() +*/ +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Closes the given FLAC decoder. + + +Parameters +---------- +pFlac (in) + The decoder to close. + + +Remarks +------- +This will destroy the decoder object. + + +See Also +-------- +drflac_open() +drflac_open_with_metadata() +drflac_open_file() +drflac_open_file_w() +drflac_open_file_with_metadata() +drflac_open_file_with_metadata_w() +drflac_open_memory() +drflac_open_memory_with_metadata() +*/ +DRFLAC_API void drflac_close(drflac* pFlac); + + +/* +Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. +*/ +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); + + +/* +Reads sample data from the given FLAC decoder, output as interleaved signed 16-bit PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. + +Note that this is lossy for streams where the bits per sample is larger than 16. +*/ +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); + +/* +Reads sample data from the given FLAC decoder, output as interleaved 32-bit floating point PCM. + + +Parameters +---------- +pFlac (in) + The decoder. + +framesToRead (in) + The number of PCM frames to read. + +pBufferOut (out, optional) + A pointer to the buffer that will receive the decoded samples. + + +Return Value +------------ +Returns the number of PCM frames actually read. If the return value is less than `framesToRead` it has reached the end. + + +Remarks +------- +pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames seeked. + +Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly represent every possible number. +*/ +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); + +/* +Seeks to the PCM frame at the given index. + + +Parameters +---------- +pFlac (in) + The decoder. + +pcmFrameIndex (in) + The index of the PCM frame to seek to. See notes below. + + +Return Value +------------- +`DRFLAC_TRUE` if successful; `DRFLAC_FALSE` otherwise. +*/ +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); + + + +#ifndef DR_FLAC_NO_STDIO +/* +Opens a FLAC decoder from the file at the given path. + + +Parameters +---------- +pFileName (in) + The path of the file to open, either absolute or relative to the current directory. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +Close the decoder with drflac_close(). + + +Remarks +------- +This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the number of files a process can have open +at any given time, so keep this mind if you have many decoders open at the same time. + + +See Also +-------- +drflac_open_file_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) + + +Parameters +---------- +pFileName (in) + The path of the file to open, either absolute or relative to the current directory. + +pAllocationCallbacks (in, optional) + A pointer to application defined callbacks for managing memory allocations. + +onMeta (in) + The callback to fire for each metadata block. + +pUserData (in) + A pointer to the user data to pass to the metadata callback. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Remarks +------- +Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + + +See Also +-------- +drflac_open_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); +#endif + +/* +Opens a FLAC decoder from a pre-allocated block of memory + + +Parameters +---------- +pData (in) + A pointer to the raw encoded FLAC data. + +dataSize (in) + The size in bytes of `data`. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Return Value +------------ +A pointer to an object representing the decoder. + + +Remarks +------- +This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for the lifetime of the decoder. + + +See Also +-------- +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) + + +Parameters +---------- +pData (in) + A pointer to the raw encoded FLAC data. + +dataSize (in) + The size in bytes of `data`. + +onMeta (in) + The callback to fire for each metadata block. + +pUserData (in) + A pointer to the user data to pass to the metadata callback. + +pAllocationCallbacks (in) + A pointer to application defined callbacks for managing memory allocations. + + +Remarks +------- +Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. + + +See Also +------- +drflac_open_with_metadata() +drflac_open() +drflac_close() +*/ +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + + + +/* High Level APIs */ + +/* +Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a +pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free(). + +You can pass in custom memory allocation callbacks via the pAllocationCallbacks parameter. This can be NULL in which +case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. + +Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously +read samples into a dynamically sized buffer on the heap until no samples are left. + +Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). +*/ +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +#ifndef DR_FLAC_NO_STDIO +/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */ +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +#endif + +/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */ +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */ +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */ +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); + +/* +Frees memory that was allocated internally by dr_flac. + +Set pAllocationCallbacks to the same object that was passed to drflac_open_*_and_read_pcm_frames_*(). If you originally passed in NULL, pass in NULL for this. +*/ +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); + + +/* Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. */ +typedef struct +{ + drflac_uint32 countRemaining; + const char* pRunningData; +} drflac_vorbis_comment_iterator; + +/* +Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT +metadata block. +*/ +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); + +/* +Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The +returned string is NOT null terminated. +*/ +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); + + +/* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */ +typedef struct +{ + drflac_uint32 countRemaining; + const char* pRunningData; +} drflac_cuesheet_track_iterator; + +/* The order of members here is important because we map this directly to the raw data within the CUESHEET metadata block. */ +typedef struct +{ + drflac_uint64 offset; + drflac_uint8 index; + drflac_uint8 reserved[3]; +} drflac_cuesheet_track_index; + +typedef struct +{ + drflac_uint64 offset; + drflac_uint8 trackNumber; + char ISRC[12]; + drflac_bool8 isAudio; + drflac_bool8 preEmphasis; + drflac_uint8 indexCount; + const drflac_cuesheet_track_index* pIndexPoints; +} drflac_cuesheet_track; + +/* +Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata +block. +*/ +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); + +/* Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments. */ +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); + + +#ifdef __cplusplus +} +#endif +#endif /* dr_flac_h */ + + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) +#ifndef dr_flac_c +#define dr_flac_c + +/* Disable some annoying warnings. */ +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif + +#ifdef __linux__ + #ifndef _BSD_SOURCE + #define _BSD_SOURCE + #endif + #ifndef _DEFAULT_SOURCE + #define _DEFAULT_SOURCE + #endif + #ifndef __USE_BSD + #define __USE_BSD + #endif + #include <endian.h> +#endif + +#include <stdlib.h> +#include <string.h> + +/* Inline */ +#ifdef _MSC_VER + #define DRFLAC_INLINE __forceinline +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define DRFLAC_GNUC_INLINE_HINT __inline__ + #else + #define DRFLAC_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT + #endif +#elif defined(__WATCOMC__) + #define DRFLAC_INLINE __inline +#else + #define DRFLAC_INLINE +#endif +/* End Inline */ + +/* +Intrinsics Support + +There's a bug in GCC 4.2.x which results in an incorrect compilation error when using _mm_slli_epi32() where it complains with + + "error: shift must be an immediate" + +Unfortuantely dr_flac depends on this for a few things so we're just going to disable SSE on GCC 4.2 and below. +*/ +#if !defined(DR_FLAC_NO_SIMD) + #if defined(DRFLAC_X64) || defined(DRFLAC_X86) + #if defined(_MSC_VER) && !defined(__clang__) + /* MSVC. */ + #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) /* 2005 */ + #define DRFLAC_SUPPORT_SSE2 + #endif + #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) /* 2010 */ + #define DRFLAC_SUPPORT_SSE41 + #endif + #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) + /* Assume GNUC-style. */ + #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) + #define DRFLAC_SUPPORT_SSE2 + #endif + #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) + #define DRFLAC_SUPPORT_SSE41 + #endif + #endif + + /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>) + #define DRFLAC_SUPPORT_SSE2 + #endif + #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>) + #define DRFLAC_SUPPORT_SSE41 + #endif + #endif + + #if defined(DRFLAC_SUPPORT_SSE41) + #include <smmintrin.h> + #elif defined(DRFLAC_SUPPORT_SSE2) + #include <emmintrin.h> + #endif + #endif + + #if defined(DRFLAC_ARM) + #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define DRFLAC_SUPPORT_NEON + #include <arm_neon.h> + #endif + #endif +#endif + +/* Compile-time CPU feature support. */ +#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include <intrin.h> + static void drflac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define DRFLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void drflac__cpuid(int info[4], int fid) + { + /* + It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the + specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for + supporting different assembly dialects. + + What's basically happening is that we're saving and restoring the ebx register manually. + */ + #if defined(DRFLAC_X86) && defined(__PIC__) + __asm__ __volatile__ ( + "xchg{l} {%%}ebx, %k1;" + "cpuid;" + "xchg{l} {%%}ebx, %k1;" + : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #else + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + #endif + } + #else + #define DRFLAC_NO_CPUID + #endif + #endif +#else + #define DRFLAC_NO_CPUID +#endif + +static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) + #if defined(DRFLAC_X64) + return DRFLAC_TRUE; /* 64-bit targets always support SSE2. */ + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ + #else + #if defined(DRFLAC_NO_CPUID) + return DRFLAC_FALSE; + #else + int info[4]; + drflac__cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; + #endif + #endif + #else + return DRFLAC_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ + #endif +#else + return DRFLAC_FALSE; /* No compiler support. */ +#endif +} + +static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) +{ +#if defined(DRFLAC_SUPPORT_SSE41) + #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) + #if defined(__SSE4_1__) || defined(__AVX__) + return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */ + #else + #if defined(DRFLAC_NO_CPUID) + return DRFLAC_FALSE; + #else + int info[4]; + drflac__cpuid(info, 1); + return (info[2] & (1 << 19)) != 0; + #endif + #endif + #else + return DRFLAC_FALSE; /* SSE41 is only supported on x86 and x64 architectures. */ + #endif +#else + return DRFLAC_FALSE; /* No compiler support. */ +#endif +} + + +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) + #define DRFLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #define DRFLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define DRFLAC_HAS_LZCNT_INTRINSIC + #endif + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #endif +#elif defined(__WATCOMC__) && defined(__386__) + #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); + extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); + extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); +#pragma aux _watcom_bswap16 = \ + "xchg al, ah" \ + parm [ax] \ + value [ax] \ + modify nomemory; +#pragma aux _watcom_bswap32 = \ + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#pragma aux _watcom_bswap64 = \ + "bswap eax" \ + "bswap edx" \ + "xchg eax,edx" \ + parm [eax edx] \ + value [eax edx] \ + modify nomemory; +#endif + + +/* Standard library stuff. */ +#ifndef DRFLAC_ASSERT +#include <assert.h> +#define DRFLAC_ASSERT(expression) assert(expression) +#endif +#ifndef DRFLAC_MALLOC +#define DRFLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRFLAC_REALLOC +#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRFLAC_FREE +#define DRFLAC_FREE(p) free((p)) +#endif +#ifndef DRFLAC_COPY_MEMORY +#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRFLAC_ZERO_MEMORY +#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef DRFLAC_ZERO_OBJECT +#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) +#endif + +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */ + +/* Result Codes */ +typedef drflac_int32 drflac_result; +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 /* A generic error. */ +#define DRFLAC_INVALID_ARGS -2 +#define DRFLAC_INVALID_OPERATION -3 +#define DRFLAC_OUT_OF_MEMORY -4 +#define DRFLAC_OUT_OF_RANGE -5 +#define DRFLAC_ACCESS_DENIED -6 +#define DRFLAC_DOES_NOT_EXIST -7 +#define DRFLAC_ALREADY_EXISTS -8 +#define DRFLAC_TOO_MANY_OPEN_FILES -9 +#define DRFLAC_INVALID_FILE -10 +#define DRFLAC_TOO_BIG -11 +#define DRFLAC_PATH_TOO_LONG -12 +#define DRFLAC_NAME_TOO_LONG -13 +#define DRFLAC_NOT_DIRECTORY -14 +#define DRFLAC_IS_DIRECTORY -15 +#define DRFLAC_DIRECTORY_NOT_EMPTY -16 +#define DRFLAC_END_OF_FILE -17 +#define DRFLAC_NO_SPACE -18 +#define DRFLAC_BUSY -19 +#define DRFLAC_IO_ERROR -20 +#define DRFLAC_INTERRUPT -21 +#define DRFLAC_UNAVAILABLE -22 +#define DRFLAC_ALREADY_IN_USE -23 +#define DRFLAC_BAD_ADDRESS -24 +#define DRFLAC_BAD_SEEK -25 +#define DRFLAC_BAD_PIPE -26 +#define DRFLAC_DEADLOCK -27 +#define DRFLAC_TOO_MANY_LINKS -28 +#define DRFLAC_NOT_IMPLEMENTED -29 +#define DRFLAC_NO_MESSAGE -30 +#define DRFLAC_BAD_MESSAGE -31 +#define DRFLAC_NO_DATA_AVAILABLE -32 +#define DRFLAC_INVALID_DATA -33 +#define DRFLAC_TIMEOUT -34 +#define DRFLAC_NO_NETWORK -35 +#define DRFLAC_NOT_UNIQUE -36 +#define DRFLAC_NOT_SOCKET -37 +#define DRFLAC_NO_ADDRESS -38 +#define DRFLAC_BAD_PROTOCOL -39 +#define DRFLAC_PROTOCOL_UNAVAILABLE -40 +#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 +#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRFLAC_SOCKET_NOT_SUPPORTED -44 +#define DRFLAC_CONNECTION_RESET -45 +#define DRFLAC_ALREADY_CONNECTED -46 +#define DRFLAC_NOT_CONNECTED -47 +#define DRFLAC_CONNECTION_REFUSED -48 +#define DRFLAC_NO_HOST -49 +#define DRFLAC_IN_PROGRESS -50 +#define DRFLAC_CANCELLED -51 +#define DRFLAC_MEMORY_ALREADY_MAPPED -52 +#define DRFLAC_AT_END -53 + +#define DRFLAC_CRC_MISMATCH -100 +/* End Result Codes */ + + +#define DRFLAC_SUBFRAME_CONSTANT 0 +#define DRFLAC_SUBFRAME_VERBATIM 1 +#define DRFLAC_SUBFRAME_FIXED 8 +#define DRFLAC_SUBFRAME_LPC 32 +#define DRFLAC_SUBFRAME_RESERVED 255 + +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 + +#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 + +#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 + +#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) + + +DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRFLAC_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRFLAC_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRFLAC_VERSION_REVISION; + } +} + +DRFLAC_API const char* drflac_version_string(void) +{ + return DRFLAC_VERSION_STRING; +} + + +/* CPU caps. */ +#if defined(__has_feature) + #if __has_feature(thread_sanitizer) + #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #else + #define DRFLAC_NO_THREAD_SANITIZE + #endif +#else + #define DRFLAC_NO_THREAD_SANITIZE +#endif + +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#endif + +#ifndef DRFLAC_NO_CPUID +static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; +static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; + +/* +I've had a bug report that Clang's ThreadSanitizer presents a warning in this function. Having reviewed this, this does +actually make sense. However, since CPU caps should never differ for a running process, I don't think the trade off of +complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore +just going to disable these warnings. This is disabled via the DRFLAC_NO_THREAD_SANITIZE attribute. +*/ +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +{ + static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; + + if (!isCPUCapsInitialized) { + /* LZCNT */ +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) + int info[4] = {0}; + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; +#endif + + /* SSE2 */ + drflac__gIsSSE2Supported = drflac_has_sse2(); + + /* SSE4.1 */ + drflac__gIsSSE41Supported = drflac_has_sse41(); + + /* Initialized. */ + isCPUCapsInitialized = DRFLAC_TRUE; + } +} +#else +static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; + +static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) +{ +#if defined(DRFLAC_SUPPORT_NEON) + #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return DRFLAC_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ + #else + /* TODO: Runtime check. */ + return DRFLAC_FALSE; + #endif + #else + return DRFLAC_FALSE; /* NEON is only supported on ARM architectures. */ + #endif +#else + return DRFLAC_FALSE; /* No compiler support. */ +#endif +} + +DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +{ + drflac__gIsNEONSupported = drflac__has_neon(); + +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + drflac__gIsLZCNTSupported = DRFLAC_TRUE; +#endif +} +#endif + + +/* Endian Management */ +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) +{ +#if defined(DRFLAC_X86) || defined(DRFLAC_X64) + return DRFLAC_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return DRFLAC_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ + /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ + drflac_uint32 r; + __asm__ __volatile__ ( + #if defined(DRFLAC_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ + return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | + ((n & ((drflac_uint64)0xFF000000 )) << 8) | + ((n & ((drflac_uint64)0x00FF0000 )) << 24) | + ((n & ((drflac_uint64)0x0000FF00 )) << 40) | + ((n & ((drflac_uint64)0x000000FF )) << 56); +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) +{ + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint16(n); + } + + return n; +} + +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) +{ + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +} + +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); +} + +static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) +{ + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint64(n); + } + + return n; +} + + +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) +{ + if (!drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +} + +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) +{ + const drflac_uint8* pNum = (drflac_uint8*)pData; + return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; +} + + +static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +{ + drflac_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + + return result; +} + + + +/* The CRC code below is based on this document: http://zlib.net/crc_v3.txt */ +static drflac_uint8 drflac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static drflac_uint16 drflac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +{ + return drflac__crc8_table[crc ^ data]; +} + +static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +{ +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") */ + drflac_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + drflac_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + + DRFLAC_ASSERT(count <= 32); + + wholeBytes = count >> 3; + leftoverBits = count - (wholeBytes*8); + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +{ + return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) +{ +#ifdef DRFLAC_64BIT + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + + return crc; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef DRFLAC_64BIT + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + } + + return crc; +} + +#if 0 +static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +{ +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + /* REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") */ + drflac_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + drflac_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + + return crc; +#else + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + + DRFLAC_ASSERT(count <= 64); + + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +{ +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + drflac_uint32 wholeBytes; + drflac_uint32 leftoverBits; + drflac_uint64 leftoverDataMask; + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + + DRFLAC_ASSERT(count <= 64); + + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */ + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +{ +#ifdef DRFLAC_64BIT + return drflac_crc16__64bit(crc, data, count); +#else + return drflac_crc16__32bit(crc, data, count); +#endif +} +#endif + + +#ifdef DRFLAC_64BIT +#define drflac__be2host__cache_line drflac__be2host_64 +#else +#define drflac__be2host__cache_line drflac__be2host_32 +#endif + +/* +BIT READING ATTEMPT #2 + +This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting +on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache +is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an +array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data +from onRead() is read into. +*/ +#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) +#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) + + +#ifndef DR_FLAC_NO_CRC +static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} + +static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +{ + if (bs->crc16CacheIgnoredBytes == 0) { + bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); + } else { + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; + } +} + +static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +{ + /* We should never be flushing in a situation where we are not aligned on a byte boundary. */ + DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + + /* + The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined + by the number of bits that have been consumed. + */ + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + drflac__update_crc16(bs); + } else { + /* We only accumulate the consumed bits. */ + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + + /* + The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated + so we can handle that later. + */ + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + + return bs->crc16; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +{ + size_t bytesRead; + size_t alignedL1LineCount; + + /* Fast path. Try loading straight from L2. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + /* + If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's + any left. + */ + if (bs->unalignedByteCount > 0) { + return DRFLAC_FALSE; /* If we have any unaligned bytes it means there's no more aligned bytes left in the client. */ + } + + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + + bs->nextL2Line = 0; + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + + /* + If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably + means we've just reached the end of the file. We need to move the valid data down to the end of the buffer + and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to + the size of the L1 so we'll need to seek backwards by any misaligned bytes. + */ + alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + + /* We need to keep track of any unaligned bytes for later use. */ + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + + if (alignedL1LineCount > 0) { + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t i; + for (i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + + bs->nextL2Line = (drflac_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } else { + /* If we get into this branch it means we weren't able to load any L1-aligned data. */ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + return DRFLAC_FALSE; + } +} + +static drflac_bool32 drflac__reload_cache(drflac_bs* bs) +{ + size_t bytesRead; + +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + + /* Fast path. Try just moving the next value in the L2 cache to the L1 cache. */ + if (drflac__reload_l1_cache_from_l2(bs)) { + bs->cache = drflac__be2host__cache_line(bs->cache); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return DRFLAC_TRUE; + } + + /* Slow path. */ + + /* + If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last + few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the + data from the unaligned cache. + */ + bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- The stream has been exhausted, so marked the bits as consumed. */ + return DRFLAC_FALSE; + } + + DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + + bs->cache = drflac__be2host__cache_line(bs->unalignedCache); + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); /* <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. */ + bs->unalignedByteCount = 0; /* <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. */ + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return DRFLAC_TRUE; +} + +static void drflac__reset_cache(drflac_bs* bs) +{ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); /* <-- This clears the L2 cache. */ + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- This clears the L1 cache. */ + bs->cache = 0; + bs->unalignedByteCount = 0; /* <-- This clears the trailing unaligned bytes. */ + bs->unalignedCache = 0; + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif +} + + +static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResultOut != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 32); + + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* + If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do + a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly + more optimal solution for this. + */ +#ifdef DRFLAC_64BIT + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; +#else + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + /* Cannot shift by 32-bits, so need to do it differently. */ + *pResultOut = (drflac_uint32)bs->cache; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } +#endif + + return DRFLAC_TRUE; + } else { + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ + drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + drflac_uint32 bitCountLo = bitCount - bitCountHi; + drflac_uint32 resultHi; + + DRFLAC_ASSERT(bitCountHi > 0); + DRFLAC_ASSERT(bitCountHi < 32); + resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) +{ + drflac_uint32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 32); + + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + /* Do not attempt to shift by 32 as it's undefined. */ + if (bitCount < 32) { + drflac_uint32 signbit; + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + } + + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; +} + +#ifdef DRFLAC_64BIT +static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) +{ + drflac_uint32 resultHi; + drflac_uint32 resultLo; + + DRFLAC_ASSERT(bitCount <= 64); + DRFLAC_ASSERT(bitCount > 32); + + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_uint32(bs, 32, &resultLo)) { + return DRFLAC_FALSE; + } + + *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); + return DRFLAC_TRUE; +} +#endif + +/* Function below is unused, but leaving it here in case I need to quickly add it again. */ +#if 0 +static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) +{ + drflac_uint64 result; + drflac_uint64 signbit; + + DRFLAC_ASSERT(bitCount <= 64); + + if (!drflac__read_uint64(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResultOut = (drflac_int64)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) +{ + drflac_uint32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 16); + + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint16)result; + return DRFLAC_TRUE; +} + +#if 0 +static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +{ + drflac_int32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 16); + + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int16)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) +{ + drflac_uint32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 8); + + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint8)result; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) +{ + drflac_int32 result; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pResult != NULL); + DRFLAC_ASSERT(bitCount > 0); + DRFLAC_ASSERT(bitCount <= 8); + + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int8)result; + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (drflac_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return DRFLAC_TRUE; + } else { + /* It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. */ + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; + + /* Simple case. Seek in groups of the same number as bits that fit within a cache line. */ +#ifdef DRFLAC_64BIT + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + + /* Whole leftover bytes. */ + while (bitsToSeek >= 8) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= 8; + } + + /* Leftover bits. */ + if (bitsToSeek > 0) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek = 0; /* <-- Necessary for the assert below. */ + } + + DRFLAC_ASSERT(bitsToSeek == 0); + return DRFLAC_TRUE; + } +} + + +/* This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. */ +static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +{ + DRFLAC_ASSERT(bs != NULL); + + /* + The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first + thing to do is align to the next byte. + */ + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + + for (;;) { + drflac_uint8 hi; + +#ifndef DR_FLAC_NO_CRC + drflac__reset_crc16(bs); +#endif + + if (!drflac__read_uint8(bs, 8, &hi)) { + return DRFLAC_FALSE; + } + + if (hi == 0xFF) { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) { + return DRFLAC_FALSE; + } + + if (lo == 0x3E) { + return DRFLAC_TRUE; + } else { + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + } + } + } + + /* Should never get here. */ + /*return DRFLAC_FALSE;*/ +} + + +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) +#define DRFLAC_IMPLEMENT_CLZ_MSVC +#endif +#if defined(__WATCOMC__) && defined(__386__) +#define DRFLAC_IMPLEMENT_CLZ_WATCOM +#endif +#ifdef __MRC__ +#include <intrinsics.h> +#define DRFLAC_IMPLEMENT_CLZ_MRC +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +{ + drflac_uint32 n; + static drflac_uint32 clz_table_4[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + if (x == 0) { + return sizeof(x)*8; + } + + n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef DRFLAC_64BIT + if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } + + return n - 1; +} + +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) +{ + /* Fast compile time check for ARM. */ +#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return DRFLAC_TRUE; +#elif defined(__MRC__) + return DRFLAC_TRUE; +#else + /* If the compiler itself does not support the intrinsic then we'll need to return false. */ + #ifdef DRFLAC_HAS_LZCNT_INTRINSIC + return drflac__gIsLZCNTSupported; + #else + return DRFLAC_FALSE; + #endif +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +{ + /* + It's critical for competitive decoding performance that this function be highly optimal. With MSVC we can use the __lzcnt64() and __lzcnt() intrinsics + to achieve good performance, however on GCC and Clang it's a little bit more annoying. The __builtin_clzl() and __builtin_clzll() intrinsics leave + it undefined as to the return value when `x` is 0. We need this to be well defined as returning 32 or 64, depending on whether or not it's a 32- or + 64-bit build. To work around this we would need to add a conditional to check for the x = 0 case, but this creates unnecessary inefficiency. To work + around this problem I have written some inline assembly to emit the LZCNT (x86) or CLZ (ARM) instruction directly which removes the need to include + the conditional. This has worked well in the past, but for some reason Clang's MSVC compatible driver, clang-cl, does not seem to be handling this + in the same way as the normal Clang driver. It seems that `clang-cl` is just outputting the wrong results sometimes, maybe due to some register + getting clobbered? + + I'm not sure if this is a bug with dr_flac's inlined assembly (most likely), a bug in `clang-cl` or just a misunderstanding on my part with inline + assembly rules for `clang-cl`. If somebody can identify an error in dr_flac's inlined assembly I'm happy to get that fixed. + + Fortunately there is an easy workaround for this. Clang implements MSVC-specific intrinsics for compatibility. It also defines _MSC_VER for extra + compatibility. We can therefore just check for _MSC_VER and use the MSVC intrinsic which, fortunately for us, Clang supports. It would still be nice + to know how to fix the inlined assembly for correctness sake, however. + */ + +#if defined(_MSC_VER) /*&& !defined(__clang__)*/ /* <-- Intentionally wanting Clang to use the MSVC __lzcnt64/__lzcnt intrinsics due to above ^. */ + #ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); + #else + return (drflac_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #if defined(DRFLAC_X64) + { + drflac_uint64 r; + __asm__ __volatile__ ( + "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + + return (drflac_uint32)r; + } + #elif defined(DRFLAC_X86) + { + drflac_uint32 r; + __asm__ __volatile__ ( + "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + + return r; + } + #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */ + { + unsigned int r; + __asm__ __volatile__ ( + #if defined(DRFLAC_64BIT) + "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) + #endif + ); + + return r; + } + #else + if (x == 0) { + return sizeof(x)*8; + } + #ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((drflac_uint64)x); + #else + return (drflac_uint32)__builtin_clzl((drflac_uint32)x); + #endif + #endif + #else + /* Unsupported compiler. */ + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +#include <intrin.h> /* For BitScanReverse(). */ + +static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +{ + drflac_uint32 n; + + if (x == 0) { + return sizeof(x)*8; + } + +#ifdef DRFLAC_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM +static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); +#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +/* Use the LZCNT instruction (only available on some processors since the 2010s). */ +#pragma aux drflac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" /* lzcnt eax, eax */ \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else +/* Use the 386+-compatible implementation. */ +#pragma aux drflac__clz_watcom = \ + "bsr eax, eax" \ + "xor eax, 31" \ + parm [eax] nomemory \ + value [eax] \ + modify exact [eax] nomemory; +#endif +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +{ +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT + if (drflac__is_lzcnt_supported()) { + return drflac__clz_lzcnt(x); + } else +#endif + { +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return drflac__clz_watcom_lzcnt(x); +#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); +#else + return drflac__clz_software(x); +#endif + } +} + + +static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +{ + drflac_uint32 zeroCounter = 0; + drflac_uint32 setBitOffsetPlus1; + + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + if (bs->cache == 1) { + /* Not catching this would lead to undefined behaviour: a shift of a 32-bit number by 32 or more is undefined */ + *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; + } + + setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 += 1; + + if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return DRFLAC_TRUE; +} + + + +static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(offsetFromStart > 0); + + /* + Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which + is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. + To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. + */ + if (offsetFromStart > 0x7FFFFFFF) { + drflac_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + } + + /* The cache should be reset to force a reload of fresh data from the client. */ + drflac__reset_cache(bs); + return DRFLAC_TRUE; +} + + +static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) +{ + drflac_uint8 crc; + drflac_uint64 result; + drflac_uint8 utf8[7] = {0}; + int byteCount; + int i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pNumberOut != NULL); + DRFLAC_ASSERT(pCRCOut != NULL); + + crc = *pCRCOut; + + if (!drflac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return DRFLAC_AT_END; + } + crc = drflac_crc8(crc, utf8[0], 8); + + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return DRFLAC_SUCCESS; + } + + /*byteCount = 1;*/ + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return DRFLAC_CRC_MISMATCH; /* Bad UTF-8 encoding. */ + } + + /* Read extra bytes. */ + DRFLAC_ASSERT(byteCount > 1); + + result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (i = 1; i < byteCount; ++i) { + if (!drflac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return DRFLAC_AT_END; + } + crc = drflac_crc8(crc, utf8[i], 8); + + result = (result << 6) | (utf8[i] & 0x3F); + } + + *pNumberOut = result; + *pCRCOut = crc; + return DRFLAC_SUCCESS; +} + + +static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) +{ +#if 1 /* Needs optimizing. */ + drflac_uint32 result = 0; + while (x > 0) { + result += 1; + x >>= 1; + } + + return result; +#endif +} + +static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) +{ + /* https://web.archive.org/web/20220205005724/https://github.com/ietf-wg-cellar/flac-specification/blob/37a49aa48ba4ba12e8757badfc59c0df35435fec/rfc_backmatter.md */ + return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; +} + + +/* +The next two functions are responsible for calculating the prediction. + +When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's +safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. +*/ +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_int32 prediction = 0; + + DRFLAC_ASSERT(order <= 32); + + /* 32-bit version. */ + + /* VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */ + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + + return (drflac_int32)(prediction >> shift); +} + +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_int64 prediction; + + DRFLAC_ASSERT(order <= 32); + + /* 64-bit version. */ + + /* This method is faster on the 32-bit build when compiling with VC++. See note below. */ +#ifndef DRFLAC_64BIT + if (order == 8) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + } + else + { + int j; + + prediction = 0; + for (j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; + } + } +#endif + + /* + VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some + reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. + */ +#ifdef DRFLAC_64BIT + prediction = 0; + switch (order) + { + case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + } +#endif + + return (drflac_int32)(prediction >> shift); +} + + +#if 0 +/* +Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the +sake of readability and should only be used as a reference. +*/ +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + for (i = 0; i < count; ++i) { + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + + + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} +#endif + +#if 0 +static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 zeroCounter = 0; + drflac_uint32 decodedRice; + + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return DRFLAC_TRUE; +} +#endif + +#if 0 +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_cache_t riceParamMask; + drflac_uint32 zeroCounter; + drflac_uint32 setBitOffsetPlus1; + drflac_uint32 riceParamPart; + drflac_uint32 riceLength; + + DRFLAC_ASSERT(riceParam > 0); /* <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case. */ + + riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + + zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + + riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + drflac_uint32 bitCountLo; + drflac_cache_t resultHi; + + bs->consumedBits += riceLength; + bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); /* <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }" */ + + /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ + bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); /* <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0. */ + + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + } + + riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + + pZeroCounterOut[0] = zeroCounter; + pRiceParamPartOut[0] = riceParamPart; + + return DRFLAC_TRUE; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 riceParamPlus1 = riceParam + 1; + /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/ + drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + + /* + The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have + no idea how this will work in practice... + */ + drflac_cache_t bs_cache = bs->cache; + drflac_uint32 bs_consumedBits = bs->consumedBits; + + /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ + drflac_uint32 lzcount = drflac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + pZeroCounterOut[0] = lzcount; + + /* + It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting + this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled + outside of this function at a higher level. + */ + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + /* Getting here means the rice parameter part is wholly contained within the current cache line. */ + pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + drflac_uint32 riceParamPartHi; + drflac_uint32 riceParamPartLo; + drflac_uint32 riceParamPartLoBitCount; + + /* + Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache + line, reload the cache, and then combine it with the head of the next cache line. + */ + + /* Grab the high part of the rice parameter part. */ + riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + + /* Before reloading the cache we need to grab the size in bits of the low part. */ + riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + + /* Now reload the cache. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + + /* We should now have enough information to construct the rice parameter part. */ + riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; + + bs_cache <<= riceParamPartLoBitCount; + } + } else { + /* + Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call + to drflac__clz() and we need to reload the cache. + */ + drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + for (;;) { + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + + lzcount = drflac__clz(bs_cache); + zeroCounter += lzcount; + + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + + pZeroCounterOut[0] = zeroCounter; + goto extract_rice_param_part; + } + + /* Make sure the cache is restored at the end of it all. */ + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + + return DRFLAC_TRUE; +} + +static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) +{ + drflac_uint32 riceParamPlus1 = riceParam + 1; + drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + + /* + The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have + no idea how this will work in practice... + */ + drflac_cache_t bs_cache = bs->cache; + drflac_uint32 bs_consumedBits = bs->consumedBits; + + /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */ + drflac_uint32 lzcount = drflac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + /* + It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting + this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled + outside of this function at a higher level. + */ + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + /* Getting here means the rice parameter part is wholly contained within the current cache line. */ + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + /* + Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache + line, reload the cache, and then combine it with the head of the next cache line. + */ + + /* Before reloading the cache we need to grab the size in bits of the low part. */ + drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + + /* Now reload the cache. */ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + /* This happens when we get to end of stream */ + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + + bs_cache <<= riceParamPartLoBitCount; + } + } else { + /* + Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call + to drflac__clz() and we need to reload the cache. + */ + for (;;) { + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + /* Slow path. We need to fetch more data from the client. */ + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + + lzcount = drflac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + + goto extract_rice_param_part; + } + + /* Make sure the cache is restored at the end of it all. */ + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + drflac_uint32 zeroCountPart0; + drflac_uint32 riceParamPart0; + drflac_uint32 riceParamMask; + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + (void)bitsPerSample; + (void)order; + (void)shift; + (void)coefficients; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + + i = 0; + while (i < count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + + pSamplesOut[i] = riceParamPart0; + + i += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + drflac_uint32 zeroCountPart0 = 0; + drflac_uint32 zeroCountPart1 = 0; + drflac_uint32 zeroCountPart2 = 0; + drflac_uint32 zeroCountPart3 = 0; + drflac_uint32 riceParamPart0 = 0; + drflac_uint32 riceParamPart1 = 0; + drflac_uint32 riceParamPart2 = 0; + drflac_uint32 riceParamPart3 = 0; + drflac_uint32 riceParamMask; + const drflac_int32* pSamplesOutEnd; + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + if (lpcOrder == 0) { + return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + pSamplesOutEnd = pSamplesOut + (count & ~3); + + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + while (pSamplesOut < pSamplesOutEnd) { + /* + Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version + against an array. Not sure why, but perhaps it's making more efficient use of registers? + */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + + pSamplesOut += 4; + } + } else { + while (pSamplesOut < pSamplesOutEnd) { + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + + pSamplesOut += 4; + } + } + + i = (count & ~3); + while (i < count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/ + + /* Sample reconstruction. */ + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } else { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } + + i += 1; + pSamplesOut += 1; + } + + return DRFLAC_TRUE; +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +{ + __m128i r; + + /* Pack. */ + r = _mm_packs_epi32(a, b); + + /* a3a2 a1a0 b3b2 b1b0 -> a3a2 b3b2 a1a0 b1b0 */ + r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); + + /* a3a2 b3b2 a1a0 b1b0 -> a3b3 a2b2 a1b1 a0b0 */ + r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + + return r; +} +#endif + +#if defined(DRFLAC_SUPPORT_SSE41) +static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) +{ + return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); +} + +static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) +{ + __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); + __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); + return _mm_add_epi32(x64, x32); +} + +static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) +{ + return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); +} + +static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) +{ + /* + To simplify this we are assuming count < 32. This restriction allows us to work on a low side and a high side. The low side + is shifted with zero bits, whereas the right side is shifted with sign bits. + */ + __m128i lo = _mm_srli_epi64(x, count); + __m128i hi = _mm_srai_epi32(x, count); + + hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); /* The high part needs to have the low part cleared. */ + + return _mm_or_si128(lo, hi); +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts0 = 0; + drflac_uint32 zeroCountParts1 = 0; + drflac_uint32 zeroCountParts2 = 0; + drflac_uint32 zeroCountParts3 = 0; + drflac_uint32 riceParamParts0 = 0; + drflac_uint32 riceParamParts1 = 0; + drflac_uint32 riceParamParts2 = 0; + drflac_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i riceParamMask128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + + /* Pre-load. */ + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); + + /* + Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than + what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results + in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted + so I think there's opportunity for this to be simplified. + */ +#if 1 + { + int runningOrder = order; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + /* This causes strict-aliasing warnings with GCC. */ + switch (order) + { + case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i prediction128; + __m128i zeroCountPart128; + __m128i riceParamPart128; + + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); /* <-- SSE2 compatible */ + /*riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01)), _mm_set1_epi32(0xFFFFFFFF)));*/ /* <-- Only supported from SSE4.1 and is slower in my testing... */ + + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } + + /* We store samples in groups of 4. */ + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts0 = 0; + drflac_uint32 zeroCountParts1 = 0; + drflac_uint32 zeroCountParts2 = 0; + drflac_uint32 zeroCountParts3 = 0; + drflac_uint32 riceParamParts0 = 0; + drflac_uint32 riceParamParts1 = 0; + drflac_uint32 riceParamParts2 = 0; + drflac_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i prediction128; + __m128i riceParamMask128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + DRFLAC_ASSERT(order <= 12); + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + + prediction128 = _mm_setzero_si128(); + + /* Pre-load. */ + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); + +#if 1 + { + int runningOrder = order; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + switch (order) + { + case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i zeroCountPart128; + __m128i riceParamPart128; + + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_xor_si128(prediction128, prediction128); /* Reset to 0. */ + + switch (order) + { + case 12: + case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); + case 10: + case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); + case 8: + case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); + case 6: + case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); + case 4: + case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); + case 2: + case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); + } + + /* Horizontal add and shift. */ + prediction128 = drflac__mm_hadd_epi64(prediction128); + prediction128 = drflac__mm_srai_epi64(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + + /* Our value should be sitting in prediction128[0]. We need to combine this with our SSE samples. */ + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + + /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + + /* We store samples in groups of 4. */ + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + /* In my testing the order is rarely > 12, so in this case I'm going to simplify the SSE implementation by only handling order <= 12. */ + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) +{ + vst1q_s32(p+0, x.val[0]); + vst1q_s32(p+4, x.val[1]); +} + +static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) +{ + vst1q_u32(p+0, x.val[0]); + vst1q_u32(p+4, x.val[1]); +} + +static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) +{ + vst1q_f32(p+0, x.val[0]); + vst1q_f32(p+4, x.val[1]); +} + +static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) +{ + vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); +} + +static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) +{ + vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); +} + +static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) +{ + drflac_int32 x[4]; + x[3] = x3; + x[2] = x2; + x[1] = x1; + x[0] = x0; + return vld1q_s32(x); +} + +static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) +{ + /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ + + /* Reference */ + /*return drflac__vdupq_n_s32x4( + vgetq_lane_s32(a, 0), + vgetq_lane_s32(b, 3), + vgetq_lane_s32(b, 2), + vgetq_lane_s32(b, 1) + );*/ + + return vextq_s32(b, a, 1); +} + +static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +{ + /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */ + + /* Reference */ + /*return drflac__vdupq_n_s32x4( + vgetq_lane_s32(a, 0), + vgetq_lane_s32(b, 3), + vgetq_lane_s32(b, 2), + vgetq_lane_s32(b, 1) + );*/ + + return vextq_u32(b, a, 1); +} + +static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) +{ + /* The sum must end up in position 0. */ + + /* Reference */ + /*return vdupq_n_s32( + vgetq_lane_s32(x, 3) + + vgetq_lane_s32(x, 2) + + vgetq_lane_s32(x, 1) + + vgetq_lane_s32(x, 0) + );*/ + + int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); + return vpadd_s32(r, r); +} + +static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) +{ + return vadd_s64(vget_high_s64(x), vget_low_s64(x)); +} + +static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) +{ + /* Reference */ + /*return drflac__vdupq_n_s32x4( + vgetq_lane_s32(x, 0), + vgetq_lane_s32(x, 1), + vgetq_lane_s32(x, 2), + vgetq_lane_s32(x, 3) + );*/ + + return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); +} + +static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) +{ + return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); +} + +static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) +{ + return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts[4]; + drflac_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int32x2_t shift64; + uint32x4_t one128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s32(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ + one128 = vdupq_n_u32(1); + + /* + Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than + what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results + in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted + so I think there's opportunity for this to be simplified. + */ + { + int runningOrder = order; + drflac_int32 tempC[4] = {0, 0, 0, 0}; + drflac_int32 tempS[4] = {0, 0, 0, 0}; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ + } + + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ + } + + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ + } + + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = drflac__vrevq_s32(coefficients128_0); + coefficients128_4 = drflac__vrevq_s32(coefficients128_4); + coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + } + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + int32x4_t prediction128; + int32x2_t prediction64; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_8, samples128_8); + prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + + samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } + + /* We store samples in groups of 4. */ + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + int i; + drflac_uint32 riceParamMask; + drflac_int32* pDecodedSamples = pSamplesOut; + drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + drflac_uint32 zeroCountParts[4]; + drflac_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int64x1_t shift64; + uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + + const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s64(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */ + one128 = vdupq_n_u32(1); + + /* + Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than + what's available in the input buffers. It would be convenient to use a fall-through switch to do this, but this results + in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted + so I think there's opportunity for this to be simplified. + */ + { + int runningOrder = order; + drflac_int32 tempC[4] = {0, 0, 0, 0}; + drflac_int32 tempS[4] = {0, 0, 0, 0}; + + /* 0 - 3. */ + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */ + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */ + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */ + } + + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 4 - 7 */ + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */ + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */ + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */ + } + + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* 8 - 11 */ + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */ + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */ + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */ + } + + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + + /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */ + coefficients128_0 = drflac__vrevq_s32(coefficients128_0); + coefficients128_4 = drflac__vrevq_s32(coefficients128_4); + coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + } + + /* For this version we are doing one sample at a time. */ + while (pDecodedSamples < pDecodedSamplesEnd) { + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return DRFLAC_FALSE; + } + + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + + for (i = 0; i < 4; i += 1) { + int64x1_t prediction64; + + prediction128 = veorq_s64(prediction128, prediction128); /* Reset to 0. */ + switch (order) + { + case 12: + case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); + case 10: + case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); + case 8: + case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); + case 6: + case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); + case 4: + case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); + case 2: + case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); + } + + /* Horizontal add and shift. */ + prediction64 = drflac__vhaddq_s64(prediction128); + prediction64 = vshl_s64(prediction64, shift64); + prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); + + /* Our value should be sitting in prediction64[0]. We need to combine this with our SSE samples. */ + samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + + /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */ + riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + + /* We store samples in groups of 4. */ + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + + /* Make sure we process the last few samples. */ + i = (count & ~3); + while (i < (int)count) { + /* Rice extraction. */ + if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return DRFLAC_FALSE; + } + + /* Rice reconstruction. */ + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + + /* Sample reconstruction. */ + pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + + i += 1; + pDecodedSamples += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(pSamplesOut != NULL); + + /* In my testing the order is rarely > 12, so in this case I'm going to simplify the NEON implementation by only handling order <= 12. */ + if (lpcOrder > 0 && lpcOrder <= 12) { + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif + +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ +#if defined(DRFLAC_SUPPORT_SSE41) + if (drflac__gIsSSE41Supported) { + return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported) { + return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#endif + { + /* Scalar fallback. */ + #if 0 + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #else + return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #endif + } +} + +/* Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. */ +static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +{ + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + + for (i = 0; i < count; ++i) { + if (!drflac__seek_rice_parts(bs, riceParam)) { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_uint32 i; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(unencodedBitsPerSample <= 31); /* <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31. */ + DRFLAC_ASSERT(pSamplesOut != NULL); + + for (i = 0; i < count; ++i) { + if (unencodedBitsPerSample > 0) { + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return DRFLAC_FALSE; + } + } else { + pSamplesOut[i] = 0; + } + + if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} + + +/* +Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called +when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be ignored. The +<blockSize> and <order> parameters are used to determine how many residual values need to be decoded. +*/ +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_uint8 residualMethod; + drflac_uint8 partitionOrder; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(blockSize != 0); + DRFLAC_ASSERT(pDecodedSamples != NULL); /* <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? */ + + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ + } + + /* Ignore the first <order> values. */ + pDecodedSamples += lpcOrder; + + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + /* + From the FLAC spec: + The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + */ + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + /* Validation check. */ + if ((blockSize / (1 << partitionOrder)) < lpcOrder) { + return DRFLAC_FALSE; + } + + samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; + partitionsRemaining = (1 << partitionOrder); + for (;;) { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } else { + drflac_uint8 unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } + + pDecodedSamples += samplesInPartition; + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } + } + + return DRFLAC_TRUE; +} + +/* +Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called +when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be set to 0. The +<blockSize> and <order> parameters are used to determine how many residual values need to be decoded. +*/ +static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) +{ + drflac_uint8 residualMethod; + drflac_uint8 partitionOrder; + drflac_uint32 samplesInPartition; + drflac_uint32 partitionsRemaining; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(blockSize != 0); + + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; /* Unknown or unsupported residual coding method. */ + } + + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + /* + From the FLAC spec: + The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + */ + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + /* Validation check. */ + if ((blockSize / (1 << partitionOrder)) <= order) { + return DRFLAC_FALSE; + } + + samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + partitionsRemaining = (1 << partitionOrder); + for (;;) + { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return DRFLAC_FALSE; + } + } else { + drflac_uint8 unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return DRFLAC_FALSE; + } + } + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +{ + drflac_uint32 i; + + /* Only a single sample needs to be decoded here. */ + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + /* + We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) + we'll want to look at a more efficient way. + */ + for (i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +{ + drflac_uint32 i; + + for (i = 0; i < blockSize; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_uint32 i; + + static drflac_int32 lpcCoefficientsTable[5][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {2, -1, 0, 0}, + {3, -3, 1, 0}, + {4, -6, 4, -1} + }; + + /* Warm up samples and coefficients. */ + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_uint8 i; + drflac_uint8 lpcPrecision; + drflac_int8 lpcShift; + drflac_int32 coefficients[32]; + + /* Warm up samples. */ + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; /* Invalid. */ + } + lpcPrecision += 1; + + if (!drflac__read_int8(bs, 5, &lpcShift)) { + return DRFLAC_FALSE; + } + + /* + From the FLAC specification: + + Quantized linear predictor coefficient shift needed in bits (NOTE: this number is signed two's-complement) + + Emphasis on the "signed two's-complement". In practice there does not seem to be any encoders nor decoders supporting negative shifts. For now dr_flac is + not going to support negative shifts as I don't have any reference files. However, when a reference file comes through I will consider adding support. + */ + if (lpcShift < 0) { + return DRFLAC_FALSE; + } + + DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + for (i = 0; i < lpcOrder; ++i) { + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + return DRFLAC_FALSE; + } + } + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) +{ + const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; /* -1 = reserved. */ + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(header != NULL); + + /* Keep looping until we find a valid sync code. */ + for (;;) { + drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */ + drflac_uint8 reserved = 0; + drflac_uint8 blockingStrategy = 0; + drflac_uint8 blockSize = 0; + drflac_uint8 sampleRate = 0; + drflac_uint8 channelAssignment = 0; + drflac_uint8 bitsPerSample = 0; + drflac_bool32 isVariableBlockSize; + + if (!drflac__find_and_seek_to_next_sync_code(bs)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockingStrategy, 1); + + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DRFLAC_FALSE; + } + if (blockSize == 0) { + continue; + } + crc8 = drflac_crc8(crc8, blockSize, 4); + + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, sampleRate, 4); + + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DRFLAC_FALSE; + } + if (channelAssignment > 10) { + continue; + } + crc8 = drflac_crc8(crc8, channelAssignment, 4); + + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DRFLAC_FALSE; + } + if (bitsPerSample == 3 || bitsPerSample == 7) { + continue; + } + crc8 = drflac_crc8(crc8, bitsPerSample, 3); + + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + drflac_uint64 pcmFrameNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_AT_END) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = 0; + header->pcmFrameNumber = pcmFrameNumber; + } else { + drflac_uint64 flacFrameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_AT_END) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = (drflac_uint32)flacFrameNumber; /* <-- Safe cast. */ + header->pcmFrameNumber = 0; + } + + + DRFLAC_ASSERT(blockSize > 0); + if (blockSize == 1) { + header->blockSizeInPCMFrames = 192; + } else if (blockSize <= 5) { + DRFLAC_ASSERT(blockSize >= 2); + header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); + header->blockSizeInPCMFrames += 1; + } else if (blockSize == 7) { + if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); + if (header->blockSizeInPCMFrames == 0xFFFF) { + return DRFLAC_FALSE; /* Frame is too big. This is the size of the frame minus 1. The STREAMINFO block defines the max block size which is 16-bits. Adding one will make it 17 bits and therefore too big. */ + } + header->blockSizeInPCMFrames += 1; + } else { + DRFLAC_ASSERT(blockSize >= 8); + header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); + } + + + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; /* Invalid. Assume an invalid block. */ + } + + + header->channelAssignment = channelAssignment; + + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + + if (header->bitsPerSample != streaminfoBitsPerSample) { + /* If this subframe has a different bitsPerSample then streaminfo or the first frame, reject it */ + return DRFLAC_FALSE; + } + + if (!drflac__read_uint8(bs, 8, &header->crc8)) { + return DRFLAC_FALSE; + } + +#ifndef DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; /* CRC mismatch. Loop back to the top and find the next sync code. */ + } +#endif + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +{ + drflac_uint8 header; + int type; + + if (!drflac__read_uint8(bs, 8, &header)) { + return DRFLAC_FALSE; + } + + /* First bit should always be 0. */ + if ((header & 0x80) != 0) { + return DRFLAC_FALSE; + } + + /* + Default to 0 for the LPC order. It's important that we always set this to 0 for non LPC + and FIXED subframes because we'll be using it in a generic validation check later. + */ + pSubframe->lpcOrder = 0; + + type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + } + } + + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { + return DRFLAC_FALSE; + } + + /* Wasted bits per sample. */ + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) { + unsigned int wastedBitsPerSample; + if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return DRFLAC_FALSE; + } + pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) +{ + drflac_subframe* pSubframe; + drflac_uint32 subframeBitsPerSample; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(frame != NULL); + + pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + + if (subframeBitsPerSample > 32) { + /* libFLAC and ffmpeg reject 33-bit subframes as well */ + return DRFLAC_FALSE; + } + + /* Need to handle wasted bits per sample. */ + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return DRFLAC_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + + pSubframe->pSamplesS32 = pDecodedSamplesOut; + + /* + pDecodedSamplesOut will be pointing to a buffer that was allocated with enough memory to store + maxBlockSizeInPCMFrames samples (as specified in the FLAC header). We need to guard against an + overflow here. At a higher level we are checking maxBlockSizeInPCMFrames from the header, but + here we need to do an additional check to ensure this frame's block size fully encompasses any + warmup samples which is determined by the LPC order. For non LPC and FIXED subframes, the LPC + order will be have been set to 0 in drflac__read_subframe_header(). + */ + if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) { + return DRFLAC_FALSE; + } + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +{ + drflac_subframe* pSubframe; + drflac_uint32 subframeBitsPerSample; + + DRFLAC_ASSERT(bs != NULL); + DRFLAC_ASSERT(frame != NULL); + + pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + /* Side channels require an extra bit per sample. Took a while to figure that one out... */ + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + + /* Need to handle wasted bits per sample. */ + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return DRFLAC_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + + pSubframe->pSamplesS32 = NULL; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + if (!drflac__seek_bits(bs, subframeBitsPerSample)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac_uint8 lpcPrecision; + + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; /* Invalid. */ + } + lpcPrecision += 1; + + + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; /* +5 for shift. */ + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) +{ + drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + + DRFLAC_ASSERT(channelAssignment <= 10); + return lookup[channelAssignment]; +} + +static drflac_result drflac__decode_flac_frame(drflac* pFlac) +{ + int channelCount; + int i; + drflac_uint8 paddingSizeInBits; + drflac_uint16 desiredCRC16; +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; +#endif + + /* This function should be called while the stream is sitting on the first byte after the frame header. */ + DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + + /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */ + if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { + return DRFLAC_ERROR; + } + + /* The number of channels in the frame must match the channel count from the STREAMINFO block. */ + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return DRFLAC_ERROR; + } + + for (i = 0; i < channelCount; ++i) { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return DRFLAC_ERROR; + } + } + + paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + if (paddingSizeInBits > 0) { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return DRFLAC_AT_END; + } + } + +#ifndef DR_FLAC_NO_CRC + actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_AT_END; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ + } +#endif + + pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + + return DRFLAC_SUCCESS; +} + +static drflac_result drflac__seek_flac_frame(drflac* pFlac) +{ + int channelCount; + int i; + drflac_uint16 desiredCRC16; +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16; +#endif + + channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + for (i = 0; i < channelCount; ++i) { + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return DRFLAC_ERROR; + } + } + + /* Padding. */ + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return DRFLAC_ERROR; + } + + /* CRC. */ +#ifndef DR_FLAC_NO_CRC + actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_AT_END; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; /* CRC mismatch. */ + } +#endif + + return DRFLAC_SUCCESS; +} + +static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) +{ + DRFLAC_ASSERT(pFlac != NULL); + + for (;;) { + drflac_result result; + + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + + result = drflac__decode_flac_frame(pFlac); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_CRC_MISMATCH) { + continue; /* CRC mismatch. Skip to the next frame. */ + } else { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; + } +} + +static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) +{ + drflac_uint64 firstPCMFrame; + drflac_uint64 lastPCMFrame; + + DRFLAC_ASSERT(pFlac != NULL); + + firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; + if (firstPCMFrame == 0) { + firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + } + + lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + if (lastPCMFrame > 0) { + lastPCMFrame -= 1; /* Needs to be zero based. */ + } + + if (pFirstPCMFrame) { + *pFirstPCMFrame = firstPCMFrame; + } + if (pLastPCMFrame) { + *pLastPCMFrame = lastPCMFrame; + } +} + +static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) +{ + drflac_bool32 result; + + DRFLAC_ASSERT(pFlac != NULL); + + result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + + DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + pFlac->currentPCMFrame = 0; + + return result; +} + +static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) +{ + /* This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. */ + DRFLAC_ASSERT(pFlac != NULL); + return drflac__seek_flac_frame(pFlac); +} + + +static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) +{ + drflac_uint64 pcmFramesRead = 0; + while (pcmFramesToSeek > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { + pcmFramesRead += pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; /* <-- Safe cast. Will always be < currentFrame.pcmFramesRemaining < 65536. */ + pcmFramesToSeek = 0; + } else { + pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; + pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + } + } + } + + pFlac->currentPCMFrame += pcmFramesRead; + return pcmFramesRead; +} + + +static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_bool32 isMidFrame = DRFLAC_FALSE; + drflac_uint64 runningPCMFrameCount; + + DRFLAC_ASSERT(pFlac != NULL); + + /* If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. */ + if (pcmFrameIndex >= pFlac->currentPCMFrame) { + /* Seeking forward. Need to seek from the current position. */ + runningPCMFrameCount = pFlac->currentPCMFrame; + + /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + /* Seeking backwards. Need to seek from the start of the file. */ + runningPCMFrameCount = 0; + + /* Move back to the start. */ + if (!drflac__seek_to_first_frame(pFlac)) { + return DRFLAC_FALSE; + } + + /* Decode the first frame in preparation for sample-exact seeking below. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } + + /* + We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its + header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. + */ + for (;;) { + drflac_uint64 pcmFrameCountInThisFLACFrame; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + /* + The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + it never existed and keep iterating. + */ + drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* We started seeking mid-frame which means we need to skip the frame decoding part. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + /* + It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + frame never existed and leave the running sample count untouched. + */ + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* + We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. + */ + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = DRFLAC_FALSE; + } + + /* If we are seeking to the end of the file and we've just hit it, we're done. */ + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return DRFLAC_TRUE; + } + } + + next_iteration: + /* Grab the next frame in preparation for the next iteration. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } +} + + +#if !defined(DR_FLAC_NO_CRC) +/* +We use an average compression ratio to determine our approximate start location. FLAC files are generally about 50%-70% the size of their +uncompressed counterparts so we'll use this as a basis. I'm going to split the middle and use a factor of 0.6 to determine the starting +location. +*/ +#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f + +static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) +{ + DRFLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + DRFLAC_ASSERT(targetByte >= rangeLo); + DRFLAC_ASSERT(targetByte <= rangeHi); + + *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; + + for (;;) { + /* After rangeLo == rangeHi == targetByte fails, we need to break out. */ + drflac_uint64 lastTargetByte = targetByte; + + /* When seeking to a byte, failure probably means we've attempted to seek beyond the end of the stream. To counter this we just halve it each attempt. */ + if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { + /* If we couldn't even seek to the first byte in the stream we have a problem. Just abandon the whole thing. */ + if (targetByte == 0) { + drflac__seek_to_first_frame(pFlac); /* Try to recover. */ + return DRFLAC_FALSE; + } + + /* Halve the byte location and continue. */ + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + /* Getting here should mean that we have seeked to an appropriate byte. */ + + /* Clear the details of the FLAC frame so we don't misreport data. */ + DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + + /* + Now seek to the next FLAC frame. We need to decode the entire frame (not just the header) because it's possible for the header to incorrectly pass the + CRC check and return bad data. We need to decode the entire frame to be more certain. Although this seems unlikely, this has happened to me in testing + so it needs to stay this way for now. + */ +#if 1 + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + /* Halve the byte location and continue. */ + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#else + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + /* Halve the byte location and continue. */ + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#endif + } + + /* We already tried this byte and there are no more to try, break out. */ + if(targetByte == lastTargetByte) { + return DRFLAC_FALSE; + } + } + + /* The current PCM frame needs to be updated based on the frame we just seeked to. */ + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + + DRFLAC_ASSERT(targetByte <= rangeHi); + + *pLastSuccessfulSeekOffset = targetByte; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) +{ + /* This section of code would be used if we were only decoding the FLAC frame header when calling drflac__seek_to_approximate_flac_frame_to_byte(). */ +#if 0 + if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { + /* We failed to decode this frame which may be due to it being corrupt. We'll just use the next valid FLAC frame. */ + if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { + return DRFLAC_FALSE; + } + } +#endif + + return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; +} + + +static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) +{ + /* This assumes pFlac->currentPCMFrame is sitting on byteRangeLo upon entry. */ + + drflac_uint64 targetByte; + drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + drflac_uint64 pcmRangeHi = 0; + drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; + drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + + targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + + for (;;) { + if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + /* We found a FLAC frame. We need to check if it contains the sample we're looking for. */ + drflac_uint64 newPCMRangeLo; + drflac_uint64 newPCMRangeHi; + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + + /* If we selected the same frame, it means we should be pretty close. Just decode the rest. */ + if (pcmRangeLo == newPCMRangeLo) { + if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + break; /* Failed to seek to closest frame. */ + } + + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return DRFLAC_TRUE; + } else { + break; /* Failed to seek forward. */ + } + } + + pcmRangeLo = newPCMRangeLo; + pcmRangeHi = newPCMRangeHi; + + if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { + /* The target PCM frame is in this FLAC frame. */ + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return DRFLAC_TRUE; + } else { + break; /* Failed to seek to FLAC frame. */ + } + } else { + const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + + if (pcmRangeLo > pcmFrameIndex) { + /* We seeked too far forward. We need to move our target byte backward and try again. */ + byteRangeHi = lastSuccessfulSeekOffset; + if (byteRangeLo > byteRangeHi) { + byteRangeLo = byteRangeHi; + } + + targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); + if (targetByte < byteRangeLo) { + targetByte = byteRangeLo; + } + } else /*if (pcmRangeHi < pcmFrameIndex)*/ { + /* We didn't seek far enough. We need to move our target byte forward and try again. */ + + /* If we're close enough we can just seek forward. */ + if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { + if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return DRFLAC_TRUE; + } else { + break; /* Failed to seek to FLAC frame. */ + } + } else { + byteRangeLo = lastSuccessfulSeekOffset; + if (byteRangeHi < byteRangeLo) { + byteRangeHi = byteRangeLo; + } + + targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + + if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { + closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; + } + } + } + } + } else { + /* Getting here is really bad. We just recover as best we can, but moving to the first frame in the stream, and then abort. */ + break; + } + } + + drflac__seek_to_first_frame(pFlac); /* <-- Try to recover. */ + return DRFLAC_FALSE; +} + +static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_uint64 byteRangeLo; + drflac_uint64 byteRangeHi; + drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + + /* Our algorithm currently assumes the FLAC stream is currently sitting at the start. */ + if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { + return DRFLAC_FALSE; + } + + /* If we're close enough to the start, just move to the start and seek forward. */ + if (pcmFrameIndex < seekForwardThreshold) { + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + } + + /* + Our starting byte range is the byte position of the first FLAC frame and the approximate end of the file as if it were completely uncompressed. This ensures + the entire file is included, even though most of the time it'll exceed the end of the actual stream. This is OK as the frame searching logic will handle it. + */ + byteRangeLo = pFlac->firstFLACFramePosInBytes; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + + return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); +} +#endif /* !DR_FLAC_NO_CRC */ + +static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_uint32 iClosestSeekpoint = 0; + drflac_bool32 isMidFrame = DRFLAC_FALSE; + drflac_uint64 runningPCMFrameCount; + drflac_uint32 iSeekpoint; + + + DRFLAC_ASSERT(pFlac != NULL); + + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { + return DRFLAC_FALSE; + } + + /* Do not use the seektable if pcmFramIndex is not coverd by it. */ + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return DRFLAC_FALSE; + } + + for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { + break; + } + + iClosestSeekpoint = iSeekpoint; + } + + /* There's been cases where the seek table contains only zeros. We need to do some basic validation on the closest seekpoint. */ + if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { + return DRFLAC_FALSE; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { + return DRFLAC_FALSE; + } + +#if !defined(DR_FLAC_NO_CRC) + /* At this point we should know the closest seek point. We can use a binary search for this. We need to know the total sample count for this. */ + if (pFlac->totalPCMFrameCount > 0) { + drflac_uint64 byteRangeLo; + drflac_uint64 byteRangeHi; + + byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; + + /* + If our closest seek point is not the last one, we only need to search between it and the next one. The section below calculates an appropriate starting + value for byteRangeHi which will clamp it appropriately. + + Note that the next seekpoint must have an offset greater than the closest seekpoint because otherwise our binary search algorithm will break down. There + have been cases where a seektable consists of seek points where every byte offset is set to 0 which causes problems. If this happens we need to abort. + */ + if (iClosestSeekpoint < pFlac->seekpointCount-1) { + drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + + /* Basic validation on the seekpoints to ensure they're usable. */ + if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { + return DRFLAC_FALSE; /* The next seekpoint doesn't look right. The seek table cannot be trusted from here. Abort. */ + } + + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Make sure it's not a placeholder seekpoint. */ + byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; /* byteRangeHi must be zero based. */ + } + } + + if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + + if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return DRFLAC_TRUE; + } + } + } + } +#endif /* !DR_FLAC_NO_CRC */ + + /* Getting here means we need to use a slower algorithm because the binary search method failed or cannot be used. */ + + /* + If we are seeking forward and the closest seekpoint is _before_ the current sample, we just seek forward from where we are. Otherwise we start seeking + from the seekpoint's first sample. + */ + if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { + /* Optimized case. Just seek forward from where we are. */ + runningPCMFrameCount = pFlac->currentPCMFrame; + + /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */ + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + /* Slower case. Seek to the start of the seekpoint and then seek forward from there. */ + runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; + + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return DRFLAC_FALSE; + } + + /* Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } + + for (;;) { + drflac_uint64 pcmFrameCountInThisFLACFrame; + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + /* + The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend + it never existed and keep iterating. + */ + drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* We started seeking mid-frame which means we need to skip the frame decoding part. */ + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + /* + It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + frame never existed and leave the running sample count untouched. + */ + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* + We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header. + */ + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = DRFLAC_FALSE; + } + + /* If we are seeking to the end of the file and we've just hit it, we're done. */ + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return DRFLAC_TRUE; + } + } + + next_iteration: + /* Grab the next frame in preparation for the next iteration. */ + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + } +} + + +#ifndef DR_FLAC_NO_OGG +typedef struct +{ + drflac_uint8 capturePattern[4]; /* Should be "OggS" */ + drflac_uint8 structureVersion; /* Always 0. */ + drflac_uint8 headerType; + drflac_uint64 granulePosition; + drflac_uint32 serialNumber; + drflac_uint32 sequenceNumber; + drflac_uint32 checksum; + drflac_uint8 segmentCount; + drflac_uint8 segmentTable[255]; +} drflac_ogg_page_header; +#endif + +typedef struct +{ + drflac_read_proc onRead; + drflac_seek_proc onSeek; + drflac_meta_proc onMeta; + drflac_container container; + void* pUserData; + void* pUserDataMD; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalPCMFrameCount; + drflac_uint16 maxBlockSizeInPCMFrames; + drflac_uint64 runningFilePos; + drflac_bool32 hasStreamInfoBlock; + drflac_bool32 hasMetadataBlocks; + drflac_bs bs; /* <-- A bit streamer is required for loading data during initialization. */ + drflac_frame_header firstFrameHeader; /* <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. */ + +#ifndef DR_FLAC_NO_OGG + drflac_uint32 oggSerial; + drflac_uint64 oggFirstBytePos; + drflac_ogg_page_header oggBosHeader; +#endif +} drflac_init_info; + +static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + blockHeader = drflac__be2host_32(blockHeader); + *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); + *blockSize = (blockHeader & 0x00FFFFFFUL); +} + +static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + drflac_uint32 blockHeader; + + *blockSize = 0; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return DRFLAC_FALSE; + } + + drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +{ + drflac_uint32 blockSizes; + drflac_uint64 frameSizes = 0; + drflac_uint64 importantProps; + drflac_uint8 md5[16]; + + /* min/max block size. */ + if (onRead(pUserData, &blockSizes, 4) != 4) { + return DRFLAC_FALSE; + } + + /* min/max frame size. */ + if (onRead(pUserData, &frameSizes, 6) != 6) { + return DRFLAC_FALSE; + } + + /* Sample rate, channels, bits per sample and total sample count. */ + if (onRead(pUserData, &importantProps, 8) != 8) { + return DRFLAC_FALSE; + } + + /* MD5 */ + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return DRFLAC_FALSE; + } + + blockSizes = drflac__be2host_32(blockSizes); + frameSizes = drflac__be2host_64(frameSizes); + importantProps = drflac__be2host_64(importantProps); + + pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + + return DRFLAC_TRUE; +} + + +static void* drflac__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRFLAC_MALLOC(sz); +} + +static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRFLAC_REALLOC(p, sz); +} + +static void drflac__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRFLAC_FREE(p); +} + + +static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + + if (p != NULL) { + DRFLAC_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + + +static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) +{ + /* + We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that + we'll be sitting on byte 42. + */ + drflac_uint64 runningFilePos = 42; + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + + for (;;) { + drflac_metadata metadata; + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType = 0; + drflac_uint32 blockSize; + if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { + return DRFLAC_FALSE; + } + runningFilePos += 4; + + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + + switch (blockType) + { + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (blockSize < 4) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + + if (onMeta) { + drflac_uint32 seekpointCount; + drflac_uint32 iSeekpoint; + void* pRawData; + + seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + + pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + /* We need to read seekpoint by seekpoint and do some processing. */ + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + + if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + /* Endian swap. */ + pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (blockSize < 8) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + drflac_uint32 i; + + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for the rest of the block */ + if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */ + if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.vorbis_comment.pComments = pRunningData; + + /* Check that the comments section is valid before passing it to the callback */ + for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { + drflac_uint32 commentLength; + + if (pRunningDataEnd - pRunningData < 4) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + pRunningData += commentLength; + } + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (blockSize < 396) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + size_t bufferSize; + drflac_uint8 iTrack; + drflac_uint8 iIndex; + void* pTrackData; + + /* + This needs to be loaded in two passes. The first pass is used to calculate the size of the memory allocation + we need for storing the necessary data. The second pass will fill that buffer with usable data. + */ + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + + DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; + metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = NULL; /* Will be filled later. */ + + /* Pass 1: Calculate the size of the buffer for the track data. */ + { + const char* pRunningDataSaved = pRunningData; /* Will be restored at the end in preparation for the second pass. */ + + bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + drflac_uint32 indexPointSize; + + if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + /* Skip to the index point count */ + pRunningData += 35; + + indexCount = pRunningData[0]; + pRunningData += 1; + + bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); + + /* Quick validation check. */ + indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + pRunningData += indexPointSize; + } + + pRunningData = pRunningDataSaved; + } + + /* Pass 2: Allocate a buffer and fill the data. Validation was done in the step above so can be skipped. */ + { + char* pRunningTrackData; + + pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + pRunningTrackData = (char*)pTrackData; + + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + drflac_uint8 indexCount; + + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; /* Skip forward, but not beyond the last byte in the CUESHEET_TRACK block which is the index count. */ + pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + + /* Grab the index count for the next part. */ + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + + /* Extract each track index. */ + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; + + DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(drflac_cuesheet_track_index); + + pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + } + } + + metadata.data.cuesheet.pTrackData = pTrackData; + } + + /* The original data is no longer needed. */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (blockSize < 32) { + return DRFLAC_FALSE; + } + + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + + pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + + metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for the rest of the block */ + if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + + /* Need space for the rest of the block */ + if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + + /* Need space for the picture after the block */ + if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */ + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (onMeta) { + metadata.data.padding.unused = 0; + + /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */ + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ + } else { + onMeta(pUserDataMD, &metadata); + } + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + { + /* Invalid chunk. Just skip over this one. */ + if (onMeta) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */ + } + } + } break; + + default: + { + /* + It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we + can at the very least report the chunk to the application and let it look at the raw data. + */ + if (onMeta) { + void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + onMeta(pUserDataMD, &metadata); + + drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + } + + /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */ + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; + } + } + + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */ + + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + + (void)onSeek; + + pInit->container = drflac_container_native; + + /* The first metadata block should be the STREAMINFO block. */ + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (!relaxed) { + /* We're opening in strict mode and the first block is not the STREAMINFO block. Error. */ + return DRFLAC_FALSE; + } else { + /* + Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined + for that frame. + */ + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + + if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return DRFLAC_FALSE; /* Couldn't find a frame. */ + } + + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return DRFLAC_FALSE; /* Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. */ + } + + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSizeInPCMFrames = 65535; /* <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo */ + return DRFLAC_TRUE; + } + } else { + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DRFLAC_FALSE; + } + + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; /* Don't care about the min block size - only the max (used for determining the size of the memory allocation). */ + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + return DRFLAC_TRUE; + } +} + +#ifndef DR_FLAC_NO_OGG +#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 /* CRC-32 of "OggS". */ + +typedef enum +{ + drflac_ogg_recover_on_crc_mismatch, + drflac_ogg_fail_on_crc_mismatch +} drflac_ogg_crc_mismatch_recovery; + +#ifndef DR_FLAC_NO_CRC +static drflac_uint32 drflac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +{ +#ifndef DR_FLAC_NO_CRC + return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} + +#if 0 +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +{ + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + return crc32; +} + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +{ + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +{ + /* This can be optimized. */ + drflac_uint32 i; + for (i = 0; i < dataSize; ++i) { + crc32 = drflac_crc32_byte(crc32, pData[i]); + } + return crc32; +} + + +static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +{ + drflac_uint32 pageBodySize = 0; + int i; + + for (i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + + return pageBodySize; +} + +static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + drflac_uint8 data[23]; + drflac_uint32 i; + + DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + + if (onRead(pUserData, data, 23) != 23) { + return DRFLAC_AT_END; + } + *pBytesRead += 23; + + /* + It's not actually used, but set the capture pattern to 'OggS' for completeness. Not doing this will cause static analysers to complain about + us trying to access uninitialized data. We could alternatively just comment out this member of the drflac_ogg_page_header structure, but I + like to have it map to the structure of the underlying data. + */ + pHeader->capturePattern[0] = 'O'; + pHeader->capturePattern[1] = 'g'; + pHeader->capturePattern[2] = 'g'; + pHeader->capturePattern[3] = 'S'; + + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + + /* Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. */ + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + + for (i = 0; i < 23; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); + } + + + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return DRFLAC_AT_END; + } + *pBytesRead += pHeader->segmentCount; + + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + + return DRFLAC_SUCCESS; +} + +static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + drflac_uint8 id[4]; + + *pBytesRead = 0; + + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_AT_END; + } + *pBytesRead += 4; + + /* We need to read byte-by-byte until we find the OggS capture pattern. */ + for (;;) { + if (drflac_ogg__is_capture_pattern(id)) { + drflac_result result; + + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + + result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) { + return DRFLAC_SUCCESS; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + /* The first 4 bytes did not equal the capture pattern. Read the next byte and try again. */ + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return DRFLAC_AT_END; + } + *pBytesRead += 1; + } + } +} + + +/* +The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works +in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed +in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type +dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from +the physical Ogg bitstream are converted and delivered in native FLAC format. +*/ +typedef struct +{ + drflac_read_proc onRead; /* The original onRead callback from drflac_open() and family. */ + drflac_seek_proc onSeek; /* The original onSeek callback from drflac_open() and family. */ + void* pUserData; /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */ + drflac_uint64 currentBytePos; /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */ + drflac_uint64 firstBytePos; /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */ + drflac_uint32 serialNumber; /* The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. */ + drflac_ogg_page_header bosPageHeader; /* Used for seeking. */ + drflac_ogg_page_header currentPageHeader; + drflac_uint32 bytesRemainingInPage; + drflac_uint32 pageDataSize; + drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; +} drflac_oggbs; /* oggbs = Ogg Bitstream */ + +static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) +{ + size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); + oggbs->currentBytePos += bytesActuallyRead; + + return bytesActuallyRead; +} + +static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) +{ + if (origin == drflac_seek_origin_start) { + if (offset <= 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return DRFLAC_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + } + } else { + while (offset > 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; + } + + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { /* <-- Safe cast thanks to the loop above. */ + return DRFLAC_FALSE; + } + oggbs->currentBytePos += offset; + + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) +{ + drflac_ogg_page_header header; + for (;;) { + drflac_uint32 crc32 = 0; + drflac_uint32 bytesRead; + drflac_uint32 pageBodySize; +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32; +#endif + + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += bytesRead; + + pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + continue; /* Invalid page size. Assume it's corrupted and just move to the next page. */ + } + + if (header.serialNumber != oggbs->serialNumber) { + /* It's not a FLAC page. Skip it. */ + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + continue; + } + + + /* We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. */ + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return DRFLAC_FALSE; + } + oggbs->pageDataSize = pageBodySize; + +#ifndef DR_FLAC_NO_CRC + actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + continue; /* CRC mismatch. Skip this page. */ + } else { + /* + Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we + go to the next valid page to ensure we're in a good state, but return false to let the caller know that the + seek did not fully complete. + */ + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; + } + } +#else + (void)recoveryMethod; /* <-- Silence a warning. */ +#endif + + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return DRFLAC_TRUE; + } +} + +/* Function below is unused at the moment, but I might be re-adding it later. */ +#if 0 +static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) +{ + drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + drflac_uint8 iSeg = 0; + drflac_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); + return iSeg; +} + +static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +{ + /* The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. */ + for (;;) { + drflac_bool32 atEndOfPage = DRFLAC_FALSE; + + drflac_uint8 bytesRemainingInSeg; + drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + + drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = DRFLAC_TRUE; + } + + break; + } + + bytesToEndOfPacketOrPage += segmentSize; + } + + /* + At this point we will have found either the packet or the end of the page. If were at the end of the page we'll + want to load the next page and keep searching for the end of the packet. + */ + drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; + + if (atEndOfPage) { + /* + We're potentially at the next packet, but we need to check the next page first to be sure because the packet may + straddle pages. + */ + if (!drflac_oggbs__goto_next_page(oggbs)) { + return DRFLAC_FALSE; + } + + /* If it's a fresh packet it most likely means we're at the next packet. */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return DRFLAC_TRUE; + } + } else { + /* We're at the next packet. */ + return DRFLAC_TRUE; + } + } +} + +static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +{ + /* The bitstream should be sitting on the first byte just after the header of the frame. */ + + /* What we're actually doing here is seeking to the start of the next packet. */ + return drflac_oggbs__seek_to_next_packet(oggbs); +} +#endif + +static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + size_t bytesRead = 0; + + DRFLAC_ASSERT(oggbs != NULL); + DRFLAC_ASSERT(pRunningBufferOut != NULL); + + /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */ + while (bytesRead < bytesToRead) { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + break; + } + + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) { + DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + DRFLAC_ASSERT(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + break; /* Failed to go to the next page. Might have simply hit the end of the stream. */ + } + } + + return bytesRead; +} + +static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + int bytesSeeked = 0; + + DRFLAC_ASSERT(oggbs != NULL); + DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + + /* Seeking is always forward which makes things a lot simpler. */ + if (origin == drflac_seek_origin_start) { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + } + + DRFLAC_ASSERT(origin == drflac_seek_origin_current); + + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + DRFLAC_ASSERT(bytesRemainingToSeek >= 0); + + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */ + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + + /* If we get here it means some of the requested data is contained in the next pages. */ + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + DRFLAC_ASSERT(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */ + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + drflac_uint64 originalBytePos; + drflac_uint64 runningGranulePosition; + drflac_uint64 runningFrameBytePos; + drflac_uint64 runningPCMFrameCount; + + DRFLAC_ASSERT(oggbs != NULL); + + originalBytePos = oggbs->currentBytePos; /* For recovery. Points to the OggS identifier. */ + + /* First seek to the first frame. */ + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return DRFLAC_FALSE; + } + oggbs->bytesRemainingInPage = 0; + + runningGranulePosition = 0; + for (;;) { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DRFLAC_FALSE; /* Never did find that sample... */ + } + + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { + break; /* The sample is somewhere in the previous page. */ + } + + /* + At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we + disregard any pages that do not begin a fresh packet. + */ + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { /* <-- Is it a fresh page? */ + if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + drflac_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { /* <-- Does the page begin with a frame's sync code? */ + runningGranulePosition = oggbs->currentPageHeader.granulePosition; + } + + continue; + } + } + } + + /* + We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the + start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of + a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until + we find the one containing the target sample. + */ + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + /* + At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep + looping over these frames until we find the one containing the sample we're after. + */ + runningPCMFrameCount = runningGranulePosition; + for (;;) { + /* + There are two ways to find the sample and seek past irrelevant frames: + 1) Use the native FLAC decoder. + 2) Use Ogg's framing system. + + Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + duplication for the decoding of frame headers. + + Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks + the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + using the native FLAC decoding APIs, such as drflac__read_next_flac_frame_header(), need to be re-implemented so as to + avoid the use of the drflac_bs object. + + Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. + 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + 3) Simplicity. + */ + drflac_uint64 firstPCMFrameInFLACFrame = 0; + drflac_uint64 lastPCMFrameInFLACFrame = 0; + drflac_uint64 pcmFrameCountInThisFrame; + + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return DRFLAC_FALSE; + } + + drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + + pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + + /* If we are seeking to the end of the file and we've just hit it, we're done. */ + if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + pFlac->currentPCMFrame = pcmFrameIndex; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + return DRFLAC_TRUE; + } else { + return DRFLAC_FALSE; + } + } + + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { + /* + The sample should be in this FLAC frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + it never existed and keep iterating. + */ + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + /* The frame is valid. We just need to skip over some samples to ensure it's sample-exact. */ + drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */ + if (pcmFramesToDecode == 0) { + return DRFLAC_TRUE; + } + + pFlac->currentPCMFrame = runningPCMFrameCount; + + return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- If this fails, something bad has happened (it should never fail). */ + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } else { + /* + It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + frame never existed and leave the running sample count untouched. + */ + drflac_result result = drflac__seek_to_next_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; /* CRC mismatch. Pretend this frame never existed. */ + } else { + return DRFLAC_FALSE; + } + } + } + } +} + + + +static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + drflac_ogg_page_header header; + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; + + /* Pre Condition: The bit stream should be sitting just past the 4-byte OggS capture pattern. */ + (void)relaxed; + + pInit->container = drflac_container_ogg; + pInit->oggFirstBytePos = 0; + + /* + We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the + stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if + any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. + */ + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + + for (;;) { + int pageBodySize; + + /* Break if we're past the beginning of stream page. */ + if ((header.headerType & 0x02) == 0) { + return DRFLAC_FALSE; + } + + /* Check if it's a FLAC header. */ + pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) { /* 51 = the lacing value of the FLAC header packet. */ + /* It could be a FLAC page... */ + drflac_uint32 bytesRemainingInPage = pageBodySize; + drflac_uint8 packetType; + + if (onRead(pUserData, &packetType, 1) != 1) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 1; + if (packetType == 0x7F) { + /* Increasingly more likely to be a FLAC page... */ + drflac_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + /* Almost certainly a FLAC page... */ + drflac_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return DRFLAC_FALSE; + } + + if (mappingVersion[0] != 1) { + return DRFLAC_FALSE; /* Only supporting version 1.x of the Ogg mapping. */ + } + + /* + The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to + be handling it in a generic way based on the serial number and packet types. + */ + if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + + /* Expecting the native FLAC signature "fLaC". */ + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + /* The remaining data in the page should be the STREAMINFO block. */ + drflac_streaminfo streaminfo; + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DRFLAC_FALSE; /* Invalid block type. First block must be the STREAMINFO block. */ + } + + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + /* Success! */ + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; /* Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. */ + pInit->oggSerial = header.serialNumber; + pInit->oggBosHeader = header; + break; + } else { + /* Failed to read STREAMINFO block. Aww, so close... */ + return DRFLAC_FALSE; + } + } else { + /* Invalid file. */ + return DRFLAC_FALSE; + } + } else { + /* Not a FLAC header. Skip it. */ + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + /* Not a FLAC header. Seek past the entire page and move on to the next. */ + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + + pInit->runningFilePos += pageBodySize; + + + /* Read the header of the next page. */ + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + } + + /* + If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next + packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the + Ogg bistream object. + */ + pInit->hasMetadataBlocks = DRFLAC_TRUE; /* <-- Always have at least VORBIS_COMMENT metadata block. */ + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +{ + drflac_bool32 relaxed; + drflac_uint8 id[4]; + + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return DRFLAC_FALSE; + } + + DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + drflac__reset_cache(&pInit->bs); + + + /* If the container is explicitly defined then we can try opening in relaxed mode. */ + relaxed = container != drflac_container_unknown; + + /* Skip over any ID3 tags. */ + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_FALSE; /* Ran out of data. */ + } + pInit->runningFilePos += 4; + + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + drflac_uint8 header[6]; + drflac_uint8 flags; + drflac_uint32 headerSize; + + if (onRead(pUserData, header, 6) != 6) { + return DRFLAC_FALSE; /* Ran out of data. */ + } + pInit->runningFilePos += 6; + + flags = header[1]; + + DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; /* Failed to seek past the tag. */ + } + pInit->runningFilePos += headerSize; + } else { + break; + } + } + + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + + /* If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. */ + if (relaxed) { + if (container == drflac_container_native) { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (container == drflac_container_ogg) { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + + /* Unsupported container. */ + return DRFLAC_FALSE; +} + +static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) +{ + DRFLAC_ASSERT(pFlac != NULL); + DRFLAC_ASSERT(pInit != NULL); + + DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + pFlac->bs = pInit->bs; + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (drflac_uint8)pInit->channels; + pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; + pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; + pFlac->container = pInit->container; +} + + +static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac_init_info init; + drflac_uint32 allocationSize; + drflac_uint32 wholeSIMDVectorCountPerChannel; + drflac_uint32 decodedSamplesAllocationSize; +#ifndef DR_FLAC_NO_OGG + drflac_oggbs* pOggbs = NULL; +#endif + drflac_uint64 firstFramePos; + drflac_uint64 seektablePos; + drflac_uint32 seekpointCount; + drflac_allocation_callbacks allocationCallbacks; + drflac* pFlac; + + /* CPU support first. */ + drflac__init_cpu_caps(); + + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + return NULL; + } + + if (pAllocationCallbacks != NULL) { + allocationCallbacks = *pAllocationCallbacks; + if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { + return NULL; /* Invalid allocation callbacks. */ + } + } else { + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drflac__malloc_default; + allocationCallbacks.onRealloc = drflac__realloc_default; + allocationCallbacks.onFree = drflac__free_default; + } + + + /* + The size of the allocation for the drflac object needs to be large enough to fit the following: + 1) The main members of the drflac structure + 2) A block of memory large enough to store the decoded samples of the largest frame in the stream + 3) If the container is Ogg, a drflac_oggbs object + + The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration + the different SIMD instruction sets. + */ + allocationSize = sizeof(drflac); + + /* + The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector + we are supporting. + */ + if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + } + + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + + allocationSize += decodedSamplesAllocationSize; + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; /* Allocate extra bytes to ensure we have enough for alignment. */ + +#ifndef DR_FLAC_NO_OGG + /* There's additional data required for Ogg streams. */ + if (init.container == drflac_container_ogg) { + allocationSize += sizeof(drflac_oggbs); + + pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; /*DRFLAC_OUT_OF_MEMORY;*/ + } + + DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; + } +#endif + + /* + This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to + consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading + and decoding the metadata. + */ + firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */ + seektablePos = 0; + seekpointCount = 0; + if (init.hasMetadataBlocks) { + drflac_read_proc onReadOverride = onRead; + drflac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + onReadOverride = drflac__on_read_ogg; + onSeekOverride = drflac__on_seek_ogg; + pUserDataOverride = (void*)pOggbs; + } +#endif + + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + + allocationSize += seekpointCount * sizeof(drflac_seekpoint); + } + + + pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + if (pFlac == NULL) { + #ifndef DR_FLAC_NO_OGG + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + + drflac__init_from_info(pFlac, &init); + pFlac->allocationCallbacks = allocationCallbacks; + pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); + DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + + /* At this point the pOggbs object has been handed over to pInternalOggbs and can be freed. */ + drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; + + /* The Ogg bistream needs to be layered on top of the original bitstream. */ + pFlac->bs.onRead = drflac__on_read_ogg; + pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; + } +#endif + + pFlac->firstFLACFramePosInBytes = firstFramePos; + + /* NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. */ +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + /* If we have a seektable we need to load it now, making sure we move back to where we were previously. */ + if (seektablePos != 0) { + pFlac->seekpointCount = seekpointCount; + pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + + DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); + DRFLAC_ASSERT(pFlac->bs.onRead != NULL); + + /* Seek to the seektable, then just read directly into our seektable buffer. */ + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + drflac_uint32 iSeekpoint; + + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { + /* Endian swap. */ + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + /* Failed to read the seektable. Pretend we don't have one. */ + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; + } + } + + /* We need to seek back to where we were. If this fails it's a critical error. */ + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } else { + /* Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. */ + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + } + } + + + /* + If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode + the first frame. + */ + if (!init.hasStreamInfoBlock) { + pFlac->currentFLACFrame.header = init.firstFrameHeader; + for (;;) { + drflac_result result = drflac__decode_flac_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + break; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + continue; + } else { + drflac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } + } + } + + return pFlac; +} + + + +#ifndef DR_FLAC_NO_STDIO +#include <stdio.h> +#ifndef DR_FLAC_NO_WCHAR +#include <wchar.h> /* For wcslen(), wcsrtombs() */ +#endif + +/* Errno */ +/* drflac_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */ +#include <errno.h> +static drflac_result drflac_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRFLAC_SUCCESS; + #ifdef EPERM + case EPERM: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRFLAC_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRFLAC_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRFLAC_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRFLAC_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRFLAC_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRFLAC_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRFLAC_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRFLAC_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRFLAC_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRFLAC_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRFLAC_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRFLAC_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRFLAC_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRFLAC_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRFLAC_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRFLAC_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRFLAC_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRFLAC_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRFLAC_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRFLAC_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRFLAC_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; + #endif + #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */ + case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRFLAC_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRFLAC_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRFLAC_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRFLAC_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRFLAC_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRFLAC_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRFLAC_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRFLAC_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRFLAC_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRFLAC_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRFLAC_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRFLAC_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRFLAC_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRFLAC_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRFLAC_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRFLAC_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRFLAC_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRFLAC_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRFLAC_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRFLAC_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRFLAC_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRFLAC_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRFLAC_ERROR; + #endif + #ifdef EADV + case EADV: return DRFLAC_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRFLAC_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRFLAC_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRFLAC_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRFLAC_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRFLAC_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRFLAC_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRFLAC_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRFLAC_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRFLAC_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRFLAC_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRFLAC_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRFLAC_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRFLAC_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRFLAC_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRFLAC_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRFLAC_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRFLAC_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRFLAC_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRFLAC_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRFLAC_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRFLAC_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRFLAC_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRFLAC_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRFLAC_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRFLAC_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRFLAC_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRFLAC_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRFLAC_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRFLAC_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRFLAC_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRFLAC_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRFLAC_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRFLAC_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRFLAC_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRFLAC_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRFLAC_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRFLAC_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRFLAC_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRFLAC_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRFLAC_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRFLAC_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRFLAC_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRFLAC_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRFLAC_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRFLAC_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRFLAC_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRFLAC_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRFLAC_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRFLAC_ERROR; + #endif + default: return DRFLAC_ERROR; + } +} +/* End Errno */ + +/* fopen */ +static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drflac_result result = drflac_result_from_errno(errno); + if (result == DRFLAC_SUCCESS) { + result = DRFLAC_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRFLAC_SUCCESS; +} + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRFLAC_HAS_WFOPEN + #endif +#endif + +#ifndef DR_FLAC_NO_WCHAR +static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRFLAC_INVALID_ARGS; + } + +#if defined(DRFLAC_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drflac_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drflac_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. + */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRFLAC_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drflac_result_from_errno(errno); + } + + pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRFLAC_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRFLAC_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + + if (*ppFile == NULL) { + return DRFLAC_ERROR; + } +#endif + + return DRFLAC_SUCCESS; +} +#endif +/* End fopen */ + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + + return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + + +DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + + return pFlac; +} + +#ifndef DR_FLAC_NO_WCHAR +DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + + return pFlac; +} +#endif + +DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + + return pFlac; +} + +#ifndef DR_FLAC_NO_WCHAR +DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + FILE* pFile; + + if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + return NULL; + } + + pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + + return pFlac; +} +#endif +#endif /* DR_FLAC_NO_STDIO */ + +static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + size_t bytesRemaining; + + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + + bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + + DRFLAC_ASSERT(memoryStream != NULL); + DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */ + + if (offset > (drflac_int64)memoryStream->dataSize) { + return DRFLAC_FALSE; + } + + if (origin == drflac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + return DRFLAC_FALSE; /* Trying to seek too far forward. */ + } + } else { + if ((drflac_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + return DRFLAC_FALSE; /* Trying to seek too far forward. */ + } + } + + return DRFLAC_TRUE; +} + +DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac__memory_stream memoryStream; + drflac* pFlac; + + memoryStream.data = (const drflac_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + /* This is an awful hack... */ +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + +DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac__memory_stream memoryStream; + drflac* pFlac; + + memoryStream.data = (const drflac_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + /* This is an awful hack... */ +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + + + +DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); +} + +DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); +} + +DRFLAC_API void drflac_close(drflac* pFlac) +{ + if (pFlac == NULL) { + return; + } + +#ifndef DR_FLAC_NO_STDIO + /* + If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() + was used by looking at the callbacks. + */ + if (pFlac->bs.onRead == drflac__on_read_stdio) { + fclose((FILE*)pFlac->bs.pUserData); + } + +#ifndef DR_FLAC_NO_OGG + /* Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. */ + if (pFlac->container == drflac_container_ogg) { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); + + if (oggbs->onRead == drflac__on_read_stdio) { + fclose((FILE*)oggbs->pUserData); + } + } +#endif +#endif + + drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0; + pOutputSamples[i*8+1] = (drflac_int32)right0; + pOutputSamples[i*8+2] = (drflac_int32)left1; + pOutputSamples[i*8+3] = (drflac_int32)right1; + pOutputSamples[i*8+4] = (drflac_int32)left2; + pOutputSamples[i*8+5] = (drflac_int32)right2; + pOutputSamples[i*8+6] = (drflac_int32)left3; + pOutputSamples[i*8+7] = (drflac_int32)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + + drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left; + pOutputSamples[i*2+1] = (drflac_int32)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + + pOutputSamples[i*8+0] = (drflac_int32)temp0L; + pOutputSamples[i*8+1] = (drflac_int32)temp0R; + pOutputSamples[i*8+2] = (drflac_int32)temp1L; + pOutputSamples[i*8+3] = (drflac_int32)temp1R; + pOutputSamples[i*8+4] = (drflac_int32)temp2L; + pOutputSamples[i*8+5] = (drflac_int32)temp2R; + pOutputSamples[i*8+6] = (drflac_int32)temp3L; + pOutputSamples[i*8+7] = (drflac_int32)temp3R; + } + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + } + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_int32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ + int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ + uint32x4_t one4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + one4 = vdupq_n_u32(1); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + } + } else { + int32x4_t shift4; + + shift -= 1; + shift4 = vdupq_n_s32(shift); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + } + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + pOutputSamples[i*8+0] = (drflac_int32)tempL0; + pOutputSamples[i*8+1] = (drflac_int32)tempR0; + pOutputSamples[i*8+2] = (drflac_int32)tempL1; + pOutputSamples[i*8+3] = (drflac_int32)tempR1; + pOutputSamples[i*8+4] = (drflac_int32)tempL2; + pOutputSamples[i*8+5] = (drflac_int32)tempR2; + pOutputSamples[i*8+6] = (drflac_int32)tempL3; + pOutputSamples[i*8+7] = (drflac_int32)tempR3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + int32x4_t shift4_0 = vdupq_n_s32(shift0); + int32x4_t shift4_1 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); + + drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) +{ + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; + + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + framesRead = 0; + while (framesToRead > 0) { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; + + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + + if (channelCount == 2) { + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + /* Generic interleaving. */ + drflac_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + } + } + } + + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + } + } + + return framesRead; +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)left0; + pOutputSamples[i*8+1] = (drflac_int16)right0; + pOutputSamples[i*8+2] = (drflac_int16)left1; + pOutputSamples[i*8+3] = (drflac_int16)right1; + pOutputSamples[i*8+4] = (drflac_int16)left2; + pOutputSamples[i*8+5] = (drflac_int16)right2; + pOutputSamples[i*8+6] = (drflac_int16)left3; + pOutputSamples[i*8+7] = (drflac_int16)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)left0; + pOutputSamples[i*8+1] = (drflac_int16)right0; + pOutputSamples[i*8+2] = (drflac_int16)left1; + pOutputSamples[i*8+3] = (drflac_int16)right1; + pOutputSamples[i*8+4] = (drflac_int16)left2; + pOutputSamples[i*8+5] = (drflac_int16)right2; + pOutputSamples[i*8+6] = (drflac_int16)left3; + pOutputSamples[i*8+7] = (drflac_int16)right3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + + drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + left >>= 16; + right >>= 16; + + pOutputSamples[i*2+0] = (drflac_int16)left; + pOutputSamples[i*2+1] = (drflac_int16)right; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)temp0L; + pOutputSamples[i*8+1] = (drflac_int16)temp0R; + pOutputSamples[i*8+2] = (drflac_int16)temp1L; + pOutputSamples[i*8+3] = (drflac_int16)temp1R; + pOutputSamples[i*8+4] = (drflac_int16)temp2L; + pOutputSamples[i*8+5] = (drflac_int16)temp2R; + pOutputSamples[i*8+6] = (drflac_int16)temp3L; + pOutputSamples[i*8+7] = (drflac_int16)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = ((drflac_int32)(mid0 + side0) >> 1); + temp1L = ((drflac_int32)(mid1 + side1) >> 1); + temp2L = ((drflac_int32)(mid2 + side2) >> 1); + temp3L = ((drflac_int32)(mid3 + side3) >> 1); + + temp0R = ((drflac_int32)(mid0 - side0) >> 1); + temp1R = ((drflac_int32)(mid1 - side1) >> 1); + temp2R = ((drflac_int32)(mid2 - side2) >> 1); + temp3R = ((drflac_int32)(mid3 - side3) >> 1); + + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)temp0L; + pOutputSamples[i*8+1] = (drflac_int16)temp0R; + pOutputSamples[i*8+2] = (drflac_int16)temp1L; + pOutputSamples[i*8+3] = (drflac_int16)temp1R; + pOutputSamples[i*8+4] = (drflac_int16)temp2L; + pOutputSamples[i*8+5] = (drflac_int16)temp2R; + pOutputSamples[i*8+6] = (drflac_int16)temp3L; + pOutputSamples[i*8+7] = (drflac_int16)temp3R; + } + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */ + int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */ + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + } + } else { + int32x4_t shift4; + + shift -= 1; + shift4 = vdupq_n_s32(shift); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + tempL0 >>= 16; + tempL1 >>= 16; + tempL2 >>= 16; + tempL3 >>= 16; + + tempR0 >>= 16; + tempR1 >>= 16; + tempR2 >>= 16; + tempR3 >>= 16; + + pOutputSamples[i*8+0] = (drflac_int16)tempL0; + pOutputSamples[i*8+1] = (drflac_int16)tempR0; + pOutputSamples[i*8+2] = (drflac_int16)tempL1; + pOutputSamples[i*8+3] = (drflac_int16)tempR1; + pOutputSamples[i*8+4] = (drflac_int16)tempL2; + pOutputSamples[i*8+5] = (drflac_int16)tempR2; + pOutputSamples[i*8+6] = (drflac_int16)tempL3; + pOutputSamples[i*8+7] = (drflac_int16)tempR3; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + + /* At this point we have results. We can now pack and interleave these into a single __m128i object and then store the in the output buffer. */ + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + + drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) +{ + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; + + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + framesRead = 0; + while (framesToRead > 0) { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; + + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + + if (channelCount == 2) { + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + /* Generic interleaving. */ + drflac_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); + } + } + } + + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + } + } + + return framesRead; +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + float factor = 1 / 2147483648.0; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 right0 = left0 - side0; + drflac_uint32 right1 = left1 - side1; + drflac_uint32 right2 = left2 - side2; + drflac_uint32 right3 = left3 - side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = _mm_set1_ps(1.0f / 8388608.0f); + + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + float32x4_t leftf; + float32x4_t rightf; + + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 left = pInputSamples0U32[i] << shift0; + drflac_uint32 side = pInputSamples1U32[i] << shift1; + drflac_uint32 right = left - side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + for (i = 0; i < frameCount; ++i) { + drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + + drflac_uint32 left0 = right0 + side0; + drflac_uint32 left1 = right1 + side1; + drflac_uint32 left2 = right2 + side2; + drflac_uint32 left3 = right3 + side3; + + pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left * factor; + pOutputSamples[i*2+1] = (drflac_int32)right * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = _mm_set1_ps(1.0f / 8388608.0f); + + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + float32x4_t leftf; + float32x4_t rightf; + + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 side = pInputSamples0U32[i] << shift0; + drflac_uint32 right = pInputSamples1U32[i] << shift1; + drflac_uint32 left = right + side; + + pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample; + float factor = 1 / 2147483648.0; + + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + } + } else { + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 temp0L; + drflac_uint32 temp1L; + drflac_uint32 temp2L; + drflac_uint32 temp3L; + drflac_uint32 temp0R; + drflac_uint32 temp1R; + drflac_uint32 temp2R; + drflac_uint32 temp3R; + + drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + + drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + + temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); + temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); + temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); + temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); + + temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); + temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); + temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); + temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); + + pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; + pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; + pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; + pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; + pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; + pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; + pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; + pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + } + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; + float factor; + __m128 factor128; + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = 1.0f / 8388608.0f; + factor128 = _mm_set1_ps(factor); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + + tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + } + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift = unusedBitsPerSample - 8; + float factor; + float32x4_t factor4; + int32x4_t shift4; + int32x4_t wbps0_4; /* Wasted Bits Per Sample */ + int32x4_t wbps1_4; /* Wasted Bits Per Sample */ + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + + factor = 1.0f / 8388608.0f; + factor4 = vdupq_n_f32(factor); + wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + + uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + + lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + + mid = (mid << 1) | (side & 0x01); + + pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + } + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + +#if 0 +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + for (drflac_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + + for (i = 0; i < frameCount4; ++i) { + drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + + drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + + pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} + +#if defined(DRFLAC_SUPPORT_SSE2) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + + float factor = 1.0f / 8388608.0f; + __m128 factor128 = _mm_set1_ps(factor); + + for (i = 0; i < frameCount4; ++i) { + __m128i lefti; + __m128i righti; + __m128 leftf; + __m128 rightf; + + lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + + leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); + + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif + +#if defined(DRFLAC_SUPPORT_NEON) +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ + drflac_uint64 i; + drflac_uint64 frameCount4 = frameCount >> 2; + const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; + const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; + drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + + float factor = 1.0f / 8388608.0f; + float32x4_t factor4 = vdupq_n_f32(factor); + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + + lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + + drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif + +static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(DRFLAC_SUPPORT_SSE2) + if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(DRFLAC_SUPPORT_NEON) + if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { + /* Scalar fallback. */ +#if 0 + drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} + +DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) +{ + drflac_uint64 framesRead; + drflac_uint32 unusedBitsPerSample; + + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + + if (pBufferOut == NULL) { + return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + + DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + framesRead = 0; + while (framesToRead > 0) { + /* If we've run out of samples in this frame, go to the next. */ + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + break; /* Couldn't read the next frame, so just break from the loop and return. */ + } + } else { + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + drflac_uint64 frameCountThisIteration = framesToRead; + + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + + if (channelCount == 2) { + const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + /* Generic interleaving. */ + drflac_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); + } + } + } + + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; + } + } + + return framesRead; +} + + +DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +{ + if (pFlac == NULL) { + return DRFLAC_FALSE; + } + + /* Don't do anything if we're already on the seek point. */ + if (pFlac->currentPCMFrame == pcmFrameIndex) { + return DRFLAC_TRUE; + } + + /* + If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present + when the decoder was opened. + */ + if (pFlac->firstFLACFramePosInBytes == 0) { + return DRFLAC_FALSE; + } + + if (pcmFrameIndex == 0) { + pFlac->currentPCMFrame = 0; + return drflac__seek_to_first_frame(pFlac); + } else { + drflac_bool32 wasSuccessful = DRFLAC_FALSE; + drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; + + /* Clamp the sample to the end. */ + if (pcmFrameIndex > pFlac->totalPCMFrameCount) { + pcmFrameIndex = pFlac->totalPCMFrameCount; + } + + /* If the target sample and the current sample are in the same frame we just move the position forward. */ + if (pcmFrameIndex > pFlac->currentPCMFrame) { + /* Forward. */ + drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { + pFlac->currentFLACFrame.pcmFramesRemaining -= offset; + pFlac->currentPCMFrame = pcmFrameIndex; + return DRFLAC_TRUE; + } + } else { + /* Backward. */ + drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + if (currentFLACFramePCMFramesConsumed > offsetAbs) { + pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; + pFlac->currentPCMFrame = pcmFrameIndex; + return DRFLAC_TRUE; + } + } + + /* + Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + we'll instead use Ogg's natural seeking facility. + */ +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + } + else +#endif + { + /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */ + if (/*!wasSuccessful && */!pFlac->_noSeekTableSeek) { + wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + } + +#if !defined(DR_FLAC_NO_CRC) + /* Fall back to binary search if seek table seeking fails. This requires the length of the stream to be known. */ + if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { + wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + } +#endif + + /* Fall back to brute force if all else fails. */ + if (!wasSuccessful && !pFlac->_noBruteForceSeek) { + wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + } + } + + if (wasSuccessful) { + pFlac->currentPCMFrame = pcmFrameIndex; + } else { + /* Seek failed. Try putting the decoder back to it's original state. */ + if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { + /* Failed to seek back to the original PCM frame. Fall back to 0. */ + drflac_seek_to_pcm_frame(pFlac, 0); + } + } + + return wasSuccessful; + } +} + + + +/* High Level APIs */ + +/* SIZE_MAX */ +#if defined(SIZE_MAX) + #define DRFLAC_SIZE_MAX SIZE_MAX +#else + #if defined(DRFLAC_64BIT) + #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRFLAC_SIZE_MAX 0xFFFFFFFF + #endif +#endif +/* End SIZE_MAX */ + + +/* Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. */ +#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ +{ \ + type* pSampleData = NULL; \ + drflac_uint64 totalPCMFrameCount; \ + \ + DRFLAC_ASSERT(pFlac != NULL); \ + \ + totalPCMFrameCount = pFlac->totalPCMFrameCount; \ + \ + if (totalPCMFrameCount == 0) { \ + type buffer[4096]; \ + drflac_uint64 pcmFramesRead; \ + size_t sampleDataBufferSize = sizeof(buffer); \ + \ + pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ + type* pNewSampleData; \ + size_t newSampleDataBufferSize; \ + \ + newSampleDataBufferSize = sampleDataBufferSize * 2; \ + pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pNewSampleData == NULL) { \ + drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + goto on_error; \ + } \ + \ + sampleDataBufferSize = newSampleDataBufferSize; \ + pSampleData = pNewSampleData; \ + } \ + \ + DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + totalPCMFrameCount += pcmFramesRead; \ + } \ + \ + /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ + protect those ears from random noise! */ \ + DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + } else { \ + drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ + goto on_error; /* The decoded data is too big. */ \ + } \ + \ + pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ + \ + drflac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + drflac_close(pFlac); \ + return NULL; \ +} + +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) +DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) + +DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} + +DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} + +DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + + pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} + +#ifndef DR_FLAC_NO_STDIO +DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +#endif + +DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} + +DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + drflac* pFlac; + + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + + pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} + + +DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + drflac__free_from_callbacks(p, pAllocationCallbacks); + } else { + drflac__free_default(p, NULL); + } +} + + + + +DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = commentCount; + pIter->pRunningData = (const char*)pComments; +} + +DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +{ + drflac_int32 length; + const char* pComment; + + /* Safety. */ + if (pCommentLengthOut) { + *pCommentLengthOut = 0; + } + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + + length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); + pIter->pRunningData += 4; + + pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + + if (pCommentLengthOut) { + *pCommentLengthOut = length; + } + + return pComment; +} + + + + +DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = trackCount; + pIter->pRunningData = (const char*)pTrackData; +} + +DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) +{ + drflac_cuesheet_track cuesheetTrack; + const char* pRunningData; + drflac_uint64 offsetHi; + drflac_uint64 offsetLo; + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return DRFLAC_FALSE; + } + + pRunningData = pIter->pRunningData; + + offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + cuesheetTrack.offset = offsetLo | (offsetHi << 32); + cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; + DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; + cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; + cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; + cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); + + pIter->pRunningData = pRunningData; + pIter->countRemaining -= 1; + + if (pCuesheetTrack) { + *pCuesheetTrack = cuesheetTrack; + } + + return DRFLAC_TRUE; +} + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif +#endif /* dr_flac_c */ +#endif /* DR_FLAC_IMPLEMENTATION */ + + +/* +REVISION HISTORY +================ +v0.12.44 - TBD + - Fix compilation for AIX OS. + +v0.12.43 - 2024-12-17 + - Fix a possible buffer overflow during decoding. + - Improve detection of ARM64EC + +v0.12.42 - 2023-11-02 + - Fix build for ARMv6-M. + - Fix a compilation warning with GCC. + +v0.12.41 - 2023-06-17 + - Fix an incorrect date in revision history. No functional change. + +v0.12.40 - 2023-05-22 + - Minor code restructure. No functional change. + +v0.12.39 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation error with Visual Studio 2019 and the ARM build. + - Fix an error with SSE 4.1 detection. + - Add support for disabling wchar_t with DR_WAV_NO_WCHAR. + - Improve compatibility with compilers which lack support for explicit struct packing. + - Improve compatibility with low-end and embedded hardware by reducing the amount of stack + allocation when loading an Ogg encapsulated file. + +v0.12.38 - 2022-04-10 + - Fix compilation error on older versions of GCC. + +v0.12.37 - 2022-02-12 + - Improve ARM detection. + +v0.12.36 - 2022-02-07 + - Fix a compilation error with the ARM build. + +v0.12.35 - 2022-02-06 + - Fix a bug due to underestimating the amount of precision required for the prediction stage. + - Fix some bugs found from fuzz testing. + +v0.12.34 - 2022-01-07 + - Fix some misalignment bugs when reading metadata. + +v0.12.33 - 2021-12-22 + - Fix a bug with seeking when the seek table does not start at PCM frame 0. + +v0.12.32 - 2021-12-11 + - Fix a warning with Clang. + +v0.12.31 - 2021-08-16 + - Silence some warnings. + +v0.12.30 - 2021-07-31 + - Fix platform detection for ARM64. + +v0.12.29 - 2021-04-02 + - Fix a bug where the running PCM frame index is set to an invalid value when over-seeking. + - Fix a decoding error due to an incorrect validation check. + +v0.12.28 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + +v0.12.27 - 2021-01-31 + - Fix a static analysis warning. + +v0.12.26 - 2021-01-17 + - Fix a compilation warning due to _BSD_SOURCE being deprecated. + +v0.12.25 - 2020-12-26 + - Update documentation. + +v0.12.24 - 2020-11-29 + - Fix ARM64/NEON detection when compiling with MSVC. + +v0.12.23 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.12.22 - 2020-11-01 + - Fix an error with the previous release. + +v0.12.21 - 2020-11-01 + - Fix a possible deadlock when seeking. + - Improve compiler support for older versions of GCC. + +v0.12.20 - 2020-09-08 + - Fix a compilation error on older compilers. + +v0.12.19 - 2020-08-30 + - Fix a bug due to an undefined 32-bit shift. + +v0.12.18 - 2020-08-14 + - Fix a crash when compiling with clang-cl. + +v0.12.17 - 2020-08-02 + - Simplify sized types. + +v0.12.16 - 2020-07-25 + - Fix a compilation warning. + +v0.12.15 - 2020-07-06 + - Check for negative LPC shifts and return an error. + +v0.12.14 - 2020-06-23 + - Add include guard for the implementation section. + +v0.12.13 - 2020-05-16 + - Add compile-time and run-time version querying. + - DRFLAC_VERSION_MINOR + - DRFLAC_VERSION_MAJOR + - DRFLAC_VERSION_REVISION + - DRFLAC_VERSION_STRING + - drflac_version() + - drflac_version_string() + +v0.12.12 - 2020-04-30 + - Fix compilation errors with VC6. + +v0.12.11 - 2020-04-19 + - Fix some pedantic warnings. + - Fix some undefined behaviour warnings. + +v0.12.10 - 2020-04-10 + - Fix some bugs when trying to seek with an invalid seek table. + +v0.12.9 - 2020-04-05 + - Fix warnings. + +v0.12.8 - 2020-04-04 + - Add drflac_open_file_w() and drflac_open_file_with_metadata_w(). + - Fix some static analysis warnings. + - Minor documentation updates. + +v0.12.7 - 2020-03-14 + - Fix compilation errors with VC6. + +v0.12.6 - 2020-03-07 + - Fix compilation error with Visual Studio .NET 2003. + +v0.12.5 - 2020-01-30 + - Silence some static analysis warnings. + +v0.12.4 - 2020-01-29 + - Silence some static analysis warnings. + +v0.12.3 - 2019-12-02 + - Fix some warnings when compiling with GCC and the -Og flag. + - Fix a crash in out-of-memory situations. + - Fix potential integer overflow bug. + - Fix some static analysis warnings. + - Fix a possible crash when using custom memory allocators without a custom realloc() implementation. + - Fix a bug with binary search seeking where the bits per sample is not a multiple of 8. + +v0.12.2 - 2019-10-07 + - Internal code clean up. + +v0.12.1 - 2019-09-29 + - Fix some Clang Static Analyzer warnings. + - Fix an unused variable warning. + +v0.12.0 - 2019-09-23 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation + routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: + - drflac_open() + - drflac_open_relaxed() + - drflac_open_with_metadata() + - drflac_open_with_metadata_relaxed() + - drflac_open_file() + - drflac_open_file_with_metadata() + - drflac_open_memory() + - drflac_open_memory_with_metadata() + - drflac_open_and_read_pcm_frames_s32() + - drflac_open_and_read_pcm_frames_s16() + - drflac_open_and_read_pcm_frames_f32() + - drflac_open_file_and_read_pcm_frames_s32() + - drflac_open_file_and_read_pcm_frames_s16() + - drflac_open_file_and_read_pcm_frames_f32() + - drflac_open_memory_and_read_pcm_frames_s32() + - drflac_open_memory_and_read_pcm_frames_s16() + - drflac_open_memory_and_read_pcm_frames_f32() + Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use + DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE. + - Remove deprecated APIs: + - drflac_read_s32() + - drflac_read_s16() + - drflac_read_f32() + - drflac_seek_to_sample() + - drflac_open_and_decode_s32() + - drflac_open_and_decode_s16() + - drflac_open_and_decode_f32() + - drflac_open_and_decode_file_s32() + - drflac_open_and_decode_file_s16() + - drflac_open_and_decode_file_f32() + - drflac_open_and_decode_memory_s32() + - drflac_open_and_decode_memory_s16() + - drflac_open_and_decode_memory_f32() + - Remove drflac.totalSampleCount which is now replaced with drflac.totalPCMFrameCount. You can emulate drflac.totalSampleCount + by doing pFlac->totalPCMFrameCount*pFlac->channels. + - Rename drflac.currentFrame to drflac.currentFLACFrame to remove ambiguity with PCM frames. + - Fix errors when seeking to the end of a stream. + - Optimizations to seeking. + - SSE improvements and optimizations. + - ARM NEON optimizations. + - Optimizations to drflac_read_pcm_frames_s16(). + - Optimizations to drflac_read_pcm_frames_s32(). + +v0.11.10 - 2019-06-26 + - Fix a compiler error. + +v0.11.9 - 2019-06-16 + - Silence some ThreadSanitizer warnings. + +v0.11.8 - 2019-05-21 + - Fix warnings. + +v0.11.7 - 2019-05-06 + - C89 fixes. + +v0.11.6 - 2019-05-05 + - Add support for C89. + - Fix a compiler warning when CRC is disabled. + - Change license to choice of public domain or MIT-0. + +v0.11.5 - 2019-04-19 + - Fix a compiler error with GCC. + +v0.11.4 - 2019-04-17 + - Fix some warnings with GCC when compiling with -std=c99. + +v0.11.3 - 2019-04-07 + - Silence warnings with GCC. + +v0.11.2 - 2019-03-10 + - Fix a warning. + +v0.11.1 - 2019-02-17 + - Fix a potential bug with seeking. + +v0.11.0 - 2018-12-16 + - API CHANGE: Deprecated drflac_read_s32(), drflac_read_s16() and drflac_read_f32() and replaced them with + drflac_read_pcm_frames_s32(), drflac_read_pcm_frames_s16() and drflac_read_pcm_frames_f32(). The new APIs take + and return PCM frame counts instead of sample counts. To upgrade you will need to change the input count by + dividing it by the channel count, and then do the same with the return value. + - API_CHANGE: Deprecated drflac_seek_to_sample() and replaced with drflac_seek_to_pcm_frame(). Same rules as + the changes to drflac_read_*() apply. + - API CHANGE: Deprecated drflac_open_and_decode_*() and replaced with drflac_open_*_and_read_*(). Same rules as + the changes to drflac_read_*() apply. + - Optimizations. + +v0.10.0 - 2018-09-11 + - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you + need to do it yourself via the callback API. + - Fix the clang build. + - Fix undefined behavior. + - Fix errors with CUESHEET metdata blocks. + - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the + Vorbis comment API. + - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams. + - Minor optimizations. + +v0.9.11 - 2018-08-29 + - Fix a bug with sample reconstruction. + +v0.9.10 - 2018-08-07 + - Improve 64-bit detection. + +v0.9.9 - 2018-08-05 + - Fix C++ build on older versions of GCC. + +v0.9.8 - 2018-07-24 + - Fix compilation errors. + +v0.9.7 - 2018-07-05 + - Fix a warning. + +v0.9.6 - 2018-06-29 + - Fix some typos. + +v0.9.5 - 2018-06-23 + - Fix some warnings. + +v0.9.4 - 2018-06-14 + - Optimizations to seeking. + - Clean up. + +v0.9.3 - 2018-05-22 + - Bug fix. + +v0.9.2 - 2018-05-12 + - Fix a compilation error due to a missing break statement. + +v0.9.1 - 2018-04-29 + - Fix compilation error with Clang. + +v0.9 - 2018-04-24 + - Fix Clang build. + - Start using major.minor.revision versioning. + +v0.8g - 2018-04-19 + - Fix build on non-x86/x64 architectures. + +v0.8f - 2018-02-02 + - Stop pretending to support changing rate/channels mid stream. + +v0.8e - 2018-02-01 + - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream. + - Fix a crash the the Rice partition order is invalid. + +v0.8d - 2017-09-22 + - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. + +v0.8c - 2017-09-07 + - Fix warning on non-x86/x64 architectures. + +v0.8b - 2017-08-19 + - Fix build on non-x86/x64 architectures. + +v0.8a - 2017-08-13 + - A small optimization for the Clang build. + +v0.8 - 2017-08-12 + - API CHANGE: Rename dr_* types to drflac_*. + - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation. + - Add support for custom implementations of malloc(), realloc(), etc. + - Add CRC checking to Ogg encapsulated streams. + - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported. + - Bug fixes. + +v0.7 - 2017-07-23 + - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed(). + +v0.6 - 2017-07-22 + - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they + never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame. + +v0.5 - 2017-07-16 + - Fix typos. + - Change drflac_bool* types to unsigned. + - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC. + +v0.4f - 2017-03-10 + - Fix a couple of bugs with the bitstreaming code. + +v0.4e - 2017-02-17 + - Fix some warnings. + +v0.4d - 2016-12-26 + - Add support for 32-bit floating-point PCM decoding. + - Use drflac_int* and drflac_uint* sized types to improve compiler support. + - Minor improvements to documentation. + +v0.4c - 2016-12-26 + - Add support for signed 16-bit integer PCM decoding. + +v0.4b - 2016-10-23 + - A minor change to drflac_bool8 and drflac_bool32 types. + +v0.4a - 2016-10-11 + - Rename drBool32 to drflac_bool32 for styling consistency. + +v0.4 - 2016-09-29 + - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type. + - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32(). + - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to + keep it consistent with drflac_audio. + +v0.3f - 2016-09-21 + - Fix a warning with GCC. + +v0.3e - 2016-09-18 + - Fixed a bug where GCC 4.3+ was not getting properly identified. + - Fixed a few typos. + - Changed date formats to ISO 8601 (YYYY-MM-DD). + +v0.3d - 2016-06-11 + - Minor clean up. + +v0.3c - 2016-05-28 + - Fixed compilation error. + +v0.3b - 2016-05-16 + - Fixed Linux/GCC build. + - Updated documentation. + +v0.3a - 2016-05-15 + - Minor fixes to documentation. + +v0.3 - 2016-05-11 + - Optimizations. Now at about parity with the reference implementation on 32-bit builds. + - Lots of clean up. + +v0.2b - 2016-05-10 + - Bug fixes. + +v0.2a - 2016-05-10 + - Made drflac_open_and_decode() more robust. + - Removed an unused debugging variable + +v0.2 - 2016-05-09 + - Added support for Ogg encapsulation. + - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek + should be relative to the start or the current position. Also changes the seeking rules such that + seeking offsets will never be negative. + - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count. + +v0.1b - 2016-05-07 + - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize. + - Removed a stale comment. + +v0.1a - 2016-05-05 + - Minor formatting changes. + - Fixed a warning on the GCC build. + +v0.1 - 2016-05-03 + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <http://unlicense.org/> + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/deps/dr_libs/src/dr_impl.c b/deps/dr_libs/src/dr_impl.c index a59b7ec..e73b028 100644 --- a/deps/dr_libs/src/dr_impl.c +++ b/deps/dr_libs/src/dr_impl.c @@ -1,3 +1,6 @@ +#define DR_FLAC_IMPLEMENTATION +#include <dr_flac.h> + #define DR_MP3_IMPLEMENTATION #include <dr_mp3.h> diff --git a/src/game/CMakeLists.txt b/game/CMakeLists.txt index c6c32bf..24e5c2d 100644 --- a/src/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -1,3 +1,7 @@ +if(BUILD_VCLIENT OR BUILD_VSERVER) + add_subdirectory(shared) +endif() + if(BUILD_VCLIENT) add_subdirectory(client) endif() @@ -5,7 +9,3 @@ endif() if(BUILD_VSERVER) add_subdirectory(server) endif() - -if(BUILD_VCLIENT OR BUILD_VSERVER) - add_subdirectory(shared) -endif() diff --git a/game/client/CMakeLists.txt b/game/client/CMakeLists.txt new file mode 100644 index 0000000..b40dbb1 --- /dev/null +++ b/game/client/CMakeLists.txt @@ -0,0 +1,47 @@ +add_executable(vclient + "${CMAKE_CURRENT_LIST_DIR}/const.hh" + "${CMAKE_CURRENT_LIST_DIR}/experiments.cc" + "${CMAKE_CURRENT_LIST_DIR}/experiments.hh" + "${CMAKE_CURRENT_LIST_DIR}/game.cc" + "${CMAKE_CURRENT_LIST_DIR}/game.hh" + "${CMAKE_CURRENT_LIST_DIR}/globals.cc" + "${CMAKE_CURRENT_LIST_DIR}/globals.hh" + "${CMAKE_CURRENT_LIST_DIR}/main.cc" + "${CMAKE_CURRENT_LIST_DIR}/pch.hh" + "${CMAKE_CURRENT_LIST_DIR}/program.cc" + "${CMAKE_CURRENT_LIST_DIR}/program.hh" + "${CMAKE_CURRENT_LIST_DIR}/receive.cc" + "${CMAKE_CURRENT_LIST_DIR}/receive.hh" + "${CMAKE_CURRENT_LIST_DIR}/screenshot.cc" + "${CMAKE_CURRENT_LIST_DIR}/screenshot.hh" + "${CMAKE_CURRENT_LIST_DIR}/session.cc" + "${CMAKE_CURRENT_LIST_DIR}/session.hh" + "${CMAKE_CURRENT_LIST_DIR}/toggles.cc" + "${CMAKE_CURRENT_LIST_DIR}/toggles.hh") +target_compile_features(vclient PUBLIC cxx_std_20) +target_compile_definitions(vclient PUBLIC GLFW_INCLUDE_NONE) +target_include_directories(vclient PRIVATE "${PROJECT_SOURCE_DIR}") +target_include_directories(vclient PRIVATE "${PROJECT_SOURCE_DIR}/game") +target_precompile_headers(vclient PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") +target_link_libraries(vclient PUBLIC shared dr_libs glad glfw3 imgui imgui_glfw imgui_opengl3 salad) + +add_subdirectory(config) +add_subdirectory(entity) +add_subdirectory(gui) +add_subdirectory(io) +add_subdirectory(resource) +add_subdirectory(sound) +add_subdirectory(world) + +if(WIN32 AND MSVC) + # GLFW defines APIENTRY and ENet includes + # Windows API headers which also define APIENTRY + target_compile_options(vclient PRIVATE /wd4005) +endif() + +if(WIN32) + enable_language(RC) + target_sources(vclient PRIVATE "${CMAKE_CURRENT_LIST_DIR}/vclient.rc") +endif() + +install(TARGETS vclient RUNTIME DESTINATION ".") diff --git a/game/client/config/CMakeLists.txt b/game/client/config/CMakeLists.txt new file mode 100644 index 0000000..0536160 --- /dev/null +++ b/game/client/config/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/gamepad_axis.cc" + "${CMAKE_CURRENT_LIST_DIR}/gamepad_axis.hh" + "${CMAKE_CURRENT_LIST_DIR}/gamepad_button.cc" + "${CMAKE_CURRENT_LIST_DIR}/gamepad_button.hh" + "${CMAKE_CURRENT_LIST_DIR}/keybind.cc" + "${CMAKE_CURRENT_LIST_DIR}/keybind.hh") diff --git a/src/game/client/gamepad_axis.cc b/game/client/config/gamepad_axis.cc index 546c647..a82de81 100644 --- a/src/game/client/gamepad_axis.cc +++ b/game/client/config/gamepad_axis.cc @@ -1,10 +1,10 @@ #include "client/pch.hh" -#include "client/gamepad_axis.hh" +#include "client/config/gamepad_axis.hh" -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" -#include "client/gamepad.hh" +#include "client/io/gamepad.hh" constexpr static const char* UNKNOWN_AXIS_NAME = "UNKNOWN"; @@ -30,11 +30,11 @@ static const char* get_axis_name(int axis) return UNKNOWN_AXIS_NAME; } -ConfigGamepadAxis::ConfigGamepadAxis(void) : ConfigGamepadAxis(INVALID_GAMEPAD_AXIS, false) +config::GamepadAxis::GamepadAxis(void) : GamepadAxis(io::INVALID_GAMEPAD_AXIS, false) { } -ConfigGamepadAxis::ConfigGamepadAxis(int axis, bool inverted) +config::GamepadAxis::GamepadAxis(int axis, bool inverted) { m_inverted = inverted; m_gamepad_axis = axis; @@ -42,12 +42,12 @@ ConfigGamepadAxis::ConfigGamepadAxis(int axis, bool inverted) m_full_string = std::format("{}:{}", m_name, m_inverted ? 1U : 0U); } -const char* ConfigGamepadAxis::get(void) const +const char* config::GamepadAxis::get(void) const { return m_full_string.c_str(); } -void ConfigGamepadAxis::set(const char* value) +void config::GamepadAxis::set(const char* value) { char new_name[64]; unsigned int new_invert; @@ -65,40 +65,40 @@ void ConfigGamepadAxis::set(const char* value) } m_inverted = false; - m_gamepad_axis = INVALID_GAMEPAD_AXIS; + m_gamepad_axis = io::INVALID_GAMEPAD_AXIS; m_name = UNKNOWN_AXIS_NAME; m_full_string = std::format("{}:{}", m_name, m_inverted ? 1U : 0U); } -int ConfigGamepadAxis::get_axis(void) const +int config::GamepadAxis::get_axis(void) const { return m_gamepad_axis; } -void ConfigGamepadAxis::set_axis(int axis) +void config::GamepadAxis::set_axis(int axis) { m_gamepad_axis = axis; m_name = get_axis_name(axis); m_full_string = std::format("{}:{}", m_name, m_inverted ? 1U : 0U); } -bool ConfigGamepadAxis::is_inverted(void) const +bool config::GamepadAxis::is_inverted(void) const { return m_inverted; } -void ConfigGamepadAxis::set_inverted(bool inverted) +void config::GamepadAxis::set_inverted(bool inverted) { m_inverted = inverted; m_full_string = std::format("{}:{}", m_name, m_inverted ? 1U : 0U); } -float ConfigGamepadAxis::get_value(const GLFWgamepadstate& state, float deadzone) const +float config::GamepadAxis::get_value(const GLFWgamepadstate& state, float deadzone) const { - if(m_gamepad_axis <= vx::array_size(state.axes)) { + if(m_gamepad_axis <= math::array_size(state.axes)) { auto value = state.axes[m_gamepad_axis]; - if(vx::abs(value) > deadzone) { + if(math::abs(value) > deadzone) { return m_inverted ? -value : value; } @@ -108,7 +108,7 @@ float ConfigGamepadAxis::get_value(const GLFWgamepadstate& state, float deadzone return 0.0f; } -const char* ConfigGamepadAxis::get_name(void) const +const char* config::GamepadAxis::get_name(void) const { return m_name; } diff --git a/src/game/client/gamepad_axis.hh b/game/client/config/gamepad_axis.hh index 6a1f761..0308ce6 100644 --- a/src/game/client/gamepad_axis.hh +++ b/game/client/config/gamepad_axis.hh @@ -2,15 +2,17 @@ #define CLIENT_GAMEPAD_AXIS_HH 1 #pragma once -#include "core/config.hh" +#include "core/config/ivalue.hh" struct GLFWgamepadstate; -class ConfigGamepadAxis final : public IConfigValue { +namespace config +{ +class GamepadAxis final : public IValue { public: - explicit ConfigGamepadAxis(void); - explicit ConfigGamepadAxis(int axis, bool inverted); - virtual ~ConfigGamepadAxis(void) = default; + explicit GamepadAxis(void); + explicit GamepadAxis(int axis, bool inverted); + virtual ~GamepadAxis(void) = default; virtual const char* get(void) const override; virtual void set(const char* value) override; @@ -35,5 +37,6 @@ private: std::string m_full_string; const char* m_name; }; +} // namespace config #endif // CLIENT_GAMEPAD_AXIS_HH diff --git a/src/game/client/gamepad_button.cc b/game/client/config/gamepad_button.cc index dd3dca7..b983baa 100644 --- a/src/game/client/gamepad_button.cc +++ b/game/client/config/gamepad_button.cc @@ -1,10 +1,10 @@ #include "client/pch.hh" -#include "client/gamepad_button.hh" +#include "client/config/gamepad_button.hh" -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" -#include "client/gamepad.hh" +#include "client/io/gamepad.hh" constexpr static const char* UNKNOWN_BUTTON_NAME = "UNKNOWN"; @@ -37,24 +37,24 @@ static const char* get_button_name(int button) return UNKNOWN_BUTTON_NAME; } -ConfigGamepadButton::ConfigGamepadButton(void) +config::GamepadButton::GamepadButton(void) { - m_gamepad_button = INVALID_GAMEPAD_BUTTON; + m_gamepad_button = io::INVALID_GAMEPAD_BUTTON; m_name = UNKNOWN_BUTTON_NAME; } -ConfigGamepadButton::ConfigGamepadButton(int button) +config::GamepadButton::GamepadButton(int button) { m_gamepad_button = button; m_name = get_button_name(button); } -const char* ConfigGamepadButton::get(void) const +const char* config::GamepadButton::get(void) const { return m_name; } -void ConfigGamepadButton::set(const char* value) +void config::GamepadButton::set(const char* value) { for(const auto& it : button_names) { if(!std::strcmp(it.second, value)) { @@ -64,27 +64,27 @@ void ConfigGamepadButton::set(const char* value) } } - m_gamepad_button = INVALID_GAMEPAD_BUTTON; + m_gamepad_button = io::INVALID_GAMEPAD_BUTTON; m_name = UNKNOWN_BUTTON_NAME; } -int ConfigGamepadButton::get_button(void) const +int config::GamepadButton::get_button(void) const { return m_gamepad_button; } -void ConfigGamepadButton::set_button(int button) +void config::GamepadButton::set_button(int button) { m_gamepad_button = button; m_name = get_button_name(button); } -bool ConfigGamepadButton::equals(int button) const +bool config::GamepadButton::equals(int button) const { return m_gamepad_button == button; } -bool ConfigGamepadButton::is_pressed(const GLFWgamepadstate& state) const +bool config::GamepadButton::is_pressed(const GLFWgamepadstate& state) const { - return m_gamepad_button < vx::array_size(state.buttons) && state.buttons[m_gamepad_button] == GLFW_PRESS; + return m_gamepad_button < math::array_size(state.buttons) && state.buttons[m_gamepad_button] == GLFW_PRESS; } diff --git a/src/game/client/gamepad_button.hh b/game/client/config/gamepad_button.hh index 079988e..11566c1 100644 --- a/src/game/client/gamepad_button.hh +++ b/game/client/config/gamepad_button.hh @@ -2,15 +2,17 @@ #define CLIENT_GAMEPAD_BUTTON_HH 1 #pragma once -#include "core/config.hh" +#include "core/config/ivalue.hh" struct GLFWgamepadstate; -class ConfigGamepadButton final : public IConfigValue { +namespace config +{ +class GamepadButton final : public IValue { public: - explicit ConfigGamepadButton(void); - explicit ConfigGamepadButton(int button); - virtual ~ConfigGamepadButton(void) = default; + explicit GamepadButton(void); + explicit GamepadButton(int button); + virtual ~GamepadButton(void) = default; virtual const char* get(void) const override; virtual void set(const char* value) override; @@ -25,5 +27,6 @@ private: int m_gamepad_button; const char* m_name; }; +} // namespace config #endif // CLIENT_GAMEPAD_BUTTON_HH diff --git a/src/game/client/keybind.cc b/game/client/config/keybind.cc index d47397d..1cf896c 100644 --- a/src/game/client/keybind.cc +++ b/game/client/config/keybind.cc @@ -1,8 +1,8 @@ #include "client/pch.hh" -#include "client/keybind.hh" +#include "client/config/keybind.hh" -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" #include "client/const.hh" @@ -142,13 +142,13 @@ static const char* get_key_name(int keycode) return UNKNOWN_KEY_NAME; } -ConfigKeyBind::ConfigKeyBind(void) +config::KeyBind::KeyBind(void) { m_glfw_keycode = GLFW_KEY_UNKNOWN; m_name = UNKNOWN_KEY_NAME; } -ConfigKeyBind::ConfigKeyBind(int default_value) +config::KeyBind::KeyBind(int default_value) { if(default_value == DEBUG_KEY) { m_glfw_keycode = GLFW_KEY_UNKNOWN; @@ -159,7 +159,7 @@ ConfigKeyBind::ConfigKeyBind(int default_value) } } -void ConfigKeyBind::set(const char* value) +void config::KeyBind::set(const char* value) { for(const auto& it : key_names) { if((it.first != DEBUG_KEY) && !std::strcmp(it.second, value)) { @@ -173,12 +173,12 @@ void ConfigKeyBind::set(const char* value) m_name = UNKNOWN_KEY_NAME; } -const char* ConfigKeyBind::get(void) const +const char* config::KeyBind::get(void) const { return m_name; } -void ConfigKeyBind::set_key(int keycode) +void config::KeyBind::set_key(int keycode) { if(keycode == DEBUG_KEY) { m_glfw_keycode = GLFW_KEY_UNKNOWN; @@ -189,12 +189,12 @@ void ConfigKeyBind::set_key(int keycode) } } -int ConfigKeyBind::get_key(void) const +int config::KeyBind::get_key(void) const { return m_glfw_keycode; } -bool ConfigKeyBind::equals(int keycode) const +bool config::KeyBind::equals(int keycode) const { return m_glfw_keycode == keycode; } diff --git a/src/game/client/keybind.hh b/game/client/config/keybind.hh index 1749357..abfb97a 100644 --- a/src/game/client/keybind.hh +++ b/game/client/config/keybind.hh @@ -2,13 +2,15 @@ #define CLIENT_KEYBIND_HH 1 #pragma once -#include "core/config.hh" +#include "core/config/ivalue.hh" -class ConfigKeyBind final : public IConfigValue { +namespace config +{ +class KeyBind final : public IValue { public: - explicit ConfigKeyBind(void); - explicit ConfigKeyBind(int default_value); - virtual ~ConfigKeyBind(void) = default; + explicit KeyBind(void); + explicit KeyBind(int default_value); + virtual ~KeyBind(void) = default; virtual void set(const char* value) override; virtual const char* get(void) const override; @@ -22,5 +24,6 @@ private: const char* m_name; int m_glfw_keycode; }; +} // namespace config #endif // CLIENT_KEYBIND_HH diff --git a/src/game/client/const.hh b/game/client/const.hh index c53fc1b..c53fc1b 100644 --- a/src/game/client/const.hh +++ b/game/client/const.hh diff --git a/game/client/entity/CMakeLists.txt b/game/client/entity/CMakeLists.txt new file mode 100644 index 0000000..4da10d6 --- /dev/null +++ b/game/client/entity/CMakeLists.txt @@ -0,0 +1,15 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/camera.cc" + "${CMAKE_CURRENT_LIST_DIR}/camera.hh" + "${CMAKE_CURRENT_LIST_DIR}/factory.cc" + "${CMAKE_CURRENT_LIST_DIR}/factory.hh" + "${CMAKE_CURRENT_LIST_DIR}/interpolation.cc" + "${CMAKE_CURRENT_LIST_DIR}/interpolation.hh" + "${CMAKE_CURRENT_LIST_DIR}/listener.cc" + "${CMAKE_CURRENT_LIST_DIR}/listener.hh" + "${CMAKE_CURRENT_LIST_DIR}/player_look.cc" + "${CMAKE_CURRENT_LIST_DIR}/player_look.hh" + "${CMAKE_CURRENT_LIST_DIR}/player_move.cc" + "${CMAKE_CURRENT_LIST_DIR}/player_move.hh" + "${CMAKE_CURRENT_LIST_DIR}/sound_emitter.cc" + "${CMAKE_CURRENT_LIST_DIR}/sound_emitter.hh") diff --git a/game/client/entity/camera.cc b/game/client/entity/camera.cc new file mode 100644 index 0000000..e0b4cb5 --- /dev/null +++ b/game/client/entity/camera.cc @@ -0,0 +1,110 @@ +#include "client/pch.hh" + +#include "client/entity/camera.hh" + +#include "core/config/number.hh" +#include "core/io/config_map.hh" +#include "core/math/angles.hh" + +#include "shared/entity/head.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + +#include "client/entity/player_move.hh" +#include "client/gui/settings.hh" + +#include "client/const.hh" +#include "client/globals.hh" +#include "client/session.hh" +#include "client/toggles.hh" + +config::Float entity::camera::roll_angle(2.0f, 0.0f, 4.0f); +config::Float entity::camera::vertical_fov(90.0f, 45.0f, 110.0f); +config::Unsigned entity::camera::view_distance(16U, 4U, 32U); + +glm::fvec3 entity::camera::angles; +glm::fvec3 entity::camera::direction; +glm::fmat4x4 entity::camera::matrix; +chunk_pos entity::camera::position_chunk; +glm::fvec3 entity::camera::position_local; + +static void reset_camera(void) +{ + entity::camera::angles = glm::fvec3(0.0f, 0.0f, 0.0f); + entity::camera::direction = DIR_FORWARD<float>; + entity::camera::matrix = glm::identity<glm::fmat4x4>(); + entity::camera::position_chunk = chunk_pos(0, 0, 0); + entity::camera::position_local = glm::fvec3(0.0f, 0.0f, 0.0f); +} + +// Gracefully contributed by PQCraft himself in 2024 +// making PlatinumSrc and Voxelius kind of related to each other +static glm::fmat4x4 platinumsrc_viewmatrix(const glm::fvec3& position, const glm::fvec3& angles) +{ + glm::fvec3 forward, up; + math::vectors(angles, &forward, nullptr, &up); + + auto result = glm::identity<glm::fmat4x4>(); + result[0][0] = forward.y * up.z - forward.z * up.y; + result[1][0] = forward.z * up.x - forward.x * up.z; + result[2][0] = forward.x * up.y - forward.y * up.x; + result[3][0] = -result[0][0] * position.x - result[1][0] * position.y - result[2][0] * position.z; + result[0][1] = up.x; + result[1][1] = up.y; + result[2][1] = up.z; + result[3][1] = -up.x * position.x - up.y * position.y - up.z * position.z; + result[0][2] = -forward.x; + result[1][2] = -forward.y; + result[2][2] = -forward.z; + result[3][2] = forward.x * position.x + forward.y * position.y + forward.z * position.z; + return result; +} + +void entity::camera::init(void) +{ + globals::client_config.add_value("camera.roll_angle", entity::camera::roll_angle); + globals::client_config.add_value("camera.vertical_fov", entity::camera::vertical_fov); + globals::client_config.add_value("camera.view_distance", entity::camera::view_distance); + + settings::add_slider(1, entity::camera::vertical_fov, settings_location::GENERAL, "camera.vertical_fov", true, "%.0f"); + settings::add_slider(0, entity::camera::view_distance, settings_location::VIDEO, "camera.view_distance", false); + settings::add_slider(10, entity::camera::roll_angle, settings_location::VIDEO, "camera.roll_angle", true, "%.01f"); + + reset_camera(); +} + +void entity::camera::update(void) +{ + if(!session::is_ingame()) { + reset_camera(); + return; + } + + const auto& head = globals::dimension->entities.get<entity::client::HeadIntr>(globals::player); + const auto& transform = globals::dimension->entities.get<entity::client::TransformIntr>(globals::player); + const auto& velocity = globals::dimension->entities.get<entity::Velocity>(globals::player); + + entity::camera::angles = transform.angles + head.angles; + entity::camera::position_chunk = transform.chunk; + entity::camera::position_local = transform.local + head.offset; + + glm::fvec3 right_vector, up_vector; + math::vectors(entity::camera::angles, &entity::camera::direction, &right_vector, &up_vector); + + auto client_angles = entity::camera::angles; + + if(!toggles::get(TOGGLE_PM_FLIGHT)) { + // Apply the quake-like view rolling + client_angles[2] = math::radians( + -entity::camera::roll_angle.get_value() * glm::dot(velocity.value / PMOVE_MAX_SPEED_GROUND, right_vector)); + } + + const auto z_near = 0.01f; + const auto z_far = 1.25f * static_cast<float>(CHUNK_SIZE * entity::camera::view_distance.get_value()); + + auto proj = glm::perspective(math::radians(entity::camera::vertical_fov.get_value()), globals::aspect, z_near, z_far); + auto view = platinumsrc_viewmatrix(entity::camera::position_local, client_angles); + + entity::camera::matrix = proj * view; +} diff --git a/game/client/entity/camera.hh b/game/client/entity/camera.hh new file mode 100644 index 0000000..2484bfd --- /dev/null +++ b/game/client/entity/camera.hh @@ -0,0 +1,35 @@ +#ifndef CLIENT_CAMERA_HH +#define CLIENT_CAMERA_HH 1 +#pragma once + +#include "shared/types.hh" + +namespace config +{ +class Float; +class Unsigned; +} // namespace config + +namespace entity::camera +{ +extern config::Float roll_angle; +extern config::Float vertical_fov; +extern config::Unsigned view_distance; +} // namespace entity::camera + +namespace entity::camera +{ +extern glm::fvec3 angles; +extern glm::fvec3 direction; +extern glm::fmat4x4 matrix; +extern chunk_pos position_chunk; +extern glm::fvec3 position_local; +} // namespace entity::camera + +namespace entity::camera +{ +void init(void); +void update(void); +} // namespace entity::camera + +#endif // CLIENT_CAMERA_HH diff --git a/game/client/entity/factory.cc b/game/client/entity/factory.cc new file mode 100644 index 0000000..708dc65 --- /dev/null +++ b/game/client/entity/factory.cc @@ -0,0 +1,29 @@ +#include "client/pch.hh" + +#include "client/entity/factory.hh" + +#include "shared/entity/factory.hh" +#include "shared/entity/head.hh" +#include "shared/entity/transform.hh" +#include "shared/world/dimension.hh" + +#include "client/entity/sound_emitter.hh" + +#include "client/globals.hh" + +void entity::client::create_player(world::Dimension* dimension, entt::entity entity) +{ + entity::shared::create_player(dimension, entity); + + const auto& head = dimension->entities.get<entity::Head>(entity); + dimension->entities.emplace_or_replace<entity::client::HeadIntr>(entity, head); + dimension->entities.emplace_or_replace<entity::client::HeadPrev>(entity, head); + + const auto& transform = dimension->entities.get<entity::Transform>(entity); + dimension->entities.emplace_or_replace<entity::client::TransformIntr>(entity, transform); + dimension->entities.emplace_or_replace<entity::client::TransformPrev>(entity, transform); + + if(globals::sound_ctx) { + dimension->entities.emplace_or_replace<entity::SoundEmitter>(entity); + } +} diff --git a/game/client/entity/factory.hh b/game/client/entity/factory.hh new file mode 100644 index 0000000..b9d3f45 --- /dev/null +++ b/game/client/entity/factory.hh @@ -0,0 +1,15 @@ +#ifndef CLIENT_FACTORY_HH +#define CLIENT_FACTORY_HH 1 +#pragma once + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity::client +{ +void create_player(world::Dimension* dimension, entt::entity entity); +} // namespace entity::client + +#endif // CLIENT_FACTORY_HH diff --git a/game/client/entity/interpolation.cc b/game/client/entity/interpolation.cc new file mode 100644 index 0000000..6eb9e65 --- /dev/null +++ b/game/client/entity/interpolation.cc @@ -0,0 +1,63 @@ +#include "client/pch.hh" + +#include "client/entity/interpolation.hh" + +#include "core/math/constexpr.hh" + +#include "shared/entity/head.hh" +#include "shared/entity/transform.hh" +#include "shared/world/dimension.hh" + +#include "shared/coord.hh" + +#include "client/globals.hh" + +static void transform_interpolate(float alpha) +{ + auto group = globals::dimension->entities.group<entity::client::TransformIntr>( + entt::get<entity::Transform, entity::client::TransformPrev>); + + for(auto [entity, interp, current, previous] : group.each()) { + interp.angles[0] = math::lerp(previous.angles[0], current.angles[0], alpha); + interp.angles[1] = math::lerp(previous.angles[1], current.angles[1], alpha); + interp.angles[2] = math::lerp(previous.angles[2], current.angles[2], alpha); + + // Figure out previous chunk-local floating-point coordinates transformed + // to the current WorldCoord's chunk domain coordinates; we're interpolating + // against these instead of using previous.position.local to prevent jittering + auto previous_shift = coord::to_relative(current.chunk, current.local, previous.chunk, previous.local); + auto previous_local = current.local + previous_shift; + + interp.chunk.x = current.chunk.x; + interp.chunk.y = current.chunk.y; + interp.chunk.z = current.chunk.z; + + interp.local.x = math::lerp(previous_local.x, current.local.x, alpha); + interp.local.y = math::lerp(previous_local.y, current.local.y, alpha); + interp.local.z = math::lerp(previous_local.z, current.local.z, alpha); + } +} + +static void head_interpolate(float alpha) +{ + auto group = globals::dimension->entities.group<entity::client::HeadIntr>(entt::get<entity::Head, entity::client::HeadPrev>); + + for(auto [entity, interp, current, previous] : group.each()) { + interp.angles[0] = math::lerp(previous.angles[0], current.angles[0], alpha); + interp.angles[1] = math::lerp(previous.angles[1], current.angles[1], alpha); + interp.angles[2] = math::lerp(previous.angles[2], current.angles[2], alpha); + + interp.offset.x = math::lerp(previous.offset.x, current.offset.x, alpha); + interp.offset.y = math::lerp(previous.offset.y, current.offset.y, alpha); + interp.offset.z = math::lerp(previous.offset.z, current.offset.z, alpha); + } +} + +void entity::interpolation::update(void) +{ + if(globals::dimension) { + auto alpha = static_cast<float>(globals::fixed_accumulator) / static_cast<float>(globals::fixed_frametime_us); + transform_interpolate(alpha); + head_interpolate(alpha); + } +}
\ No newline at end of file diff --git a/src/game/client/interpolation.hh b/game/client/entity/interpolation.hh index 677f31b..74a2b7e 100644 --- a/src/game/client/interpolation.hh +++ b/game/client/entity/interpolation.hh @@ -2,9 +2,9 @@ #define CLIENT_INTERPOLATION_HH 1 #pragma once -namespace interpolation +namespace entity::interpolation { void update(void); -} // namespace interpolation +} // namespace entity::interpolation #endif // CLIENT_INTERPOLATION_HH diff --git a/game/client/entity/listener.cc b/game/client/entity/listener.cc new file mode 100644 index 0000000..2156571 --- /dev/null +++ b/game/client/entity/listener.cc @@ -0,0 +1,39 @@ +#include "client/pch.hh" + +#include "client/entity/listener.hh" + +#include "core/config/number.hh" +#include "core/math/constexpr.hh" + +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + +#include "client/entity/camera.hh" +#include "client/sound/sound.hh" + +#include "client/const.hh" +#include "client/globals.hh" +#include "client/session.hh" + +void entity::listener::update(void) +{ + if(session::is_ingame()) { + const auto& velocity = globals::dimension->entities.get<entity::Velocity>(globals::player).value; + const auto& position = entity::camera::position_local; + + alListener3f(AL_POSITION, position.x, position.y, position.z); + alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z); + + float orientation[6]; + orientation[0] = entity::camera::direction.x; + orientation[1] = entity::camera::direction.y; + orientation[2] = entity::camera::direction.z; + orientation[3] = DIR_UP<float>.x; + orientation[4] = DIR_UP<float>.y; + orientation[5] = DIR_UP<float>.z; + + alListenerfv(AL_ORIENTATION, orientation); + } + + alListenerf(AL_GAIN, math::clamp(sound::volume_master.get_value() * 0.01f, 0.0f, 1.0f)); +} diff --git a/src/game/client/listener.hh b/game/client/entity/listener.hh index fbf9f4a..fd76816 100644 --- a/src/game/client/listener.hh +++ b/game/client/entity/listener.hh @@ -2,9 +2,9 @@ #define CLIENT_LISTENER_HH 1 #pragma once -namespace listener +namespace entity::listener { void update(void); -} // namespace listener +} // namespace entity::listener #endif // CLIENT_LISTENER_HH diff --git a/src/game/client/player_look.cc b/game/client/entity/player_look.cc index 767c171..9326af2 100644 --- a/src/game/client/player_look.cc +++ b/game/client/entity/player_look.cc @@ -1,41 +1,44 @@ #include "client/pch.hh" -#include "client/player_look.hh" +#include "client/entity/player_look.hh" -#include "core/angles.hh" -#include "core/config.hh" +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/io/config_map.hh" +#include "core/math/angles.hh" -#include "shared/dimension.hh" -#include "shared/head.hh" +#include "shared/entity/head.hh" +#include "shared/world/dimension.hh" + +#include "client/config/gamepad_axis.hh" +#include "client/config/gamepad_button.hh" +#include "client/config/keybind.hh" +#include "client/gui/settings.hh" +#include "client/io/gamepad.hh" +#include "client/io/glfw.hh" #include "client/const.hh" -#include "client/gamepad.hh" -#include "client/gamepad_axis.hh" -#include "client/gamepad_button.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/keybind.hh" #include "client/session.hh" -#include "client/settings.hh" -constexpr static float PITCH_MIN = -1.0f * vx::radians(90.0f); -constexpr static float PITCH_MAX = +1.0f * vx::radians(90.0f); +constexpr static float PITCH_MIN = -1.0f * math::radians(90.0f); +constexpr static float PITCH_MAX = +1.0f * math::radians(90.0f); // Mouse options -static ConfigBoolean mouse_raw_input(true); -static ConfigUnsigned mouse_sensitivity(25U, 1U, 100U); +static config::Boolean mouse_raw_input(true); +static config::Unsigned mouse_sensitivity(25U, 1U, 100U); // Gamepad options -static ConfigFloat gamepad_fastlook_factor(1.5f, 1.0f, 5.0f); -static ConfigUnsigned gamepad_accel_pitch(15U, 1U, 100U); -static ConfigUnsigned gamepad_accel_yaw(25U, 1U, 100U); +static config::Float gamepad_fastlook_factor(1.5f, 1.0f, 5.0f); +static config::Unsigned gamepad_accel_pitch(15U, 1U, 100U); +static config::Unsigned gamepad_accel_yaw(25U, 1U, 100U); // Gamepad axes -static ConfigGamepadAxis axis_pitch(GLFW_GAMEPAD_AXIS_LEFT_Y, false); -static ConfigGamepadAxis axis_yaw(GLFW_GAMEPAD_AXIS_LEFT_X, false); +static config::GamepadAxis axis_pitch(GLFW_GAMEPAD_AXIS_LEFT_Y, false); +static config::GamepadAxis axis_yaw(GLFW_GAMEPAD_AXIS_LEFT_X, false); // Gamepad buttons -static ConfigGamepadButton button_fastlook(GLFW_GAMEPAD_BUTTON_LEFT_THUMB); +static config::GamepadButton button_fastlook(GLFW_GAMEPAD_BUTTON_LEFT_THUMB); static bool fastlook_enabled; static glm::fvec2 last_cursor; @@ -43,24 +46,24 @@ static glm::fvec2 last_cursor; static void add_angles(float pitch, float yaw) { if(session::is_ingame()) { - auto& head = globals::dimension->entities.get<HeadComponent>(globals::player); + auto& head = globals::dimension->entities.get<entity::Head>(globals::player); head.angles[0] += pitch; head.angles[1] += yaw; - head.angles[0] = vx::clamp(head.angles[0], PITCH_MIN, PITCH_MAX); - head.angles = cxangles::wrap_180(head.angles); + head.angles[0] = math::clamp(head.angles[0], PITCH_MIN, PITCH_MAX); + head.angles = math::wrap_180(head.angles); // Client-side head angles are not interpolated; // Re-assigning the previous state after the current // state has been already modified is certainly a way // to circumvent the interpolation applied to anything with a head - globals::dimension->entities.emplace_or_replace<HeadComponentPrev>(globals::player, head); + globals::dimension->entities.emplace_or_replace<entity::client::HeadPrev>(globals::player, head); } } -static void on_glfw_cursor_pos(const GlfwCursorPosEvent& event) +static void on_glfw_cursor_pos(const io::GlfwCursorPosEvent& event) { - if(gamepad::available && gamepad::active.get_value()) { + if(io::gamepad::available && io::gamepad::active.get_value()) { // The player is assumed to be using // a gamepad instead of mouse and keyboard last_cursor = event.pos; @@ -73,21 +76,21 @@ static void on_glfw_cursor_pos(const GlfwCursorPosEvent& event) return; } - auto dx = -0.01f * static_cast<float>(mouse_sensitivity.get_value()) * vx::radians(event.pos.x - last_cursor.x); - auto dy = -0.01f * static_cast<float>(mouse_sensitivity.get_value()) * vx::radians(event.pos.y - last_cursor.y); + auto dx = -0.01f * static_cast<float>(mouse_sensitivity.get_value()) * math::radians(event.pos.x - last_cursor.x); + auto dy = -0.01f * static_cast<float>(mouse_sensitivity.get_value()) * math::radians(event.pos.y - last_cursor.y); add_angles(dy, dx); last_cursor = event.pos; } -static void on_gamepad_button(const GamepadButtonEvent& event) +static void on_gamepad_button(const io::GamepadButtonEvent& event) { if(button_fastlook.equals(event.button)) { fastlook_enabled = event.action == GLFW_PRESS; } } -void player_look::init(void) +void entity::player_look::init(void) { globals::client_config.add_value("player_look.mouse.raw_input", mouse_raw_input); globals::client_config.add_value("player_look.mouse.sensitivity", mouse_sensitivity); @@ -113,15 +116,15 @@ void player_look::init(void) last_cursor.x = 0.5f * static_cast<float>(globals::width); last_cursor.y = 0.5f * static_cast<float>(globals::height); - globals::dispatcher.sink<GlfwCursorPosEvent>().connect<&on_glfw_cursor_pos>(); - globals::dispatcher.sink<GamepadButtonEvent>().connect<&on_gamepad_button>(); + globals::dispatcher.sink<io::GlfwCursorPosEvent>().connect<&on_glfw_cursor_pos>(); + globals::dispatcher.sink<io::GamepadButtonEvent>().connect<&on_gamepad_button>(); } -void player_look::update_late(void) +void entity::player_look::update_late(void) { - if(gamepad::available && gamepad::active.get_value() && !globals::gui_screen) { - auto pitch_value = axis_pitch.get_value(gamepad::state, gamepad::deadzone.get_value()); - auto yaw_value = axis_yaw.get_value(gamepad::state, gamepad::deadzone.get_value()); + if(io::gamepad::available && io::gamepad::active.get_value() && !globals::gui_screen) { + auto pitch_value = axis_pitch.get_value(io::gamepad::state, io::gamepad::deadzone.get_value()); + auto yaw_value = axis_yaw.get_value(io::gamepad::state, io::gamepad::deadzone.get_value()); if(fastlook_enabled) { // Fastlook allows the camera to diff --git a/src/game/client/player_look.hh b/game/client/entity/player_look.hh index 84e0e96..6532b8f 100644 --- a/src/game/client/player_look.hh +++ b/game/client/entity/player_look.hh @@ -2,10 +2,10 @@ #define CLIENT_PLAYER_LOOK_HH 1 #pragma once -namespace player_look +namespace entity::player_look { void init(void); void update_late(void); -} // namespace player_look +} // namespace entity::player_look #endif // CLIENT_PLAYER_LOOK_HH diff --git a/src/game/client/player_move.cc b/game/client/entity/player_move.cc index d949d53..65e8669 100644 --- a/src/game/client/player_move.cc +++ b/game/client/entity/player_move.cc @@ -1,53 +1,56 @@ #include "client/pch.hh" -#include "client/player_move.hh" - -#include "core/angles.hh" -#include "core/config.hh" -#include "core/constexpr.hh" - -#include "shared/dimension.hh" -#include "shared/grounded.hh" -#include "shared/head.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" +#include "client/entity/player_move.hh" + +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/io/config_map.hh" +#include "core/math/angles.hh" +#include "core/math/constexpr.hh" + +#include "shared/entity/grounded.hh" +#include "shared/entity/head.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + +#include "client/config/gamepad_axis.hh" +#include "client/config/gamepad_button.hh" +#include "client/config/keybind.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/settings.hh" +#include "client/gui/status_lines.hh" +#include "client/io/gamepad.hh" +#include "client/sound/sound.hh" +#include "client/world/voxel_sounds.hh" #include "client/const.hh" -#include "client/gamepad.hh" -#include "client/gamepad_axis.hh" -#include "client/gamepad_button.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/keybind.hh" #include "client/session.hh" -#include "client/settings.hh" -#include "client/sound.hh" -#include "client/status_lines.hh" #include "client/toggles.hh" -#include "client/voxel_sounds.hh" constexpr static std::uint64_t PMOVE_JUMP_COOLDOWN = 500000; // 0.5 seconds constexpr static float PMOVE_FOOTSTEP_SIZE = 2.0f; // Movement keys -static ConfigKeyBind key_move_forward(GLFW_KEY_W); -static ConfigKeyBind key_move_back(GLFW_KEY_S); -static ConfigKeyBind key_move_left(GLFW_KEY_A); -static ConfigKeyBind key_move_right(GLFW_KEY_D); -static ConfigKeyBind key_move_down(GLFW_KEY_LEFT_SHIFT); -static ConfigKeyBind key_move_up(GLFW_KEY_SPACE); +static config::KeyBind key_move_forward(GLFW_KEY_W); +static config::KeyBind key_move_back(GLFW_KEY_S); +static config::KeyBind key_move_left(GLFW_KEY_A); +static config::KeyBind key_move_right(GLFW_KEY_D); +static config::KeyBind key_move_down(GLFW_KEY_LEFT_SHIFT); +static config::KeyBind key_move_up(GLFW_KEY_SPACE); // Movement gamepad axes -static ConfigGamepadAxis axis_move_forward(GLFW_GAMEPAD_AXIS_RIGHT_X, false); -static ConfigGamepadAxis axis_move_sideways(GLFW_GAMEPAD_AXIS_RIGHT_Y, false); +static config::GamepadAxis axis_move_forward(GLFW_GAMEPAD_AXIS_RIGHT_X, false); +static config::GamepadAxis axis_move_sideways(GLFW_GAMEPAD_AXIS_RIGHT_Y, false); // Movement gamepad buttons -static ConfigGamepadButton button_move_down(GLFW_GAMEPAD_BUTTON_DPAD_DOWN); -static ConfigGamepadButton button_move_up(GLFW_GAMEPAD_BUTTON_DPAD_UP); +static config::GamepadButton button_move_down(GLFW_GAMEPAD_BUTTON_DPAD_DOWN); +static config::GamepadButton button_move_up(GLFW_GAMEPAD_BUTTON_DPAD_UP); // General movement options -static ConfigBoolean enable_speedometer(true); +static config::Boolean enable_speedometer(true); static glm::fvec3 movement_direction; @@ -70,7 +73,7 @@ static glm::fvec3 pm_accelerate(const glm::fvec3& wishdir, const glm::fvec3& vel return velocity; } - auto accel_speed = vx::min(add_speed, accel * globals::fixed_frametime * wishspeed); + auto accel_speed = math::min(add_speed, accel * globals::fixed_frametime * wishspeed); auto result = glm::fvec3(velocity); result.x += accel_speed * wishdir.x; @@ -89,7 +92,7 @@ static glm::fvec3 pm_ground_move(const glm::fvec3& wishdir, const glm::fvec3& ve { if(auto speed = glm::length(velocity)) { auto speed_drop = speed * PMOVE_FRICTION_GROUND * globals::fixed_frametime; - auto speed_factor = vx::max(speed - speed_drop, 0.0f) / speed; + auto speed_factor = math::max(speed - speed_drop, 0.0f) / speed; return pm_accelerate(wishdir, velocity * speed_factor, PMOVE_ACCELERATION_GROUND, PMOVE_MAX_SPEED_GROUND); } @@ -104,7 +107,7 @@ static glm::fvec3 pm_flight_move(const glm::fvec3& wishdir) return wishdir * PMOVE_MAX_SPEED_AIR; } -void player_move::init(void) +void entity::player_move::init(void) { movement_direction = ZERO_VEC3<float>; @@ -143,17 +146,17 @@ void player_move::init(void) settings::add_checkbox(2, enable_speedometer, settings_location::VIDEO_GUI, "player_move.enable_speedometer", true); } -void player_move::fixed_update(void) +void entity::player_move::fixed_update(void) { - const auto& head = globals::dimension->entities.get<HeadComponent>(globals::player); - auto& transform = globals::dimension->entities.get<TransformComponent>(globals::player); - auto& velocity = globals::dimension->entities.get<VelocityComponent>(globals::player); + const auto& head = globals::dimension->entities.get<entity::Head>(globals::player); + auto& transform = globals::dimension->entities.get<entity::Transform>(globals::player); + auto& velocity = globals::dimension->entities.get<entity::Velocity>(globals::player); // Interpolation - preserve current component states - globals::dimension->entities.emplace_or_replace<TransformComponentPrev>(globals::player, transform); + globals::dimension->entities.emplace_or_replace<entity::client::TransformPrev>(globals::player, transform); glm::fvec3 forward, right; - cxangles::vectors(glm::fvec3(0.0f, head.angles[1], 0.0f), &forward, &right, nullptr); + math::vectors(glm::fvec3(0.0f, head.angles[1], 0.0f), &forward, &right, nullptr); glm::fvec3 wishdir = ZERO_VEC3<float>; glm::fvec3 movevars = glm::fvec3(movement_direction.x, 0.0f, movement_direction.z); @@ -165,7 +168,7 @@ void player_move::fixed_update(void) return; } - auto grounded = globals::dimension->entities.try_get<GroundedComponent>(globals::player); + auto grounded = globals::dimension->entities.try_get<entity::Grounded>(globals::player); auto velocity_horizontal = glm::fvec3(velocity.value.x, 0.0f, velocity.value.z); if(grounded) { @@ -182,7 +185,7 @@ void player_move::fixed_update(void) } if(footsteps_distance >= PMOVE_FOOTSTEP_SIZE) { - if(auto effect = voxel_sounds::get_footsteps(grounded->surface)) { + if(auto effect = world::voxel_sounds::get_footsteps(grounded->surface)) { sound::play_player(effect, false, pitch_distrib(pitch_random)); } @@ -213,28 +216,28 @@ void player_move::fixed_update(void) next_jump_us = globals::curtime + PMOVE_JUMP_COOLDOWN; if(enable_speedometer.get_value()) { - if(vx::abs(speed_change) < 0.01f) { + if(math::abs(speed_change) < 0.01f) { // No considerable speed increase within // the precision we use to draw the speedometer - status_lines::set(STATUS_DEBUG, new_speed_text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f), 1.0f); + gui::status_lines::set(gui::STATUS_DEBUG, new_speed_text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f), 1.0f); } else if(speed_change < 0.0f) { // Speed change is negative, we are actively // slowing down; use the red color for the status line - status_lines::set(STATUS_DEBUG, new_speed_text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 1.0f); + gui::status_lines::set(gui::STATUS_DEBUG, new_speed_text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 1.0f); } else { // Speed change is positive, we are actively // speeding up; use the green color for the status line - status_lines::set(STATUS_DEBUG, new_speed_text, ImVec4(0.0f, 1.0f, 0.0f, 1.0f), 1.0f); + gui::status_lines::set(gui::STATUS_DEBUG, new_speed_text, ImVec4(0.0f, 1.0f, 0.0f, 1.0f), 1.0f); } } - if(auto effect = voxel_sounds::get_footsteps(grounded->surface)) { + if(auto effect = world::voxel_sounds::get_footsteps(grounded->surface)) { sound::play_player(effect, false, 1.0f); } } } -void player_move::update_late(void) +void entity::player_move::update_late(void) { movement_direction = ZERO_VEC3<float>; @@ -244,17 +247,17 @@ void player_move::update_late(void) return; } - if(gamepad::available && gamepad::active.get_value()) { - if(button_move_down.is_pressed(gamepad::state)) { + if(io::gamepad::available && io::gamepad::active.get_value()) { + if(button_move_down.is_pressed(io::gamepad::state)) { movement_direction += DIR_DOWN<float>; } - if(button_move_up.is_pressed(gamepad::state)) { + if(button_move_up.is_pressed(io::gamepad::state)) { movement_direction += DIR_UP<float>; } - movement_direction.x += axis_move_sideways.get_value(gamepad::state, gamepad::deadzone.get_value()); - movement_direction.z -= axis_move_forward.get_value(gamepad::state, gamepad::deadzone.get_value()); + movement_direction.x += axis_move_sideways.get_value(io::gamepad::state, io::gamepad::deadzone.get_value()); + movement_direction.z -= axis_move_forward.get_value(io::gamepad::state, io::gamepad::deadzone.get_value()); } else { if(GLFW_PRESS == glfwGetKey(globals::window, key_move_forward.get_key())) { movement_direction += DIR_FORWARD<float>; diff --git a/src/game/client/player_move.hh b/game/client/entity/player_move.hh index 27fb2dc..aa221f3 100644 --- a/src/game/client/player_move.hh +++ b/game/client/entity/player_move.hh @@ -9,11 +9,11 @@ constexpr static float PMOVE_ACCELERATION_GROUND = 6.0f; constexpr static float PMOVE_FRICTION_GROUND = 10.0f; constexpr static float PMOVE_JUMP_FORCE = 0.275f; -namespace player_move +namespace entity::player_move { void init(void); void fixed_update(void); void update_late(void); -} // namespace player_move +} // namespace entity::player_move #endif // CLIENT_PLAYER_MOVE_HH diff --git a/src/game/client/sound_emitter.cc b/game/client/entity/sound_emitter.cc index 9b84df2..7ee5cc1 100644 --- a/src/game/client/sound_emitter.cc +++ b/game/client/entity/sound_emitter.cc @@ -1,48 +1,50 @@ #include "client/pch.hh" -#include "client/sound_emitter.hh" +#include "client/entity/sound_emitter.hh" -#include "core/config.hh" -#include "core/constexpr.hh" +#include "core/config/number.hh" +#include "core/math/constexpr.hh" + +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "client/camera.hh" +#include "client/entity/camera.hh" +#include "client/sound/sound.hh" + #include "client/globals.hh" -#include "client/sound.hh" -SoundEmitterComponent::SoundEmitterComponent(void) +entity::SoundEmitter::SoundEmitter(void) { alGenSources(1, &source); sound = nullptr; } -SoundEmitterComponent::~SoundEmitterComponent(void) +entity::SoundEmitter::~SoundEmitter(void) { alSourceStop(source); alDeleteSources(1, &source); } -void SoundEmitterComponent::update(void) +void entity::SoundEmitter::update(void) { if(globals::dimension) { - const auto view = globals::dimension->entities.view<SoundEmitterComponent>(); + const auto view = globals::dimension->entities.view<entity::SoundEmitter>(); - const auto& pivot = camera::position_chunk; - const auto gain = vx::clamp(sound::volume_effects.get_value() * 0.01f, 0.0f, 1.0f); + const auto& pivot = entity::camera::position_chunk; + const auto gain = math::clamp(sound::volume_effects.get_value() * 0.01f, 0.0f, 1.0f); for(const auto [entity, emitter] : view.each()) { alSourcef(emitter.source, AL_GAIN, gain); - if(const auto transform = globals::dimension->entities.try_get<TransformComponentIntr>(entity)) { + if(const auto transform = globals::dimension->entities.try_get<entity::client::TransformIntr>(entity)) { auto position = coord::to_relative(pivot, transform->chunk, transform->local); alSource3f(emitter.source, AL_POSITION, position.x, position.y, position.z); } - if(const auto velocity = globals::dimension->entities.try_get<VelocityComponent>(entity)) { + if(const auto velocity = globals::dimension->entities.try_get<entity::Velocity>(entity)) { alSource3f(emitter.source, AL_VELOCITY, velocity->value.x, velocity->value.y, velocity->value.z); } diff --git a/src/game/client/sound_emitter.hh b/game/client/entity/sound_emitter.hh index 17598ad..d5834d1 100644 --- a/src/game/client/sound_emitter.hh +++ b/game/client/entity/sound_emitter.hh @@ -2,20 +2,23 @@ #define CLIENT_SOUND_EMITTER_HH 1 #pragma once -#include "core/resource.hh" +#include "core/resource/resource.hh" struct SoundEffect; -struct SoundEmitterComponent final { +namespace entity +{ +struct SoundEmitter final { resource_ptr<SoundEffect> sound; ALuint source; public: - explicit SoundEmitterComponent(void); - virtual ~SoundEmitterComponent(void); + explicit SoundEmitter(void); + virtual ~SoundEmitter(void); public: static void update(void); }; +} // namespace entity #endif // CLIENT_SOUND_EMITTER_HH diff --git a/game/client/experiments.cc b/game/client/experiments.cc new file mode 100644 index 0000000..c9b5e0c --- /dev/null +++ b/game/client/experiments.cc @@ -0,0 +1,79 @@ +#include "client/pch.hh" + +#include "client/experiments.hh" + +#include "shared/world/dimension.hh" +#include "shared/world/item_registry.hh" + +#include "shared/game_items.hh" +#include "shared/game_voxels.hh" + +#include "client/gui/chat.hh" +#include "client/gui/hotbar.hh" +#include "client/gui/status_lines.hh" +#include "client/io/glfw.hh" +#include "client/world/player_target.hh" + +#include "client/globals.hh" +#include "client/session.hh" + +static void on_glfw_mouse_button(const io::GlfwMouseButtonEvent& event) +{ + if(!globals::gui_screen && session::is_ingame()) { + if((event.action == GLFW_PRESS) && (world::player_target::voxel != NULL_VOXEL_ID)) { + if(event.button == GLFW_MOUSE_BUTTON_LEFT) { + experiments::attack(); + return; + } + + if(event.button == GLFW_MOUSE_BUTTON_RIGHT) { + experiments::interact(); + return; + } + } + } +} + +void experiments::init(void) +{ + globals::dispatcher.sink<io::GlfwMouseButtonEvent>().connect<&on_glfw_mouse_button>(); +} + +void experiments::init_late(void) +{ + gui::hotbar::slots[0] = game_items::cobblestone; + gui::hotbar::slots[1] = game_items::stone; + gui::hotbar::slots[2] = game_items::dirt; + gui::hotbar::slots[3] = game_items::grass; + gui::hotbar::slots[4] = game_items::oak_leaves; + gui::hotbar::slots[5] = game_items::oak_planks; + gui::hotbar::slots[6] = game_items::oak_log; + gui::hotbar::slots[7] = game_items::glass; + gui::hotbar::slots[8] = game_items::slime; +} + +void experiments::shutdown(void) +{ +} + +void experiments::update(void) +{ +} + +void experiments::update_late(void) +{ +} + +void experiments::attack(void) +{ + globals::dimension->set_voxel(NULL_VOXEL_ID, world::player_target::coord); +} + +void experiments::interact(void) +{ + if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { + if(info->place_voxel != NULL_VOXEL_ID) { + globals::dimension->set_voxel(info->place_voxel, world::player_target::coord + world::player_target::normal); + } + } +} diff --git a/src/game/client/experiments.hh b/game/client/experiments.hh index a2ce66a..a2ce66a 100644 --- a/src/game/client/experiments.hh +++ b/game/client/experiments.hh diff --git a/src/game/client/game.cc b/game/client/game.cc index 8d157b0..0418236 100644 --- a/src/game/client/game.cc +++ b/game/client/game.cc @@ -2,93 +2,98 @@ #include "client/game.hh" -#include "core/angles.hh" -#include "core/binfile.hh" -#include "core/config.hh" -#include "core/resource.hh" - -#include "shared/collision.hh" -#include "shared/coord.hh" -#include "shared/dimension.hh" +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/config/string.hh" +#include "core/io/config_map.hh" +#include "core/math/angles.hh" +#include "core/resource/binfile.hh" +#include "core/resource/resource.hh" + +#include "shared/entity/collision.hh" +#include "shared/entity/gravity.hh" +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/stasis.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" #include "shared/game_items.hh" #include "shared/game_voxels.hh" -#include "shared/gravity.hh" -#include "shared/head.hh" -#include "shared/item_registry.hh" -#include "shared/player.hh" +#include "shared/world/dimension.hh" +#include "shared/world/item_registry.hh" +#include "shared/world/ray_dda.hh" +#include "shared/world/voxel_registry.hh" + +#include "shared/coord.hh" #include "shared/protocol.hh" -#include "shared/ray_dda.hh" -#include "shared/stasis.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "shared/voxel_registry.hh" - -#include "client/background.hh" -#include "client/bother.hh" -#include "client/camera.hh" -#include "client/chat.hh" -#include "client/chunk_mesher.hh" -#include "client/chunk_renderer.hh" -#include "client/chunk_visibility.hh" + +#include "client/config/keybind.hh" +#include "client/entity/camera.hh" +#include "client/entity/interpolation.hh" +#include "client/entity/listener.hh" +#include "client/entity/player_look.hh" +#include "client/entity/player_move.hh" +#include "client/entity/sound_emitter.hh" +#include "client/gui/background.hh" +#include "client/gui/bother.hh" +#include "client/gui/chat.hh" +#include "client/gui/crosshair.hh" +#include "client/gui/direct_connection.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/hotbar.hh" +#include "client/gui/language.hh" +#include "client/gui/main_menu.hh" +#include "client/gui/message_box.hh" +#include "client/gui/metrics.hh" +#include "client/gui/play_menu.hh" +#include "client/gui/progress_bar.hh" +#include "client/gui/scoreboard.hh" +#include "client/gui/settings.hh" +#include "client/gui/splash.hh" +#include "client/gui/status_lines.hh" +#include "client/gui/window_title.hh" +#include "client/io/gamepad.hh" +#include "client/io/glfw.hh" +#include "client/resource/texture_gui.hh" +#include "client/sound/sound.hh" +#include "client/world/chunk_mesher.hh" +#include "client/world/chunk_renderer.hh" +#include "client/world/chunk_visibility.hh" +#include "client/world/outline.hh" +#include "client/world/player_target.hh" +#include "client/world/skybox.hh" +#include "client/world/voxel_anims.hh" +#include "client/world/voxel_atlas.hh" +#include "client/world/voxel_sounds.hh" + #include "client/const.hh" -#include "client/crosshair.hh" -#include "client/direct_connection.hh" #include "client/experiments.hh" -#include "client/gamepad.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/hotbar.hh" -#include "client/interpolation.hh" -#include "client/keybind.hh" -#include "client/language.hh" -#include "client/listener.hh" -#include "client/main_menu.hh" -#include "client/message_box.hh" -#include "client/metrics.hh" -#include "client/outline.hh" -#include "client/play_menu.hh" -#include "client/player_look.hh" -#include "client/player_move.hh" -#include "client/player_target.hh" -#include "client/progress_bar.hh" #include "client/receive.hh" -#include "client/scoreboard.hh" #include "client/screenshot.hh" #include "client/session.hh" -#include "client/settings.hh" -#include "client/skybox.hh" -#include "client/sound.hh" -#include "client/sound_emitter.hh" -#include "client/splash.hh" -#include "client/status_lines.hh" -#include "client/texture_gui.hh" #include "client/toggles.hh" -#include "client/voxel_anims.hh" -#include "client/voxel_atlas.hh" -#include "client/voxel_sounds.hh" -#include "client/window_title.hh" -ConfigBoolean client_game::streamer_mode(false); -ConfigBoolean client_game::vertical_sync(true); -ConfigBoolean client_game::world_curvature(true); -ConfigUnsigned client_game::fog_mode(1U, 0U, 2U); -ConfigString client_game::username("player"); +config::Boolean client_game::streamer_mode(false); +config::Boolean client_game::vertical_sync(true); +config::Boolean client_game::world_curvature(true); +config::Unsigned client_game::fog_mode(1U, 0U, 2U); +config::String client_game::username("player"); bool client_game::hide_hud = false; -static ConfigKeyBind hide_hud_toggle(GLFW_KEY_F1); +static config::KeyBind hide_hud_toggle(GLFW_KEY_F1); static resource_ptr<BinFile> bin_unscii16; static resource_ptr<BinFile> bin_unscii8; -static void on_glfw_framebuffer_size(const GlfwFramebufferSizeEvent& event) +static void on_glfw_framebuffer_size(const io::GlfwFramebufferSizeEvent& event) { auto width_float = static_cast<float>(event.size.x); auto height_float = static_cast<float>(event.size.y); - auto wscale = vx::max(1U, vx::floor<unsigned int>(width_float / static_cast<float>(BASE_WIDTH))); - auto hscale = vx::max(1U, vx::floor<unsigned int>(height_float / static_cast<float>(BASE_HEIGHT))); - auto scale = vx::min(wscale, hscale); + auto wscale = math::max(1U, math::floor<unsigned int>(width_float / static_cast<float>(BASE_WIDTH))); + auto hscale = math::max(1U, math::floor<unsigned int>(height_float / static_cast<float>(BASE_HEIGHT))); + auto scale = math::min(wscale, hscale); if(globals::gui_scale != scale) { auto& io = ImGui::GetIO(); @@ -164,7 +169,7 @@ static void on_glfw_framebuffer_size(const GlfwFramebufferSizeEvent& event) } } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if(!globals::gui_keybind_ptr && hide_hud_toggle.equals(event.key) && (event.action == GLFW_PRESS)) { client_game::hide_hud = !client_game::hide_hud; @@ -181,8 +186,8 @@ void client_game::init(void) std::terminate(); } - client_splash::init(); - client_splash::render(); + gui::client_splash::init(); + gui::client_splash::render(); globals::client_config.add_value("game.streamer_mode", client_game::streamer_mode); globals::client_config.add_value("game.vertical_sync", client_game::vertical_sync); @@ -207,31 +212,31 @@ void client_game::init(void) std::terminate(); } - language::init(); + gui::language::init(); session::init(); - player_look::init(); - player_move::init(); - player_target::init(); + entity::player_look::init(); + entity::player_move::init(); + world::player_target::init(); - gamepad::init(); + io::gamepad::init(); - camera::init(); + entity::camera::init(); - voxel_anims::init(); + world::voxel_anims::init(); - outline::init(); - chunk_mesher::init(); - chunk_renderer::init(); + world::outline::init(); + world::chunk_mesher::init(); + world::chunk_renderer::init(); globals::world_fbo = 0; globals::world_fbo_color = 0; globals::world_fbo_depth = 0; - voxel_sounds::init(); + world::voxel_sounds::init(); - skybox::init(); + world::skybox::init(); ImGuiStyle& style = ImGui::GetStyle(); @@ -313,24 +318,24 @@ void client_game::init(void) toggles::init(); - background::init(); + gui::background::init(); - scoreboard::init(); + gui::scoreboard::init(); - client_chat::init(); + gui::client_chat::init(); - bother::init(); + gui::bother::init(); - main_menu::init(); - play_menu::init(); - progress_bar::init(); - message_box::init(); - direct_connection::init(); + gui::main_menu::init(); + gui::play_menu::init(); + gui::progress_bar::init(); + gui::message_box::init(); + gui::direct_connection::init(); - crosshair::init(); - hotbar::init(); - metrics::init(); - status_lines::init(); + gui::crosshair::init(); + gui::hotbar::init(); + gui::metrics::init(); + gui::status_lines::init(); screenshot::init(); @@ -348,8 +353,8 @@ void client_game::init(void) experiments::init(); - globals::dispatcher.sink<GlfwFramebufferSizeEvent>().connect<&on_glfw_framebuffer_size>(); - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwFramebufferSizeEvent>().connect<&on_glfw_framebuffer_size>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); } void client_game::init_late(void) @@ -360,13 +365,13 @@ void client_game::init_late(void) sound::init_late(); } - language::init_late(); + gui::language::init_late(); settings::init_late(); - client_chat::init_late(); + gui::client_chat::init_late(); - status_lines::init_late(); + gui::status_lines::init_late(); game_voxels::populate(); game_items::populate(); @@ -377,18 +382,18 @@ void client_game::init_late(void) // NOTE: this is very debug, early and a quite // conservative limit choice; there must be a better // way to make this limit way smaller than it currently is - for(const std::shared_ptr<VoxelInfo>& info : voxel_registry::voxels) { - for(const VoxelTexture& vtex : info->textures) { + for(const std::shared_ptr<world::VoxelInfo>& info : world::voxel_registry::voxels) { + for(const world::VoxelTexture& vtex : info->textures) { max_texture_count += vtex.paths.size(); } } // UNDONE: asset packs for non-16x16 stuff - voxel_atlas::create(16, 16, max_texture_count); + world::voxel_atlas::create(16, 16, max_texture_count); - for(std::shared_ptr<VoxelInfo>& info : voxel_registry::voxels) { - for(VoxelTexture& vtex : info->textures) { - if(auto strip = voxel_atlas::find_or_load(vtex.paths)) { + for(std::shared_ptr<world::VoxelInfo>& info : world::voxel_registry::voxels) { + for(world::VoxelTexture& vtex : info->textures) { + if(auto strip = world::voxel_atlas::find_or_load(vtex.paths)) { vtex.cached_offset = strip->offset; vtex.cached_plane = strip->plane; continue; @@ -399,22 +404,22 @@ void client_game::init_late(void) } } - voxel_atlas::generate_mipmaps(); + world::voxel_atlas::generate_mipmaps(); - for(std::shared_ptr<ItemInfo>& info : item_registry::items) { + for(std::shared_ptr<world::ItemInfo>& info : world::item_registry::items) { info->cached_texture = resource::load<TextureGUI>(info->texture.c_str(), TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); } experiments::init_late(); - client_splash::init_late(); + gui::client_splash::init_late(); - window_title::update(); + gui::window_title::update(); } void client_game::shutdown(void) { - voxel_sounds::shutdown(); + world::voxel_sounds::shutdown(); experiments::shutdown(); @@ -424,34 +429,34 @@ void client_game::shutdown(void) sound::shutdown(); } - hotbar::shutdown(); - main_menu::shutdown(); - play_menu::shutdown(); + gui::hotbar::shutdown(); + gui::main_menu::shutdown(); + gui::play_menu::shutdown(); - bother::shutdown(); + gui::bother::shutdown(); - client_chat::shutdown(); + gui::client_chat::shutdown(); - background::shutdown(); + gui::background::shutdown(); - crosshair::shutdown(); + gui::crosshair::shutdown(); delete globals::dimension; globals::player = entt::null; globals::dimension = nullptr; - item_registry::purge(); - voxel_registry::purge(); + world::item_registry::purge(); + world::voxel_registry::purge(); - voxel_atlas::destroy(); + world::voxel_atlas::destroy(); glDeleteRenderbuffers(1, &globals::world_fbo_depth); glDeleteTextures(1, &globals::world_fbo_color); glDeleteFramebuffers(1, &globals::world_fbo); - outline::shutdown(); - chunk_renderer::shutdown(); - chunk_mesher::shutdown(); + world::outline::shutdown(); + world::chunk_renderer::shutdown(); + world::chunk_mesher::shutdown(); enet_host_destroy(globals::client_host); @@ -461,26 +466,26 @@ void client_game::shutdown(void) void client_game::fixed_update(void) { - player_move::fixed_update(); + entity::player_move::fixed_update(); // Only update world simulation gamesystems // if the player can actually observe all the // changes these gamesystems cause visually if(session::is_ingame()) { - CollisionComponent::fixed_update(globals::dimension); - VelocityComponent::fixed_update(globals::dimension); - TransformComponent::fixed_update(globals::dimension); - GravityComponent::fixed_update(globals::dimension); - StasisComponent::fixed_update(globals::dimension); + entity::Collision::fixed_update(globals::dimension); + entity::Velocity::fixed_update(globals::dimension); + entity::Transform::fixed_update(globals::dimension); + entity::Gravity::fixed_update(globals::dimension); + entity::Stasis::fixed_update(globals::dimension); } } void client_game::fixed_update_late(void) { if(session::is_ingame()) { - const auto& head = globals::dimension->entities.get<HeadComponent>(globals::player); - const auto& transform = globals::dimension->entities.get<TransformComponent>(globals::player); - const auto& velocity = globals::dimension->entities.get<VelocityComponent>(globals::player); + const auto& head = globals::dimension->entities.get<entity::Head>(globals::player); + const auto& transform = globals::dimension->entities.get<entity::Transform>(globals::player); + const auto& velocity = globals::dimension->entities.get<entity::Velocity>(globals::player); protocol::EntityHead head_packet; head_packet.entity = entt::null; // ignored by server @@ -506,31 +511,31 @@ void client_game::update(void) { if(session::is_ingame()) { if(toggles::get(TOGGLE_PM_FLIGHT)) { - globals::dimension->entities.remove<GravityComponent>(globals::player); + globals::dimension->entities.remove<entity::Gravity>(globals::player); } else { - globals::dimension->entities.emplace_or_replace<GravityComponent>(globals::player); + globals::dimension->entities.emplace_or_replace<entity::Gravity>(globals::player); } } if(globals::sound_ctx) { sound::update(); - listener::update(); + entity::listener::update(); - SoundEmitterComponent::update(); + entity::SoundEmitter::update(); } - interpolation::update(); + entity::interpolation::update(); - player_target::update(); + world::player_target::update(); - camera::update(); + entity::camera::update(); - voxel_anims::update(); + world::voxel_anims::update(); - chunk_mesher::update(); + world::chunk_mesher::update(); - client_chat::update(); + gui::client_chat::update(); experiments::update(); } @@ -554,18 +559,18 @@ void client_game::update_late(void) } } - player_look::update_late(); - player_move::update_late(); + entity::player_look::update_late(); + entity::player_move::update_late(); - play_menu::update_late(); + gui::play_menu::update_late(); - bother::update_late(); + gui::bother::update_late(); experiments::update_late(); - gamepad::update_late(); + io::gamepad::update_late(); - chunk_visibility::update_late(); + world::chunk_visibility::update_late(); if(client_game::vertical_sync.get_value()) { glfwSwapInterval(1); @@ -578,22 +583,22 @@ void client_game::render(void) { glViewport(0, 0, globals::width, globals::height); glBindFramebuffer(GL_FRAMEBUFFER, globals::world_fbo); - glClearColor(skybox::fog_color.r, skybox::fog_color.g, skybox::fog_color.b, 1.000f); + glClearColor(world::skybox::fog_color.r, world::skybox::fog_color.g, world::skybox::fog_color.b, 1.000f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if(globals::dimension) { - chunk_renderer::render(); + world::chunk_renderer::render(); } glEnable(GL_DEPTH_TEST); - player_target::render(); + world::player_target::render(); if(globals::dimension) { auto group = globals::dimension->entities.group( - entt::get<PlayerComponent, CollisionComponent, HeadComponentIntr, TransformComponentIntr>); + entt::get<entity::Player, entity::Collision, entity::client::HeadIntr, entity::client::TransformIntr>); - outline::prepare(); + world::outline::prepare(); for(const auto [entity, collision, head, transform] : group.each()) { if(entity == globals::player) { @@ -602,15 +607,15 @@ void client_game::render(void) } glm::fvec3 forward; - cxangles::vectors(transform.angles + head.angles, forward); + math::vectors(transform.angles + head.angles, forward); forward *= 2.0f; glm::fvec3 hull_size = collision.aabb.max - collision.aabb.min; glm::fvec3 hull_fpos = transform.local + collision.aabb.min; glm::fvec3 look = transform.local + head.offset; - outline::cube(transform.chunk, hull_fpos, hull_size, 1.0f, glm::fvec4(1.0f, 0.0f, 0.0f, 1.0f)); - outline::line(transform.chunk, look, forward, 1.0f, glm::fvec4(0.9f, 0.9f, 0.9f, 1.0f)); + world::outline::cube(transform.chunk, hull_fpos, hull_size, 1.0f, glm::fvec4(1.0f, 0.0f, 0.0f, 1.0f)); + world::outline::line(transform.chunk, look, forward, 1.0f, glm::fvec4(0.9f, 0.9f, 0.9f, 1.0f)); } } @@ -629,7 +634,7 @@ void client_game::render(void) void client_game::layout(void) { if(!session::is_ingame()) { - background::layout(); + gui::background::layout(); } if(!globals::gui_screen || (globals::gui_screen == GUI_CHAT)) { @@ -637,18 +642,18 @@ void client_game::layout(void) // This contains Minecraft-esque debug information // about the hardware, world state and other // things that might be uesful - metrics::layout(); + gui::metrics::layout(); } } if(session::is_ingame()) { - client_chat::layout(); - scoreboard::layout(); + gui::client_chat::layout(); + gui::scoreboard::layout(); if(!globals::gui_screen && !client_game::hide_hud) { - hotbar::layout(); - status_lines::layout(); - crosshair::layout(); + gui::hotbar::layout(); + gui::status_lines::layout(); + gui::crosshair::layout(); } } @@ -662,22 +667,22 @@ void client_game::layout(void) switch(globals::gui_screen) { case GUI_MAIN_MENU: - main_menu::layout(); + gui::main_menu::layout(); break; case GUI_PLAY_MENU: - play_menu::layout(); + gui::play_menu::layout(); break; case GUI_SETTINGS: settings::layout(); break; case GUI_PROGRESS_BAR: - progress_bar::layout(); + gui::progress_bar::layout(); break; case GUI_MESSAGE_BOX: - message_box::layout(); + gui::message_box::layout(); break; case GUI_DIRECT_CONNECTION: - direct_connection::layout(); + gui::direct_connection::layout(); break; } } diff --git a/src/game/client/game.hh b/game/client/game.hh index 09ab60b..cc136df 100644 --- a/src/game/client/game.hh +++ b/game/client/game.hh @@ -2,17 +2,20 @@ #define CLIENT_GAME_HH 1 #pragma once -class ConfigBoolean; -class ConfigString; -class ConfigUnsigned; +namespace config +{ +class Boolean; +class String; +class Unsigned; +} // namespace config namespace client_game { -extern ConfigBoolean streamer_mode; -extern ConfigBoolean vertical_sync; -extern ConfigBoolean world_curvature; -extern ConfigUnsigned fog_mode; -extern ConfigString username; +extern config::Boolean streamer_mode; +extern config::Boolean vertical_sync; +extern config::Boolean world_curvature; +extern config::Unsigned fog_mode; +extern config::String username; } // namespace client_game namespace client_game diff --git a/src/game/client/globals.cc b/game/client/globals.cc index 6e00680..5f5b3c9 100644 --- a/src/game/client/globals.cc +++ b/game/client/globals.cc @@ -2,11 +2,11 @@ #include "client/globals.hh" -#include "core/config.hh" +#include "core/io/config_map.hh" -#include "client/gui_screen.hh" +#include "client/gui/gui_screen.hh" -Config globals::client_config; +io::ConfigMap globals::client_config; GLFWwindow* globals::window; @@ -30,16 +30,16 @@ std::size_t globals::num_triangles; ENetHost* globals::client_host; -Dimension* globals::dimension = nullptr; +world::Dimension* globals::dimension = nullptr; entt::entity globals::player; ImFont* globals::font_debug; ImFont* globals::font_default; ImFont* globals::font_chat; -ConfigKeyBind* globals::gui_keybind_ptr = nullptr; -ConfigGamepadAxis* globals::gui_gamepad_axis_ptr = nullptr; -ConfigGamepadButton* globals::gui_gamepad_button_ptr = nullptr; +config::KeyBind* globals::gui_keybind_ptr = nullptr; +config::GamepadAxis* globals::gui_gamepad_axis_ptr = nullptr; +config::GamepadButton* globals::gui_gamepad_button_ptr = nullptr; unsigned int globals::gui_scale = 0U; unsigned int globals::gui_screen = GUI_SCREEN_NONE; diff --git a/src/game/client/globals.hh b/game/client/globals.hh index a59b8bf..0ed98ac 100644 --- a/src/game/client/globals.hh +++ b/game/client/globals.hh @@ -4,19 +4,29 @@ #include "shared/globals.hh" -class Config; -class ConfigKeyBind; -class ConfigGamepadAxis; -class ConfigGamepadButton; +namespace config +{ +class KeyBind; +class GamepadAxis; +class GamepadButton; +} // namespace config + +namespace io +{ +class ConfigMap; +} // namespace io struct GLFWwindow; struct ImFont; +namespace world +{ class Dimension; +} // namespace world namespace globals { -extern Config client_config; +extern io::ConfigMap client_config; extern GLFWwindow* window; @@ -45,16 +55,16 @@ extern std::size_t num_triangles; extern ENetHost* client_host; -extern Dimension* dimension; +extern world::Dimension* dimension; extern entt::entity player; extern ImFont* font_debug; extern ImFont* font_default; extern ImFont* font_chat; -extern ConfigKeyBind* gui_keybind_ptr; -extern ConfigGamepadAxis* gui_gamepad_axis_ptr; -extern ConfigGamepadButton* gui_gamepad_button_ptr; +extern config::KeyBind* gui_keybind_ptr; +extern config::GamepadAxis* gui_gamepad_axis_ptr; +extern config::GamepadButton* gui_gamepad_button_ptr; extern unsigned int gui_scale; extern unsigned int gui_screen; diff --git a/game/client/gui/CMakeLists.txt b/game/client/gui/CMakeLists.txt new file mode 100644 index 0000000..46d64a1 --- /dev/null +++ b/game/client/gui/CMakeLists.txt @@ -0,0 +1,38 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/background.cc" + "${CMAKE_CURRENT_LIST_DIR}/background.hh" + "${CMAKE_CURRENT_LIST_DIR}/bother.cc" + "${CMAKE_CURRENT_LIST_DIR}/bother.hh" + "${CMAKE_CURRENT_LIST_DIR}/chat.cc" + "${CMAKE_CURRENT_LIST_DIR}/chat.hh" + "${CMAKE_CURRENT_LIST_DIR}/crosshair.cc" + "${CMAKE_CURRENT_LIST_DIR}/crosshair.hh" + "${CMAKE_CURRENT_LIST_DIR}/direct_connection.cc" + "${CMAKE_CURRENT_LIST_DIR}/direct_connection.hh" + "${CMAKE_CURRENT_LIST_DIR}/gui_screen.hh" + "${CMAKE_CURRENT_LIST_DIR}/hotbar.cc" + "${CMAKE_CURRENT_LIST_DIR}/hotbar.hh" + "${CMAKE_CURRENT_LIST_DIR}/imdraw_ext.cc" + "${CMAKE_CURRENT_LIST_DIR}/imdraw_ext.hh" + "${CMAKE_CURRENT_LIST_DIR}/language.cc" + "${CMAKE_CURRENT_LIST_DIR}/language.hh" + "${CMAKE_CURRENT_LIST_DIR}/main_menu.cc" + "${CMAKE_CURRENT_LIST_DIR}/main_menu.hh" + "${CMAKE_CURRENT_LIST_DIR}/message_box.cc" + "${CMAKE_CURRENT_LIST_DIR}/message_box.hh" + "${CMAKE_CURRENT_LIST_DIR}/metrics.cc" + "${CMAKE_CURRENT_LIST_DIR}/metrics.hh" + "${CMAKE_CURRENT_LIST_DIR}/play_menu.cc" + "${CMAKE_CURRENT_LIST_DIR}/play_menu.hh" + "${CMAKE_CURRENT_LIST_DIR}/progress_bar.cc" + "${CMAKE_CURRENT_LIST_DIR}/progress_bar.hh" + "${CMAKE_CURRENT_LIST_DIR}/scoreboard.cc" + "${CMAKE_CURRENT_LIST_DIR}/scoreboard.hh" + "${CMAKE_CURRENT_LIST_DIR}/settings.cc" + "${CMAKE_CURRENT_LIST_DIR}/settings.hh" + "${CMAKE_CURRENT_LIST_DIR}/splash.cc" + "${CMAKE_CURRENT_LIST_DIR}/splash.hh" + "${CMAKE_CURRENT_LIST_DIR}/status_lines.cc" + "${CMAKE_CURRENT_LIST_DIR}/status_lines.hh" + "${CMAKE_CURRENT_LIST_DIR}/window_title.cc" + "${CMAKE_CURRENT_LIST_DIR}/window_title.hh") diff --git a/src/game/client/background.cc b/game/client/gui/background.cc index d104d7a..0c38283 100644 --- a/src/game/client/background.cc +++ b/game/client/gui/background.cc @@ -1,16 +1,17 @@ #include "client/pch.hh" -#include "client/background.hh" +#include "client/gui/background.hh" -#include "core/constexpr.hh" -#include "core/resource.hh" +#include "core/math/constexpr.hh" +#include "core/resource/resource.hh" + +#include "client/resource/texture_gui.hh" #include "client/globals.hh" -#include "client/texture_gui.hh" static resource_ptr<TextureGUI> texture; -void background::init(void) +void gui::background::init(void) { texture = resource::load<TextureGUI>("textures/gui/background.png", TEXTURE_GUI_LOAD_VFLIP); @@ -20,12 +21,12 @@ void background::init(void) } } -void background::shutdown(void) +void gui::background::shutdown(void) { texture = nullptr; } -void background::layout(void) +void gui::background::layout(void) { auto viewport = ImGui::GetMainViewport(); auto draw_list = ImGui::GetBackgroundDrawList(); diff --git a/src/game/client/background.hh b/game/client/gui/background.hh index f6a68bb..1974c34 100644 --- a/src/game/client/background.hh +++ b/game/client/gui/background.hh @@ -2,11 +2,11 @@ #define CLIENT_BACKGROUND_HH 1 #pragma once -namespace background +namespace gui::background { void init(void); void shutdown(void); void layout(void); -} // namespace background +} // namespace gui::background #endif // CLIENT_BACKGROUND_HH diff --git a/src/game/client/bother.cc b/game/client/gui/bother.cc index 7b10be5..3a35438 100644 --- a/src/game/client/bother.cc +++ b/game/client/gui/bother.cc @@ -1,6 +1,6 @@ #include "client/pch.hh" -#include "client/bother.hh" +#include "client/gui/bother.hh" #include "shared/protocol.hh" @@ -26,7 +26,7 @@ static void on_status_response_packet(const protocol::StatusResponse& packet) bother_set.erase(identity); - BotherResponseEvent event; + gui::BotherResponseEvent event; event.identity = identity; event.is_server_unreachable = false; event.protocol_version = packet.version; @@ -38,7 +38,7 @@ static void on_status_response_packet(const protocol::StatusResponse& packet) enet_peer_disconnect(packet.peer, protocol::CHANNEL); } -void bother::init(void) +void gui::bother::init(void) { bother_host = enet_host_create(nullptr, BOTHER_PEERS, 1, 0, 0); bother_dispatcher.clear(); @@ -47,14 +47,14 @@ void bother::init(void) bother_dispatcher.sink<protocol::StatusResponse>().connect<&on_status_response_packet>(); } -void bother::shutdown(void) +void gui::bother::shutdown(void) { enet_host_destroy(bother_host); bother_dispatcher.clear(); bother_set.clear(); } -void bother::update_late(void) +void gui::bother::update_late(void) { unsigned int free_peers = 0U; @@ -117,7 +117,7 @@ void bother::update_late(void) } } -void bother::ping(unsigned int identity, const char* host, std::uint16_t port) +void gui::bother::ping(unsigned int identity, const char* host, std::uint16_t port) { if(bother_set.count(identity)) { // Already in the process @@ -139,7 +139,7 @@ void bother::ping(unsigned int identity, const char* host, std::uint16_t port) bother_queue.push_back(item); } -void bother::cancel(unsigned int identity) +void gui::bother::cancel(unsigned int identity) { bother_set.erase(identity); diff --git a/src/game/client/bother.hh b/game/client/gui/bother.hh index 2ff44cf..c10bf8a 100644 --- a/src/game/client/bother.hh +++ b/game/client/gui/bother.hh @@ -2,6 +2,8 @@ #define CLIENT_BOTHER_HH 1 #pragma once +namespace gui +{ struct BotherResponseEvent final { unsigned int identity; bool is_server_unreachable; @@ -10,14 +12,15 @@ struct BotherResponseEvent final { std::uint16_t max_players; std::string motd; }; +} // namespace gui -namespace bother +namespace gui::bother { void init(void); void shutdown(void); void update_late(void); void ping(unsigned int identity, const char* host, std::uint16_t port); void cancel(unsigned int identity); -} // namespace bother +} // namespace gui::bother #endif // CLIENT_BOTHER_HH diff --git a/src/game/client/chat.cc b/game/client/gui/chat.cc index 2dfea38..3cf0958 100644 --- a/src/game/client/chat.cc +++ b/game/client/gui/chat.cc @@ -1,23 +1,26 @@ #include "client/pch.hh" -#include "client/chat.hh" +#include "client/gui/chat.hh" -#include "core/config.hh" -#include "core/resource.hh" -#include "core/strtools.hh" +#include "core/config/number.hh" +#include "core/config/string.hh" +#include "core/io/config_map.hh" +#include "core/resource/resource.hh" +#include "core/utils/string.hh" #include "shared/protocol.hh" +#include "client/config/keybind.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/gui/settings.hh" +#include "client/io/glfw.hh" +#include "client/resource/sound_effect.hh" +#include "client/sound/sound.hh" + #include "client/game.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/keybind.hh" -#include "client/language.hh" #include "client/session.hh" -#include "client/settings.hh" -#include "client/sound.hh" -#include "client/sound_effect.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; constexpr static unsigned int MAX_HISTORY_SIZE = 128U; @@ -28,8 +31,8 @@ struct GuiChatMessage final { ImVec4 color; }; -static ConfigKeyBind key_chat(GLFW_KEY_ENTER); -static ConfigUnsigned history_size(32U, 0U, MAX_HISTORY_SIZE); +static config::KeyBind key_chat(GLFW_KEY_ENTER); +static config::Unsigned history_size(32U, 0U, MAX_HISTORY_SIZE); static std::deque<GuiChatMessage> history; static std::string chat_input; @@ -54,7 +57,7 @@ static void append_player_join(const std::string& sender) { GuiChatMessage message; message.spawn = globals::curtime; - message.text = std::format("{} {}", sender, language::resolve("chat.client_join")); + message.text = std::format("{} {}", sender, gui::language::resolve("chat.client_join")); message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); history.push_back(message); @@ -67,7 +70,7 @@ static void append_player_leave(const std::string& sender, const std::string& re { GuiChatMessage message; message.spawn = globals::curtime; - message.text = std::format("{} {} ({})", sender, language::resolve("chat.client_left"), language::resolve(reason.c_str())); + message.text = std::format("{} {} ({})", sender, gui::language::resolve("chat.client_left"), gui::language::resolve(reason.c_str())); message.color = ImGui::GetStyleColorVec4(ImGuiCol_DragDropTarget); history.push_back(message); @@ -94,11 +97,11 @@ static void on_chat_message_packet(const protocol::ChatMessage& packet) } } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if(event.action == GLFW_PRESS) { if((event.key == GLFW_KEY_ENTER) && (globals::gui_screen == GUI_CHAT)) { - if(!strtools::is_whitespace(chat_input)) { + if(!utils::is_whitespace(chat_input)) { protocol::ChatMessage packet; packet.type = protocol::ChatMessage::TEXT_MESSAGE; packet.sender = client_game::username.get(); @@ -127,7 +130,7 @@ static void on_glfw_key(const GlfwKeyEvent& event) } } -void client_chat::init(void) +void gui::client_chat::init(void) { globals::client_config.add_value("chat.key", key_chat); globals::client_config.add_value("chat.history_size", history_size); @@ -136,28 +139,28 @@ void client_chat::init(void) settings::add_slider(1, history_size, settings_location::VIDEO_GUI, "chat.history_size", false); globals::dispatcher.sink<protocol::ChatMessage>().connect<&on_chat_message_packet>(); - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); sfx_chat_message = resource::load<SoundEffect>("sounds/ui/chat_message.wav"); } -void client_chat::init_late(void) +void gui::client_chat::init_late(void) { } -void client_chat::shutdown(void) +void gui::client_chat::shutdown(void) { sfx_chat_message = nullptr; } -void client_chat::update(void) +void gui::client_chat::update(void) { while(history.size() > history_size.get_value()) { history.pop_front(); } } -void client_chat::layout(void) +void gui::client_chat::layout(void) { auto viewport = ImGui::GetMainViewport(); auto window_start = ImVec2(0.0f, 0.0f); @@ -232,12 +235,12 @@ void client_chat::layout(void) ImGui::PopFont(); } -void client_chat::clear(void) +void gui::client_chat::clear(void) { history.clear(); } -void client_chat::refresh_timings(void) +void gui::client_chat::refresh_timings(void) { for(auto it = history.begin(); it < history.end(); ++it) { // Reset the spawn time so the fadeout timer @@ -246,7 +249,7 @@ void client_chat::refresh_timings(void) } } -void client_chat::print(const std::string& text) +void gui::client_chat::print(const std::string& text) { GuiChatMessage message = {}; message.spawn = globals::curtime; diff --git a/src/game/client/chat.hh b/game/client/gui/chat.hh index 300cb1f..b56681e 100644 --- a/src/game/client/chat.hh +++ b/game/client/gui/chat.hh @@ -2,20 +2,20 @@ #define CLIENT_CHAT_HH 1 #pragma once -namespace client_chat +namespace gui::client_chat { void init(void); void init_late(void); void shutdown(void); void update(void); void layout(void); -} // namespace client_chat +} // namespace gui::client_chat -namespace client_chat +namespace gui::client_chat { void clear(void); void refresh_timings(void); void print(const std::string& string); -} // namespace client_chat +} // namespace gui::client_chat #endif // CLIENT_CHAT_HH diff --git a/src/game/client/crosshair.cc b/game/client/gui/crosshair.cc index 7a878a9..729ede9 100644 --- a/src/game/client/crosshair.cc +++ b/game/client/gui/crosshair.cc @@ -1,17 +1,18 @@ #include "client/pch.hh" -#include "client/crosshair.hh" +#include "client/gui/crosshair.hh" -#include "core/constexpr.hh" -#include "core/resource.hh" +#include "core/math/constexpr.hh" +#include "core/resource/resource.hh" + +#include "client/resource/texture_gui.hh" #include "client/globals.hh" #include "client/session.hh" -#include "client/texture_gui.hh" static resource_ptr<TextureGUI> texture; -void crosshair::init(void) +void gui::crosshair::init(void) { texture = resource::load<TextureGUI>( "textures/gui/hud_crosshair.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T | TEXTURE_GUI_LOAD_VFLIP); @@ -22,18 +23,18 @@ void crosshair::init(void) } } -void crosshair::shutdown(void) +void gui::crosshair::shutdown(void) { texture = nullptr; } -void crosshair::layout(void) +void gui::crosshair::layout(void) { auto viewport = ImGui::GetMainViewport(); auto draw_list = ImGui::GetForegroundDrawList(); - auto scaled_width = vx::max<int>(texture->size.x, globals::gui_scale * texture->size.x / 2); - auto scaled_height = vx::max<int>(texture->size.y, globals::gui_scale * texture->size.y / 2); + auto scaled_width = math::max<int>(texture->size.x, globals::gui_scale * texture->size.x / 2); + auto scaled_height = math::max<int>(texture->size.y, globals::gui_scale * texture->size.y / 2); auto start = ImVec2( static_cast<int>(0.5f * viewport->Size.x) - (scaled_width / 2), static_cast<int>(0.5f * viewport->Size.y) - (scaled_height / 2)); auto end = ImVec2(start.x + scaled_width, start.y + scaled_height); diff --git a/src/game/client/crosshair.hh b/game/client/gui/crosshair.hh index ad202d1..d29a661 100644 --- a/src/game/client/crosshair.hh +++ b/game/client/gui/crosshair.hh @@ -2,11 +2,11 @@ #define CLIENT_CROSSHAIR_HH 1 #pragma once -namespace crosshair +namespace gui::crosshair { void init(void); void shutdown(void); void layout(void); -} // namespace crosshair +} // namespace gui::crosshair #endif // CLIENT_CROSSHAIR_HH diff --git a/src/game/client/direct_connection.cc b/game/client/gui/direct_connection.cc index c2efc4e..8a09e48 100644 --- a/src/game/client/direct_connection.cc +++ b/game/client/gui/direct_connection.cc @@ -1,17 +1,18 @@ #include "client/pch.hh" -#include "client/direct_connection.hh" +#include "client/gui/direct_connection.hh" -#include "core/config.hh" -#include "core/strtools.hh" +#include "core/config/boolean.hh" +#include "core/utils/string.hh" #include "shared/protocol.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/io/glfw.hh" + #include "client/game.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/language.hh" #include "client/session.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; @@ -26,7 +27,7 @@ static std::string str_password; static std::string direct_hostname; static std::string direct_password; -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if((event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { if(globals::gui_screen == GUI_DIRECT_CONNECTION) { @@ -36,19 +37,19 @@ static void on_glfw_key(const GlfwKeyEvent& event) } } -static void on_language_set(const LanguageSetEvent& event) +static void on_language_set(const gui::LanguageSetEvent& event) { - str_title = language::resolve("direct_connection.title"); - str_connect = language::resolve_gui("direct_connection.connect"); - str_cancel = language::resolve_gui("direct_connection.cancel"); + str_title = gui::language::resolve("direct_connection.title"); + str_connect = gui::language::resolve_gui("direct_connection.connect"); + str_cancel = gui::language::resolve_gui("direct_connection.cancel"); - str_hostname = language::resolve("direct_connection.hostname"); - str_password = language::resolve("direct_connection.password"); + str_hostname = gui::language::resolve("direct_connection.hostname"); + str_password = gui::language::resolve("direct_connection.password"); } static void connect_to_server(void) { - auto parts = strtools::split(direct_hostname, ":"); + auto parts = utils::split(direct_hostname, ":"); std::string parsed_hostname; std::uint16_t parsed_port; @@ -59,7 +60,7 @@ static void connect_to_server(void) } if(parts.size() >= 2) { - parsed_port = vx::clamp<std::uint16_t>(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); + parsed_port = math::clamp<std::uint16_t>(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); } else { parsed_port = protocol::PORT; } @@ -67,13 +68,13 @@ static void connect_to_server(void) session::connect(parsed_hostname.c_str(), parsed_port, direct_password.c_str()); } -void direct_connection::init(void) +void gui::direct_connection::init(void) { - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); globals::dispatcher.sink<LanguageSetEvent>().connect<&on_language_set>(); } -void direct_connection::layout(void) +void gui::direct_connection::layout(void) { auto viewport = ImGui::GetMainViewport(); auto window_start = ImVec2(0.25f * viewport->Size.x, 0.20f * viewport->Size.y); @@ -123,7 +124,7 @@ void direct_connection::layout(void) ImGui::Dummy(ImVec2(0.0f, 4.0f * globals::gui_scale)); - ImGui::BeginDisabled(strtools::is_whitespace(direct_hostname)); + ImGui::BeginDisabled(utils::is_whitespace(direct_hostname)); if(ImGui::Button(str_connect.c_str(), ImVec2(avail_width, 0.0f))) { connect_to_server(); diff --git a/src/game/client/direct_connection.hh b/game/client/gui/direct_connection.hh index f07f57d..7331843 100644 --- a/src/game/client/direct_connection.hh +++ b/game/client/gui/direct_connection.hh @@ -2,10 +2,10 @@ #define CLIENT_DIRECT_CONNECTION_HH 1 #pragma once -namespace direct_connection +namespace gui::direct_connection { void init(void); void layout(void); -} // namespace direct_connection +} // namespace gui::direct_connection #endif // CLIENT_DIRECT_CONNECTION_HH diff --git a/src/game/client/gui_screen.hh b/game/client/gui/gui_screen.hh index b36e6b2..b36e6b2 100644 --- a/src/game/client/gui_screen.hh +++ b/game/client/gui/gui_screen.hh diff --git a/src/game/client/hotbar.cc b/game/client/gui/hotbar.cc index 6481f08..a7c3c62 100644 --- a/src/game/client/hotbar.cc +++ b/game/client/gui/hotbar.cc @@ -1,28 +1,29 @@ #include "client/pch.hh" -#include "client/hotbar.hh" +#include "client/gui/hotbar.hh" -#include "core/config.hh" -#include "core/resource.hh" +#include "core/io/config_map.hh" +#include "core/resource/resource.hh" -#include "shared/item_registry.hh" +#include "shared/world/item_registry.hh" + +#include "client/config/keybind.hh" +#include "client/gui/settings.hh" +#include "client/gui/status_lines.hh" +#include "client/io/glfw.hh" +#include "client/resource/texture_gui.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/keybind.hh" -#include "client/settings.hh" -#include "client/status_lines.hh" -#include "client/texture_gui.hh" constexpr static float ITEM_SIZE = 20.0f; constexpr static float ITEM_PADDING = 2.0f; constexpr static float SELECTOR_PADDING = 1.0f; constexpr static float HOTBAR_PADDING = 2.0f; -unsigned int hotbar::active_slot = 0U; -item_id hotbar::slots[HOTBAR_SIZE]; +unsigned int gui::hotbar::active_slot = 0U; +item_id gui::hotbar::slots[HOTBAR_SIZE]; -static ConfigKeyBind hotbar_keys[HOTBAR_SIZE]; +static config::KeyBind hotbar_keys[HOTBAR_SIZE]; static resource_ptr<TextureGUI> hotbar_background; static resource_ptr<TextureGUI> hotbar_selector; @@ -35,23 +36,23 @@ static ImU32 get_color_alpha(ImGuiCol style_color, float alpha) static void update_hotbar_item(void) { - if(hotbar::slots[hotbar::active_slot] == NULL_ITEM_ID) { - status_lines::unset(STATUS_HOTBAR); + if(gui::hotbar::slots[gui::hotbar::active_slot] == NULL_ITEM_ID) { + gui::status_lines::unset(gui::STATUS_HOTBAR); return; } - if(auto info = item_registry::find(hotbar::slots[hotbar::active_slot])) { - status_lines::set(STATUS_HOTBAR, info->name, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); + if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { + gui::status_lines::set(gui::STATUS_HOTBAR, info->name, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); return; } } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if((event.action == GLFW_PRESS) && !globals::gui_screen) { for(unsigned int i = 0U; i < HOTBAR_SIZE; ++i) { if(hotbar_keys[i].equals(event.key)) { - hotbar::active_slot = i; + gui::hotbar::active_slot = i; update_hotbar_item(); break; } @@ -59,22 +60,22 @@ static void on_glfw_key(const GlfwKeyEvent& event) } } -static void on_glfw_scroll(const GlfwScrollEvent& event) +static void on_glfw_scroll(const io::GlfwScrollEvent& event) { if(!globals::gui_screen) { if(event.dy < 0.0) { - hotbar::next_slot(); + gui::hotbar::next_slot(); return; } if(event.dy > 0.0) { - hotbar::prev_slot(); + gui::hotbar::prev_slot(); return; } } } -void hotbar::init(void) +void gui::hotbar::init(void) { hotbar_keys[0].set_key(GLFW_KEY_1); hotbar_keys[1].set_key(GLFW_KEY_2); @@ -109,17 +110,17 @@ void hotbar::init(void) hotbar_background = resource::load<TextureGUI>("textures/gui/hud_hotbar.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); hotbar_selector = resource::load<TextureGUI>("textures/gui/hud_selector.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); - globals::dispatcher.sink<GlfwScrollEvent>().connect<&on_glfw_scroll>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwScrollEvent>().connect<&on_glfw_scroll>(); } -void hotbar::shutdown(void) +void gui::hotbar::shutdown(void) { hotbar_background = nullptr; hotbar_selector = nullptr; } -void hotbar::layout(void) +void gui::hotbar::layout(void) { auto& style = ImGui::GetStyle(); @@ -139,7 +140,7 @@ void hotbar::layout(void) auto selector_padding_a = SELECTOR_PADDING * globals::gui_scale; auto selector_padding_b = SELECTOR_PADDING * globals::gui_scale * 2.0f; auto selector_start = ImVec2( - background_start.x + hotbar::active_slot * item_size - selector_padding_a, background_start.y - selector_padding_a); + background_start.x + gui::hotbar::active_slot * item_size - selector_padding_a, background_start.y - selector_padding_a); auto selector_end = ImVec2(selector_start.x + item_size + selector_padding_b, selector_start.y + item_size + selector_padding_b); draw_list->AddImage(hotbar_selector->handle, selector_start, selector_end); @@ -149,7 +150,7 @@ void hotbar::layout(void) // Draw individual item textures in the hotbar for(std::size_t i = 0; i < HOTBAR_SIZE; ++i) { - const auto info = item_registry::find(hotbar::slots[i]); + const auto info = world::item_registry::find(gui::hotbar::slots[i]); if((info == nullptr) || (info->cached_texture == nullptr)) { // There's either no item in the slot @@ -163,16 +164,16 @@ void hotbar::layout(void) } } -void hotbar::next_slot(void) +void gui::hotbar::next_slot(void) { - hotbar::active_slot += 1U; - hotbar::active_slot %= HOTBAR_SIZE; + gui::hotbar::active_slot += 1U; + gui::hotbar::active_slot %= HOTBAR_SIZE; update_hotbar_item(); } -void hotbar::prev_slot(void) +void gui::hotbar::prev_slot(void) { - hotbar::active_slot += HOTBAR_SIZE - 1U; - hotbar::active_slot %= HOTBAR_SIZE; + gui::hotbar::active_slot += HOTBAR_SIZE - 1U; + gui::hotbar::active_slot %= HOTBAR_SIZE; update_hotbar_item(); } diff --git a/src/game/client/hotbar.hh b/game/client/gui/hotbar.hh index e16f3fa..4712ee5 100644 --- a/src/game/client/hotbar.hh +++ b/game/client/gui/hotbar.hh @@ -9,23 +9,23 @@ constexpr static unsigned int HOTBAR_SIZE = 9U; -namespace hotbar +namespace gui::hotbar { extern unsigned int active_slot; extern item_id slots[HOTBAR_SIZE]; -} // namespace hotbar +} // namespace gui::hotbar -namespace hotbar +namespace gui::hotbar { void init(void); void shutdown(void); void layout(void); -} // namespace hotbar +} // namespace gui::hotbar -namespace hotbar +namespace gui::hotbar { void next_slot(void); void prev_slot(void); -} // namespace hotbar +} // namespace gui::hotbar #endif // CLIENT_HOTBAR_HH diff --git a/src/game/client/imdraw_ext.cc b/game/client/gui/imdraw_ext.cc index 67df3c8..e6db148 100644 --- a/src/game/client/imdraw_ext.cc +++ b/game/client/gui/imdraw_ext.cc @@ -1,10 +1,10 @@ #include "client/pch.hh" -#include "client/imdraw_ext.hh" +#include "client/gui/imdraw_ext.hh" #include "client/globals.hh" -void imdraw_ext::text_shadow( +void gui::imdraw_ext::text_shadow( const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, ImDrawList* draw_list) { const auto shadow_position = ImVec2(position.x + 0.5f * globals::gui_scale, position.y + 0.5f * globals::gui_scale); diff --git a/src/game/client/imdraw_ext.hh b/game/client/gui/imdraw_ext.hh index 04d3b68..7f0abfb 100644 --- a/src/game/client/imdraw_ext.hh +++ b/game/client/gui/imdraw_ext.hh @@ -2,10 +2,10 @@ #define CLIENT_IMDRAW_EXT_HH 1 #pragma once -namespace imdraw_ext +namespace gui::imdraw_ext { void text_shadow( const std::string& text, const ImVec2& position, ImU32 text_color, ImU32 shadow_color, ImFont* font, ImDrawList* draw_list); -} // namespace imdraw_ext +} // namespace gui::imdraw_ext #endif // CLIENT_IMDRAW_EXT_HH diff --git a/src/game/client/language.cc b/game/client/gui/language.cc index 2ae0bc6..04906b4 100644 --- a/src/game/client/language.cc +++ b/game/client/gui/language.cc @@ -1,11 +1,13 @@ #include "client/pch.hh" -#include "client/language.hh" +#include "client/gui/language.hh" -#include "core/config.hh" +#include "core/config/string.hh" +#include "core/io/config_map.hh" + +#include "client/gui/settings.hh" #include "client/globals.hh" -#include "client/settings.hh" constexpr static const char* DEFAULT_LANGUAGE = "en_US"; @@ -15,20 +17,20 @@ constexpr static const char* DEFAULT_LANGUAGE = "en_US"; // system knows what language it can load and will act accordingly constexpr static const char* MANIFEST_PATH = "lang/manifest.json"; -static LanguageManifest manifest; -static LanguageIterator current_language; +static gui::LanguageManifest manifest; +static gui::LanguageIterator current_language; static std::unordered_map<std::string, std::string> language_map; -static std::unordered_map<std::string, LanguageIterator> ietf_map; -static ConfigString config_language(DEFAULT_LANGUAGE); +static std::unordered_map<std::string, gui::LanguageIterator> ietf_map; +static config::String config_language(DEFAULT_LANGUAGE); -static void send_language_event(LanguageIterator new_language) +static void send_language_event(gui::LanguageIterator new_language) { - LanguageSetEvent event; + gui::LanguageSetEvent event; event.new_language = new_language; globals::dispatcher.trigger(event); } -void language::init(void) +void gui::language::init(void) { globals::client_config.add_value("language", config_language); @@ -81,19 +83,19 @@ void language::init(void) current_language = manifest.cend(); } -void language::init_late(void) +void gui::language::init_late(void) { auto user_language = ietf_map.find(config_language.get()); if(user_language != ietf_map.cend()) { - language::set(user_language->second); + gui::language::set(user_language->second); return; } auto fallback = ietf_map.find(DEFAULT_LANGUAGE); if(fallback != ietf_map.cend()) { - language::set(fallback->second); + gui::language::set(fallback->second); return; } @@ -102,7 +104,7 @@ void language::init_late(void) std::terminate(); } -void language::set(LanguageIterator new_language) +void gui::language::set(LanguageIterator new_language) { if(new_language != manifest.cend()) { auto path = std::format("lang/lang.{}.json", new_language->ietf); @@ -152,12 +154,12 @@ void language::set(LanguageIterator new_language) send_language_event(new_language); } -LanguageIterator language::get_current(void) +gui::LanguageIterator gui::language::get_current(void) { return current_language; } -LanguageIterator language::find(const char* ietf) +gui::LanguageIterator gui::language::find(const char* ietf) { const auto it = ietf_map.find(ietf); if(it != ietf_map.cend()) { @@ -167,17 +169,17 @@ LanguageIterator language::find(const char* ietf) } } -LanguageIterator language::cbegin(void) +gui::LanguageIterator gui::language::cbegin(void) { return manifest.cbegin(); } -LanguageIterator language::cend(void) +gui::LanguageIterator gui::language::cend(void) { return manifest.cend(); } -const char* language::resolve(const char* key) +const char* gui::language::resolve(const char* key) { const auto it = language_map.find(key); if(it != language_map.cend()) { @@ -187,10 +189,10 @@ const char* language::resolve(const char* key) } } -std::string language::resolve_gui(const char* key) +std::string gui::language::resolve_gui(const char* key) { // We need window tags to retain their hierarchy when a language // dynamically changes; ImGui allows to provide hidden unique identifiers // to GUI primitives that have their name change dynamically, so we're using this - return std::format("{}###{}", language::resolve(key), key); + return std::format("{}###{}", gui::language::resolve(key), key); } diff --git a/src/game/client/language.hh b/game/client/gui/language.hh index 44f6834..d54208a 100644 --- a/src/game/client/language.hh +++ b/game/client/gui/language.hh @@ -2,6 +2,8 @@ #define CLIENT_LANGUAGE_HH 1 #pragma once +namespace gui +{ struct LanguageInfo final { std::string endonym; // Language's self-name std::string display; // Display for the settings GUI @@ -14,30 +16,31 @@ using LanguageIterator = LanguageManifest::const_iterator; struct LanguageSetEvent final { LanguageIterator new_language; }; +} // namespace gui -namespace language +namespace gui::language { void init(void); void init_late(void); -} // namespace language +} // namespace gui::language -namespace language +namespace gui::language { void set(LanguageIterator new_language); -} // namespace language +} // namespace gui::language -namespace language +namespace gui::language { LanguageIterator get_current(void); LanguageIterator find(const char* ietf); LanguageIterator cbegin(void); LanguageIterator cend(void); -} // namespace language +} // namespace gui::language -namespace language +namespace gui::language { const char* resolve(const char* key); std::string resolve_gui(const char* key); -} // namespace language +} // namespace gui::language #endif // CLIENT_LANGUAGE_HH diff --git a/src/game/client/main_menu.cc b/game/client/gui/main_menu.cc index 382f540..7af1f10 100644 --- a/src/game/client/main_menu.cc +++ b/game/client/gui/main_menu.cc @@ -1,18 +1,20 @@ #include "client/pch.hh" -#include "client/main_menu.hh" +#include "client/gui/main_menu.hh" + +#include "core/math/constexpr.hh" +#include "core/resource/resource.hh" -#include "core/constexpr.hh" -#include "core/resource.hh" #include "core/version.hh" -#include "client/glfw.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/gui/window_title.hh" +#include "client/io/glfw.hh" +#include "client/resource/texture_gui.hh" + #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/language.hh" #include "client/session.hh" -#include "client/texture_gui.hh" -#include "client/window_title.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; @@ -25,7 +27,7 @@ static std::string str_quit; static resource_ptr<TextureGUI> title; static float title_aspect; -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if(session::is_ingame() && (event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { if(globals::gui_screen == GUI_SCREEN_NONE) { @@ -40,16 +42,16 @@ static void on_glfw_key(const GlfwKeyEvent& event) } } -static void on_language_set(const LanguageSetEvent& event) +static void on_language_set(const gui::LanguageSetEvent& event) { - str_play = language::resolve_gui("main_menu.play"); - str_resume = language::resolve_gui("main_menu.resume"); - str_settings = language::resolve("main_menu.settings"); - str_leave = language::resolve("main_menu.leave"); - str_quit = language::resolve("main_menu.quit"); + str_play = gui::language::resolve_gui("main_menu.play"); + str_resume = gui::language::resolve_gui("main_menu.resume"); + str_settings = gui::language::resolve("main_menu.settings"); + str_leave = gui::language::resolve("main_menu.leave"); + str_quit = gui::language::resolve("main_menu.quit"); } -void main_menu::init(void) +void gui::main_menu::init(void) { title = resource::load<TextureGUI>("textures/gui/menu_title.png", TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); @@ -64,16 +66,16 @@ void main_menu::init(void) title_aspect = static_cast<float>(title->size.y) / static_cast<float>(title->size.x); } - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); globals::dispatcher.sink<LanguageSetEvent>().connect<&on_language_set>(); } -void main_menu::shutdown(void) +void gui::main_menu::shutdown(void) { title = nullptr; } -void main_menu::layout(void) +void gui::main_menu::layout(void) { const auto viewport = ImGui::GetMainViewport(); const auto window_start = ImVec2(0.0f, viewport->Size.y * 0.15f); @@ -89,7 +91,7 @@ void main_menu::layout(void) ImGui::Dummy(ImVec2(0.0f, 32.0f * globals::gui_scale)); } else { auto reference_height = 0.225f * window_size.y; - auto image_width = vx::min(window_size.x, reference_height * title_aspect); + auto image_width = math::min(window_size.x, reference_height * title_aspect); auto image_height = image_width / title_aspect; ImGui::SetCursorPosX(0.5f * (window_size.x - image_width)); ImGui::Image(title->handle, ImVec2(image_width, image_height)); @@ -132,7 +134,7 @@ void main_menu::layout(void) if(ImGui::Button(str_leave.c_str(), ImVec2(button_width, 0.0f))) { session::disconnect("protocol.client_disconnect"); globals::gui_screen = GUI_PLAY_MENU; - window_title::update(); + gui::window_title::update(); } ImGui::Spacing(); diff --git a/src/game/client/main_menu.hh b/game/client/gui/main_menu.hh index fea0ba9..c93e284 100644 --- a/src/game/client/main_menu.hh +++ b/game/client/gui/main_menu.hh @@ -2,11 +2,11 @@ #define CLIENT_MAIN_MENU_HH 1 #pragma once -namespace main_menu +namespace gui::main_menu { void init(void); void shutdown(void); void layout(void); -} // namespace main_menu +} // namespace gui::main_menu #endif // CLIENT_MAIN_MENU_HH diff --git a/src/game/client/message_box.cc b/game/client/gui/message_box.cc index da1b715..615281b 100644 --- a/src/game/client/message_box.cc +++ b/game/client/gui/message_box.cc @@ -1,15 +1,16 @@ #include "client/pch.hh" -#include "client/message_box.hh" +#include "client/gui/message_box.hh" + +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/language.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; struct Button final { - message_box_action action; + gui::message_box_action action; std::string str_title; }; @@ -17,14 +18,14 @@ static std::string str_title; static std::string str_subtitle; static std::vector<Button> buttons; -void message_box::init(void) +void gui::message_box::init(void) { str_title = std::string(); str_subtitle = std::string(); buttons.clear(); } -void message_box::layout(void) +void gui::message_box::layout(void) { const auto viewport = ImGui::GetMainViewport(); const auto window_start = ImVec2(0.0f, viewport->Size.y * 0.30f); @@ -67,27 +68,27 @@ void message_box::layout(void) ImGui::End(); } -void message_box::reset(void) +void gui::message_box::reset(void) { str_title.clear(); str_subtitle.clear(); buttons.clear(); } -void message_box::set_title(const char* title) +void gui::message_box::set_title(const char* title) { - str_title = language::resolve(title); + str_title = gui::language::resolve(title); } -void message_box::set_subtitle(const char* subtitle) +void gui::message_box::set_subtitle(const char* subtitle) { - str_subtitle = language::resolve(subtitle); + str_subtitle = gui::language::resolve(subtitle); } -void message_box::add_button(const char* text, const message_box_action& action) +void gui::message_box::add_button(const char* text, const message_box_action& action) { Button button = {}; - button.str_title = std::format("{}###MessageBox_Button{}", language::resolve(text), buttons.size()); + button.str_title = std::format("{}###MessageBox_Button{}", gui::language::resolve(text), buttons.size()); button.action = action; buttons.push_back(button); diff --git a/src/game/client/message_box.hh b/game/client/gui/message_box.hh index ad5cb57..c5545fc 100644 --- a/src/game/client/message_box.hh +++ b/game/client/gui/message_box.hh @@ -2,20 +2,23 @@ #define CLIENT_MESSAGE_BOX_HH 1 #pragma once +namespace gui +{ using message_box_action = void (*)(void); +} // namespace gui -namespace message_box +namespace gui::message_box { void init(void); void layout(void); void reset(void); -} // namespace message_box +} // namespace gui::message_box -namespace message_box +namespace gui::message_box { void set_title(const char* title); void set_subtitle(const char* subtitle); void add_button(const char* text, const message_box_action& action); -} // namespace message_box +} // namespace gui::message_box #endif // CLIENT_MESSAGE_BOX_HH diff --git a/src/game/client/metrics.cc b/game/client/gui/metrics.cc index 8b5b7dd..350208c 100644 --- a/src/game/client/metrics.cc +++ b/game/client/gui/metrics.cc @@ -1,20 +1,22 @@ #include "client/pch.hh" -#include "client/metrics.hh" +#include "client/gui/metrics.hh" #include "core/version.hh" +#include "shared/entity/grounded.hh" +#include "shared/entity/head.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/grounded.hh" -#include "shared/head.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "client/camera.hh" +#include "client/entity/camera.hh" +#include "client/gui/imdraw_ext.hh" + #include "client/game.hh" #include "client/globals.hh" -#include "client/imdraw_ext.hh" #include "client/session.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = @@ -23,13 +25,13 @@ constexpr static ImGuiWindowFlags WINDOW_FLAGS = static std::basic_string<GLubyte> r_version; static std::basic_string<GLubyte> r_renderer; -void metrics::init(void) +void gui::metrics::init(void) { r_version = std::basic_string<GLubyte>(glGetString(GL_VERSION)); r_renderer = std::basic_string<GLubyte>(glGetString(GL_RENDERER)); } -void metrics::layout(void) +void gui::metrics::layout(void) { if(!session::is_ingame()) { // Sanity check; we are checking this @@ -49,50 +51,50 @@ void metrics::layout(void) // Draw version auto version_line = std::format("Voxelius {}", project_version_string); - imdraw_ext::text_shadow(version_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(version_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += 1.5f * y_step; // Draw client-side window framerate metrics auto window_framerate = 1.0f / globals::window_frametime_avg; auto window_frametime = 1000.0f * globals::window_frametime_avg; auto window_fps_line = std::format("{:.02f} FPS [{:.02f} ms]", window_framerate, window_frametime); - imdraw_ext::text_shadow(window_fps_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(window_fps_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += y_step; // Draw world rendering metrics auto drawcall_line = std::format("World: {} DC / {} TRI", globals::num_drawcalls, globals::num_triangles); - imdraw_ext::text_shadow(drawcall_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(drawcall_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += y_step; // Draw OpenGL version string auto r_version_line = std::format("GL_VERSION: {}", reinterpret_cast<const char*>(r_version.c_str())); - imdraw_ext::text_shadow(r_version_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(r_version_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += y_step; // Draw OpenGL renderer string auto r_renderer_line = std::format("GL_RENDERER: {}", reinterpret_cast<const char*>(r_renderer.c_str())); - imdraw_ext::text_shadow(r_renderer_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(r_renderer_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += 1.5f * y_step; - const auto& head = globals::dimension->entities.get<HeadComponent>(globals::player); - const auto& transform = globals::dimension->entities.get<TransformComponent>(globals::player); - const auto& velocity = globals::dimension->entities.get<VelocityComponent>(globals::player); + const auto& head = globals::dimension->entities.get<entity::Head>(globals::player); + const auto& transform = globals::dimension->entities.get<entity::Transform>(globals::player); + const auto& velocity = globals::dimension->entities.get<entity::Velocity>(globals::player); // Draw player voxel position auto voxel_position = coord::to_voxel(transform.chunk, transform.local); auto voxel_line = std::format("voxel: [{} {} {}]", voxel_position.x, voxel_position.y, voxel_position.z); - imdraw_ext::text_shadow(voxel_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(voxel_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += y_step; // Draw player world position auto world_line = std::format("world: [{} {} {}] [{:.03f} {:.03f} {:.03f}]", transform.chunk.x, transform.chunk.y, transform.chunk.z, transform.local.x, transform.local.y, transform.local.z); - imdraw_ext::text_shadow(world_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(world_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += y_step; // Draw player look angles auto angles = glm::degrees(transform.angles + head.angles); auto angle_line = std::format("angle: [{: .03f} {: .03f} {: .03f}]", angles[0], angles[1], angles[2]); - imdraw_ext::text_shadow(angle_line, position, text_color, shadow_color, globals::font_debug, draw_list); + gui::imdraw_ext::text_shadow(angle_line, position, text_color, shadow_color, globals::font_debug, draw_list); position.y += y_step; } diff --git a/src/game/client/metrics.hh b/game/client/gui/metrics.hh index 1f13761..57e1108 100644 --- a/src/game/client/metrics.hh +++ b/game/client/gui/metrics.hh @@ -2,10 +2,10 @@ #define CLIENT_METRICS_HH 1 #pragma once -namespace metrics +namespace gui::metrics { void init(void); void layout(void); -} // namespace metrics +} // namespace gui::metrics #endif // CLIENT_METRICS_HH diff --git a/src/game/client/play_menu.cc b/game/client/gui/play_menu.cc index 51ce30c..922dd4e 100644 --- a/src/game/client/play_menu.cc +++ b/game/client/gui/play_menu.cc @@ -1,19 +1,21 @@ #include "client/pch.hh" -#include "client/play_menu.hh" +#include "client/gui/play_menu.hh" -#include "core/config.hh" -#include "core/constexpr.hh" -#include "core/strtools.hh" +#include "core/config/boolean.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" +#include "core/utils/string.hh" #include "shared/protocol.hh" -#include "client/bother.hh" +#include "client/gui/bother.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/io/glfw.hh" + #include "client/game.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/language.hh" #include "client/session.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; @@ -79,7 +81,7 @@ static bool needs_focus; static void parse_hostname(ServerStatusItem* item, const std::string& hostname) { - auto parts = strtools::split(hostname, ":"); + auto parts = utils::split(hostname, ":"); if(!parts[0].empty()) { item->hostname = parts[0]; @@ -88,7 +90,7 @@ static void parse_hostname(ServerStatusItem* item, const std::string& hostname) } if(parts.size() >= 2) { - item->port = vx::clamp<std::uint16_t>(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); + item->port = math::clamp<std::uint16_t>(strtoul(parts[1].c_str(), nullptr, 10), 1024, UINT16_MAX); } else { item->port = protocol::PORT; } @@ -135,7 +137,7 @@ static void edit_selected_server(void) static void remove_selected_server(void) { - bother::cancel(selected_server->identity); + gui::bother::cancel(selected_server->identity); for(auto it = servers_deque.cbegin(); it != servers_deque.cend(); ++it) { if(selected_server == (*it)) { @@ -154,7 +156,7 @@ static void join_selected_server(void) } } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if((event.key == GLFW_KEY_ESCAPE) && (event.action == GLFW_PRESS)) { if(globals::gui_screen == GUI_PLAY_MENU) { @@ -178,26 +180,26 @@ static void on_glfw_key(const GlfwKeyEvent& event) } } -static void on_language_set(const LanguageSetEvent& event) +static void on_language_set(const gui::LanguageSetEvent& event) { - str_tab_servers = language::resolve_gui("play_menu.tab.servers"); + str_tab_servers = gui::language::resolve_gui("play_menu.tab.servers"); - str_join = language::resolve_gui("play_menu.join"); - str_connect = language::resolve_gui("play_menu.connect"); - str_add = language::resolve_gui("play_menu.add"); - str_edit = language::resolve_gui("play_menu.edit"); - str_remove = language::resolve_gui("play_menu.remove"); - str_refresh = language::resolve_gui("play_menu.refresh"); + str_join = gui::language::resolve_gui("play_menu.join"); + str_connect = gui::language::resolve_gui("play_menu.connect"); + str_add = gui::language::resolve_gui("play_menu.add"); + str_edit = gui::language::resolve_gui("play_menu.edit"); + str_remove = gui::language::resolve_gui("play_menu.remove"); + str_refresh = gui::language::resolve_gui("play_menu.refresh"); - str_status_init = language::resolve("play_menu.status.init"); - str_status_ping = language::resolve("play_menu.status.ping"); - str_status_fail = language::resolve("play_menu.status.fail"); + str_status_init = gui::language::resolve("play_menu.status.init"); + str_status_ping = gui::language::resolve("play_menu.status.ping"); + str_status_fail = gui::language::resolve("play_menu.status.fail"); - str_outdated_client = language::resolve("play_menu.outdated_client"); - str_outdated_server = language::resolve("play_menu.outdated_server"); + str_outdated_client = gui::language::resolve("play_menu.outdated_client"); + str_outdated_server = gui::language::resolve("play_menu.outdated_server"); } -static void on_bother_response(const BotherResponseEvent& event) +static void on_bother_response(const gui::BotherResponseEvent& event) { for(auto item : servers_deque) { if(item->identity == event.identity) { @@ -314,7 +316,7 @@ static void layout_server_edit(ServerStatusItem* item) ImGui::InputText("###play_menu.servers.edit_itemname", &input_itemname); ImGui::SameLine(); - const bool ignore_input = strtools::is_whitespace(input_itemname) || input_hostname.empty(); + const bool ignore_input = utils::is_whitespace(input_itemname) || input_hostname.empty(); ImGui::BeginDisabled(ignore_input); @@ -330,7 +332,7 @@ static void layout_server_edit(ServerStatusItem* item) input_itemname.clear(); input_hostname.clear(); - bother::cancel(item->identity); + gui::bother::cancel(item->identity); } ImGui::EndDisabled(); @@ -424,14 +426,14 @@ static void layout_servers_buttons(void) if(item->status != item_status::PINGING) { if(!editing_server || item != selected_server) { item->status = item_status::UNKNOWN; - bother::cancel(item->identity); + gui::bother::cancel(item->identity); } } } } } -void play_menu::init(void) +void gui::play_menu::init(void) { if(auto file = PHYSFS_openRead(SERVERS_TXT)) { auto source = std::string(PHYSFS_fileLength(file), char(0x00)); @@ -442,7 +444,7 @@ void play_menu::init(void) auto line = std::string(); while(std::getline(stream, line)) { - auto parts = strtools::split(line, "%"); + auto parts = utils::split(line, "%"); auto item = new ServerStatusItem(); item->port = protocol::PORT; @@ -472,12 +474,12 @@ void play_menu::init(void) } } - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); globals::dispatcher.sink<LanguageSetEvent>().connect<&on_language_set>(); globals::dispatcher.sink<BotherResponseEvent>().connect<&on_bother_response>(); } -void play_menu::shutdown(void) +void gui::play_menu::shutdown(void) { std::ostringstream stream; @@ -496,7 +498,7 @@ void play_menu::shutdown(void) servers_deque.clear(); } -void play_menu::layout(void) +void gui::play_menu::layout(void) { const auto viewport = ImGui::GetMainViewport(); const auto window_start = ImVec2(viewport->Size.x * 0.05f, viewport->Size.y * 0.05f); @@ -536,11 +538,11 @@ void play_menu::layout(void) ImGui::End(); } -void play_menu::update_late(void) +void gui::play_menu::update_late(void) { for(auto item : servers_deque) { if(item->status == item_status::UNKNOWN) { - bother::ping(item->identity, item->hostname.c_str(), item->port); + gui::bother::ping(item->identity, item->hostname.c_str(), item->port); item->status = item_status::PINGING; continue; } diff --git a/src/game/client/play_menu.hh b/game/client/gui/play_menu.hh index 5714397..f63d27e 100644 --- a/src/game/client/play_menu.hh +++ b/game/client/gui/play_menu.hh @@ -2,12 +2,12 @@ #define CLIENT_PLAY_MENU_HH 1 #pragma once -namespace play_menu +namespace gui::play_menu { void init(void); void shutdown(void); void layout(void); void update_late(void); -} // namespace play_menu +} // namespace gui::play_menu #endif // CLIENT_PLAY_MENU_HH diff --git a/src/game/client/progress_bar.cc b/game/client/gui/progress_bar.cc index dfab705..2bfb69e 100644 --- a/src/game/client/progress_bar.cc +++ b/game/client/gui/progress_bar.cc @@ -1,26 +1,27 @@ #include "client/pch.hh" -#include "client/progress_bar.hh" +#include "client/gui/progress_bar.hh" -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" + +#include "client/gui/language.hh" #include "client/globals.hh" -#include "client/language.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; static std::string str_title; static std::string str_button; -static progress_bar_action button_action; +static gui::progress_bar_action button_action; -void progress_bar::init(void) +void gui::progress_bar::init(void) { str_title = "Loading"; str_button = std::string(); button_action = nullptr; } -void progress_bar::layout(void) +void gui::progress_bar::layout(void) { const auto viewport = ImGui::GetMainViewport(); const auto window_start = ImVec2(0.0f, viewport->Size.y * 0.30f); @@ -57,10 +58,10 @@ void progress_bar::layout(void) const float modifier = std::exp(-8.0f * (0.5f + 0.5f * sinval)); ImVec4 color = {}; - color.x = vx::lerp(background.x, foreground.x, modifier); - color.y = vx::lerp(background.y, foreground.y, modifier); - color.z = vx::lerp(background.z, foreground.z, modifier); - color.w = vx::lerp(background.w, foreground.w, modifier); + color.x = math::lerp(background.x, foreground.x, modifier); + color.y = math::lerp(background.y, foreground.y, modifier); + color.z = math::lerp(background.z, foreground.z, modifier); + color.w = math::lerp(background.w, foreground.w, modifier); const ImVec2 start = ImVec2(base_xpos + bar_width * i, base_ypos); const ImVec2 end = ImVec2(start.x + bar_width, start.y + bar_height); @@ -91,20 +92,20 @@ void progress_bar::layout(void) ImGui::End(); } -void progress_bar::reset(void) +void gui::progress_bar::reset(void) { str_title.clear(); str_button.clear(); button_action = nullptr; } -void progress_bar::set_title(const char* title) +void gui::progress_bar::set_title(const char* title) { - str_title = language::resolve(title); + str_title = gui::language::resolve(title); } -void progress_bar::set_button(const char* text, const progress_bar_action& action) +void gui::progress_bar::set_button(const char* text, const progress_bar_action& action) { - str_button = std::format("{}###ProgressBar_Button", language::resolve(text)); + str_button = std::format("{}###ProgressBar_Button", gui::language::resolve(text)); button_action = action; } diff --git a/src/game/client/progress_bar.hh b/game/client/gui/progress_bar.hh index 96524dd..3765543 100644 --- a/src/game/client/progress_bar.hh +++ b/game/client/gui/progress_bar.hh @@ -2,19 +2,22 @@ #define CLIENT_PROGRESS_BAR_HH 1 #pragma once +namespace gui +{ using progress_bar_action = void (*)(void); +} // namespace gui -namespace progress_bar +namespace gui::progress_bar { void init(void); void layout(void); -} // namespace progress_bar +} // namespace gui::progress_bar -namespace progress_bar +namespace gui::progress_bar { void reset(void); void set_title(const char* title); void set_button(const char* text, const progress_bar_action& action); -} // namespace progress_bar +} // namespace gui::progress_bar #endif // CLIENT_PROGRESS_BAR_HH diff --git a/src/game/client/scoreboard.cc b/game/client/gui/scoreboard.cc index 23010ea..85a982f 100644 --- a/src/game/client/scoreboard.cc +++ b/game/client/gui/scoreboard.cc @@ -1,21 +1,22 @@ #include "client/pch.hh" -#include "client/scoreboard.hh" +#include "client/gui/scoreboard.hh" -#include "core/config.hh" +#include "core/io/config_map.hh" #include "shared/protocol.hh" +#include "client/config/keybind.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/settings.hh" + #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/keybind.hh" #include "client/session.hh" -#include "client/settings.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground; -static ConfigKeyBind list_key(GLFW_KEY_TAB); +static config::KeyBind list_key(GLFW_KEY_TAB); static std::vector<std::string> usernames; static float max_username_size; @@ -26,7 +27,7 @@ static void on_scoreboard_update_packet(const protocol::ScoreboardUpdate& packet max_username_size = 0.0f; } -void scoreboard::init(void) +void gui::scoreboard::init(void) { globals::client_config.add_value("scoreboard.key", list_key); @@ -35,7 +36,7 @@ void scoreboard::init(void) globals::dispatcher.sink<protocol::ScoreboardUpdate>().connect<&on_scoreboard_update_packet>(); } -void scoreboard::layout(void) +void gui::scoreboard::layout(void) { if(globals::gui_screen == GUI_SCREEN_NONE && session::is_ingame() && glfwGetKey(globals::window, list_key.get_key()) == GLFW_PRESS) { const auto viewport = ImGui::GetMainViewport(); @@ -67,7 +68,7 @@ void scoreboard::layout(void) // Having a minimum size allows for // generally better in-game visibility - const float true_size = vx::max<float>(0.25f * window_size.x, max_username_size); + const float true_size = math::max<float>(0.25f * window_size.x, max_username_size); // Figure out username rect dimensions const float rect_start_x = 0.5f * window_size.x - 0.5f * true_size; diff --git a/src/game/client/scoreboard.hh b/game/client/gui/scoreboard.hh index 58e2f8b..af77ae2 100644 --- a/src/game/client/scoreboard.hh +++ b/game/client/gui/scoreboard.hh @@ -2,10 +2,10 @@ #define CLIENT_SCOREBOARD_HH 1 #pragma once -namespace scoreboard +namespace gui::scoreboard { void init(void); void layout(void); -} // namespace scoreboard +} // namespace gui::scoreboard #endif // CLIENT_SCOREBOARD_HH diff --git a/src/game/client/settings.cc b/game/client/gui/settings.cc index 54d6ba2..5c6b5eb 100644 --- a/src/game/client/settings.cc +++ b/game/client/gui/settings.cc @@ -1,38 +1,42 @@ #include "client/pch.hh" -#include "client/settings.hh" - -#include "core/config.hh" -#include "core/constexpr.hh" +#include "client/gui/settings.hh" + +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/config/string.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" + +#include "client/config/gamepad_axis.hh" +#include "client/config/gamepad_button.hh" +#include "client/config/keybind.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/io/gamepad.hh" +#include "client/io/glfw.hh" #include "client/const.hh" -#include "client/gamepad.hh" -#include "client/gamepad_axis.hh" -#include "client/gamepad_button.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/keybind.hh" -#include "client/language.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; constexpr static unsigned int NUM_LOCATIONS = static_cast<unsigned int>(settings_location::COUNT); enum class setting_type : unsigned int { - CHECKBOX = 0x0000U, ///< ConfigBoolean - INPUT_INT = 0x0001U, ///< ConfigNumber<int> - INPUT_FLOAT = 0x0002U, ///< ConfigNumber<float> - INPUT_UINT = 0x0003U, ///< ConfigNumber<unsigned int> - INPUT_STRING = 0x0004U, ///< ConfigString - SLIDER_INT = 0x0005U, ///< ConfigNumber<int> - SLIDER_FLOAT = 0x0006U, ///< ConfigNumber<float> - SLIDER_UINT = 0x0007U, ///< ConfigNumber<unsigned int> - STEPPER_INT = 0x0008U, ///< ConfigNumber<int> - STEPPER_UINT = 0x0009U, ///< ConfigNumber<unsigned int> - KEYBIND = 0x000AU, ///< ConfigKeyBind - GAMEPAD_AXIS = 0x000BU, ///< ConfigGamepadAxis - GAMEPAD_BUTTON = 0x000CU, ///< ConfigGamepadButton - LANGUAGE_SELECT = 0x000DU, ///< ConfigString internally + CHECKBOX = 0x0000U, ///< config::Boolean + INPUT_INT = 0x0001U, ///< config::Number<int> + INPUT_FLOAT = 0x0002U, ///< config::Number<float> + INPUT_UINT = 0x0003U, ///< config::Number<unsigned int> + INPUT_STRING = 0x0004U, ///< config::String + SLIDER_INT = 0x0005U, ///< config::Number<int> + SLIDER_FLOAT = 0x0006U, ///< config::Number<float> + SLIDER_UINT = 0x0007U, ///< config::Number<unsigned int> + STEPPER_INT = 0x0008U, ///< config::Number<int> + STEPPER_UINT = 0x0009U, ///< config::Number<unsigned int> + KEYBIND = 0x000AU, ///< config::KeyBind + GAMEPAD_AXIS = 0x000BU, ///< config::GamepadAxis + GAMEPAD_BUTTON = 0x000CU, ///< config::GamepadButton + LANGUAGE_SELECT = 0x000DU, ///< config::String internally }; class SettingValue { @@ -66,7 +70,7 @@ public: void refresh_wids(void); public: - ConfigBoolean* value; + config::Boolean* value; std::string wids[2]; }; @@ -76,7 +80,7 @@ public: virtual void layout(void) const override; public: - ConfigInt* value; + config::Int* value; }; class SettingValue_InputFloat final : public SettingValueWID { @@ -86,7 +90,7 @@ public: public: std::string format; - ConfigFloat* value; + config::Float* value; }; class SettingValue_InputUnsigned final : public SettingValueWID { @@ -95,7 +99,7 @@ public: virtual void layout(void) const override; public: - ConfigUnsigned* value; + config::Unsigned* value; }; class SettingValue_InputString final : public SettingValueWID { @@ -104,7 +108,7 @@ public: virtual void layout(void) const override; public: - ConfigString* value; + config::String* value; bool allow_whitespace; }; @@ -114,7 +118,7 @@ public: virtual void layout(void) const override; public: - ConfigInt* value; + config::Int* value; }; class SettingValue_SliderFloat final : public SettingValueWID { @@ -124,7 +128,7 @@ public: public: std::string format; - ConfigFloat* value; + config::Float* value; }; class SettingValue_SliderUnsigned final : public SettingValueWID { @@ -133,7 +137,7 @@ public: virtual void layout(void) const override; public: - ConfigUnsigned* value; + config::Unsigned* value; }; class SettingValue_StepperInt final : public SettingValue { @@ -144,7 +148,7 @@ public: public: std::vector<std::string> wids; - ConfigInt* value; + config::Int* value; }; class SettingValue_StepperUnsigned final : public SettingValue { @@ -155,7 +159,7 @@ public: public: std::vector<std::string> wids; - ConfigUnsigned* value; + config::Unsigned* value; }; class SettingValue_KeyBind final : public SettingValue { @@ -166,7 +170,7 @@ public: public: std::string wids[2]; - ConfigKeyBind* value; + config::KeyBind* value; }; class SettingValue_GamepadAxis final : public SettingValue { @@ -178,7 +182,7 @@ public: public: std::string wids[2]; std::string wid_checkbox; - ConfigGamepadAxis* value; + config::GamepadAxis* value; }; class SettingValue_GamepadButton final : public SettingValue { @@ -189,7 +193,7 @@ public: public: std::string wids[2]; - ConfigGamepadButton* value; + config::GamepadButton* value; }; class SettingValue_Language final : public SettingValueWID { @@ -387,7 +391,7 @@ void SettingValue_StepperInt::refresh_wids(void) { for(std::size_t i = 0; i < wids.size(); ++i) { auto key = std::format("settings.value.{}.{}", name, i); - wids[i] = std::format("{}###{}", language::resolve(key.c_str()), static_cast<const void*>(value)); + wids[i] = std::format("{}###{}", gui::language::resolve(key.c_str()), static_cast<const void*>(value)); } } @@ -417,7 +421,7 @@ void SettingValue_StepperUnsigned::refresh_wids(void) { for(std::size_t i = 0; i < wids.size(); ++i) { auto key = std::format("settings.value.{}.{}", name, i); - wids[i] = std::format("{}###{}", language::resolve(key.c_str()), static_cast<const void*>(value)); + wids[i] = std::format("{}###{}", gui::language::resolve(key.c_str()), static_cast<const void*>(value)); } } @@ -498,12 +502,12 @@ void SettingValue_GamepadButton::refresh_wids(void) void SettingValue_Language::layout(void) const { - auto current_language = language::get_current(); + auto current_language = gui::language::get_current(); if(ImGui::BeginCombo(wid.c_str(), current_language->endonym.c_str())) { - for(auto it = language::cbegin(); it != language::cend(); ++it) { + for(auto it = gui::language::cbegin(); it != gui::language::cend(); ++it) { if(ImGui::Selectable(it->display.c_str(), it == current_language)) { - language::set(it); + gui::language::set(it); continue; } } @@ -537,7 +541,7 @@ static void refresh_input_wids(void) } } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if((event.action == GLFW_PRESS) && (event.key != DEBUG_KEY)) { if(globals::gui_keybind_ptr || globals::gui_gamepad_axis_ptr || globals::gui_gamepad_button_ptr) { @@ -570,7 +574,7 @@ static void on_glfw_key(const GlfwKeyEvent& event) } } -static void on_gamepad_axis(const GamepadAxisEvent& event) +static void on_gamepad_axis(const io::GamepadAxisEvent& event) { if(globals::gui_gamepad_axis_ptr) { auto& io = ImGui::GetIO(); @@ -585,7 +589,7 @@ static void on_gamepad_axis(const GamepadAxisEvent& event) } } -static void on_gamepad_button(const GamepadButtonEvent& event) +static void on_gamepad_button(const io::GamepadButtonEvent& event) { if(globals::gui_gamepad_button_ptr) { auto& io = ImGui::GetIO(); @@ -600,35 +604,35 @@ static void on_gamepad_button(const GamepadButtonEvent& event) } } -static void on_language_set(const LanguageSetEvent& event) +static void on_language_set(const gui::LanguageSetEvent& event) { - str_checkbox_false = language::resolve("settings.checkbox.false"); - str_checkbox_true = language::resolve("settings.checkbox.true"); + str_checkbox_false = gui::language::resolve("settings.checkbox.false"); + str_checkbox_true = gui::language::resolve("settings.checkbox.true"); - str_tab_general = language::resolve("settings.tab.general"); - str_tab_input = language::resolve("settings.tab.input"); - str_tab_video = language::resolve("settings.tab.video"); - str_tab_sound = language::resolve("settings.tab.sound"); + str_tab_general = gui::language::resolve("settings.tab.general"); + str_tab_input = gui::language::resolve("settings.tab.input"); + str_tab_video = gui::language::resolve("settings.tab.video"); + str_tab_sound = gui::language::resolve("settings.tab.sound"); - str_input_keyboard = language::resolve("settings.input.keyboard"); - str_input_gamepad = language::resolve("settings.input.gamepad"); - str_input_mouse = language::resolve("settings.input.mouse"); + str_input_keyboard = gui::language::resolve("settings.input.keyboard"); + str_input_gamepad = gui::language::resolve("settings.input.gamepad"); + str_input_mouse = gui::language::resolve("settings.input.mouse"); - str_keyboard_movement = language::resolve("settings.keyboard.movement"); - str_keyboard_gameplay = language::resolve("settings.keyboard.gameplay"); - str_keyboard_misc = language::resolve("settings.keyboard.misc"); + str_keyboard_movement = gui::language::resolve("settings.keyboard.movement"); + str_keyboard_gameplay = gui::language::resolve("settings.keyboard.gameplay"); + str_keyboard_misc = gui::language::resolve("settings.keyboard.misc"); - str_gamepad_movement = language::resolve("settings.gamepad.movement"); - str_gamepad_gameplay = language::resolve("settings.gamepad.gameplay"); - str_gamepad_misc = language::resolve("settings.gamepad.misc"); + str_gamepad_movement = gui::language::resolve("settings.gamepad.movement"); + str_gamepad_gameplay = gui::language::resolve("settings.gamepad.gameplay"); + str_gamepad_misc = gui::language::resolve("settings.gamepad.misc"); - str_gamepad_axis_prefix = language::resolve("settings.gamepad.axis"); - str_gamepad_button_prefix = language::resolve("settings.gamepad.button"); - str_gamepad_checkbox_tooltip = language::resolve("settings.gamepad.checkbox_tooltip"); + str_gamepad_axis_prefix = gui::language::resolve("settings.gamepad.axis"); + str_gamepad_button_prefix = gui::language::resolve("settings.gamepad.button"); + str_gamepad_checkbox_tooltip = gui::language::resolve("settings.gamepad.checkbox_tooltip"); - str_video_gui = language::resolve("settings.video.gui"); + str_video_gui = gui::language::resolve("settings.video.gui"); - str_sound_levels = language::resolve("settings.sound.levels"); + str_sound_levels = gui::language::resolve("settings.sound.levels"); for(SettingValue* value : values_all) { if(value->type == setting_type::CHECKBOX) { @@ -646,10 +650,10 @@ static void on_language_set(const LanguageSetEvent& event) stepper->refresh_wids(); } - value->title = language::resolve(std::format("settings.value.{}", value->name).c_str()); + value->title = gui::language::resolve(std::format("settings.value.{}", value->name).c_str()); if(value->has_tooltip) { - value->tooltip = language::resolve(std::format("settings.tooltip.{}", value->name).c_str()); + value->tooltip = gui::language::resolve(std::format("settings.tooltip.{}", value->name).c_str()); } } } @@ -720,7 +724,7 @@ static void layout_input(void) ImGui::EndTabItem(); } - if(gamepad::available) { + if(io::gamepad::available) { if(ImGui::BeginTabItem(str_input_gamepad.c_str())) { globals::gui_keybind_ptr = nullptr; layout_input_gamepad(); @@ -762,10 +766,10 @@ static void layout_sound(void) void settings::init(void) { - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); - globals::dispatcher.sink<GamepadAxisEvent>().connect<&on_gamepad_axis>(); - globals::dispatcher.sink<GamepadButtonEvent>().connect<&on_gamepad_button>(); - globals::dispatcher.sink<LanguageSetEvent>().connect<&on_language_set>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GamepadAxisEvent>().connect<&on_gamepad_axis>(); + globals::dispatcher.sink<io::GamepadButtonEvent>().connect<&on_gamepad_button>(); + globals::dispatcher.sink<gui::LanguageSetEvent>().connect<&on_language_set>(); } void settings::init_late(void) @@ -840,7 +844,7 @@ void settings::layout(void) ImGui::End(); } -void settings::add_checkbox(int priority, ConfigBoolean& value, settings_location location, const char* name, bool tooltip) +void settings::add_checkbox(int priority, config::Boolean& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_CheckBox; setting_value->type = setting_type::CHECKBOX; @@ -855,7 +859,7 @@ void settings::add_checkbox(int priority, ConfigBoolean& value, settings_locatio values_all.push_back(setting_value); } -void settings::add_input(int priority, ConfigInt& value, settings_location location, const char* name, bool tooltip) +void settings::add_input(int priority, config::Int& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_InputInt; setting_value->type = setting_type::INPUT_INT; @@ -870,7 +874,7 @@ void settings::add_input(int priority, ConfigInt& value, settings_location locat values_all.push_back(setting_value); } -void settings::add_input(int priority, ConfigFloat& value, settings_location location, const char* name, bool tooltip, const char* format) +void settings::add_input(int priority, config::Float& value, settings_location location, const char* name, bool tooltip, const char* format) { auto setting_value = new SettingValue_InputFloat; setting_value->type = setting_type::INPUT_FLOAT; @@ -885,7 +889,7 @@ void settings::add_input(int priority, ConfigFloat& value, settings_location loc values_all.push_back(setting_value); } -void settings::add_input(int priority, ConfigUnsigned& value, settings_location location, const char* name, bool tooltip) +void settings::add_input(int priority, config::Unsigned& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_InputUnsigned; setting_value->type = setting_type::INPUT_UINT; @@ -901,7 +905,7 @@ void settings::add_input(int priority, ConfigUnsigned& value, settings_location } void settings::add_input( - int priority, ConfigString& value, settings_location location, const char* name, bool tooltip, bool allow_whitespace) + int priority, config::String& value, settings_location location, const char* name, bool tooltip, bool allow_whitespace) { auto setting_value = new SettingValue_InputString; setting_value->type = setting_type::INPUT_STRING; @@ -917,7 +921,7 @@ void settings::add_input( values_all.push_back(setting_value); } -void settings::add_slider(int priority, ConfigInt& value, settings_location location, const char* name, bool tooltip) +void settings::add_slider(int priority, config::Int& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_SliderInt; setting_value->type = setting_type::SLIDER_INT; @@ -932,7 +936,8 @@ void settings::add_slider(int priority, ConfigInt& value, settings_location loca values_all.push_back(setting_value); } -void settings::add_slider(int priority, ConfigFloat& value, settings_location location, const char* name, bool tooltip, const char* format) +void settings::add_slider( + int priority, config::Float& value, settings_location location, const char* name, bool tooltip, const char* format) { auto setting_value = new SettingValue_SliderFloat; setting_value->type = setting_type::SLIDER_FLOAT; @@ -948,7 +953,7 @@ void settings::add_slider(int priority, ConfigFloat& value, settings_location lo values_all.push_back(setting_value); } -void settings::add_slider(int priority, ConfigUnsigned& value, settings_location location, const char* name, bool tooltip) +void settings::add_slider(int priority, config::Unsigned& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_SliderUnsigned; setting_value->type = setting_type::SLIDER_UINT; @@ -963,7 +968,7 @@ void settings::add_slider(int priority, ConfigUnsigned& value, settings_location values_all.push_back(setting_value); } -void settings::add_stepper(int priority, ConfigInt& value, settings_location location, const char* name, bool tooltip) +void settings::add_stepper(int priority, config::Int& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_StepperInt; setting_value->type = setting_type::STEPPER_INT; @@ -979,7 +984,7 @@ void settings::add_stepper(int priority, ConfigInt& value, settings_location loc values_all.push_back(setting_value); } -void settings::add_stepper(int priority, ConfigUnsigned& value, settings_location location, const char* name, bool tooltip) +void settings::add_stepper(int priority, config::Unsigned& value, settings_location location, const char* name, bool tooltip) { auto setting_value = new SettingValue_StepperUnsigned; setting_value->type = setting_type::STEPPER_UINT; @@ -995,7 +1000,7 @@ void settings::add_stepper(int priority, ConfigUnsigned& value, settings_locatio values_all.push_back(setting_value); } -void settings::add_keybind(int priority, ConfigKeyBind& value, settings_location location, const char* name) +void settings::add_keybind(int priority, config::KeyBind& value, settings_location location, const char* name) { auto setting_value = new SettingValue_KeyBind; setting_value->type = setting_type::KEYBIND; @@ -1010,7 +1015,7 @@ void settings::add_keybind(int priority, ConfigKeyBind& value, settings_location values_all.push_back(setting_value); } -void settings::add_gamepad_axis(int priority, ConfigGamepadAxis& value, settings_location location, const char* name) +void settings::add_gamepad_axis(int priority, config::GamepadAxis& value, settings_location location, const char* name) { auto setting_value = new SettingValue_GamepadAxis; setting_value->type = setting_type::GAMEPAD_AXIS; @@ -1025,7 +1030,7 @@ void settings::add_gamepad_axis(int priority, ConfigGamepadAxis& value, settings values_all.push_back(setting_value); } -void settings::add_gamepad_button(int priority, ConfigGamepadButton& value, settings_location location, const char* name) +void settings::add_gamepad_button(int priority, config::GamepadButton& value, settings_location location, const char* name) { auto setting_value = new SettingValue_GamepadButton; setting_value->type = setting_type::GAMEPAD_BUTTON; diff --git a/game/client/gui/settings.hh b/game/client/gui/settings.hh new file mode 100644 index 0000000..15aa6a7 --- /dev/null +++ b/game/client/gui/settings.hh @@ -0,0 +1,93 @@ +#ifndef CLIENT_SETTINGS_HH +#define CLIENT_SETTINGS_HH 1 +#pragma once + +namespace config +{ +class Boolean; +class String; +} // namespace config + +namespace config +{ +class Int; +class Float; +class Unsigned; +} // namespace config + +namespace config +{ +class KeyBind; +class GamepadAxis; +class GamepadButton; +} // namespace config + +enum class settings_location : unsigned int { + GENERAL = 0x0000U, + KEYBOARD_MOVEMENT = 0x0001U, + KEYBOARD_GAMEPLAY = 0x0002U, + KEYBOARD_MISC = 0x0003U, + GAMEPAD = 0x0004U, + GAMEPAD_MOVEMENT = 0x0005U, + GAMEPAD_GAMEPLAY = 0x0006U, + GAMEPAD_MISC = 0x0007U, + MOUSE = 0x0008U, + VIDEO = 0x0009U, + VIDEO_GUI = 0x000AU, + SOUND = 0x000BU, + SOUND_LEVELS = 0x000CU, + COUNT = 0x000DU, +}; + +namespace settings +{ +void init(void); +void init_late(void); +void shutdown(void); +void layout(void); +} // namespace settings + +namespace settings +{ +void add_checkbox(int priority, config::Boolean& value, settings_location location, const char* name, bool tooltip); +} // namespace settings + +namespace settings +{ +void add_input(int priority, config::Int& value, settings_location location, const char* name, bool tooltip); +void add_input(int priority, config::Float& value, settings_location location, const char* name, bool tooltip, const char* format = "%.3f"); +void add_input(int priority, config::Unsigned& value, settings_location location, const char* name, bool tooltip); +void add_input(int priority, config::String& value, settings_location location, const char* name, bool tooltip, bool allow_whitespace); +} // namespace settings + +namespace settings +{ +void add_slider(int priority, config::Int& value, settings_location location, const char* name, bool tooltip); +void add_slider( + int priority, config::Float& value, settings_location location, const char* name, bool tooltip, const char* format = "%.3f"); +void add_slider(int priority, config::Unsigned& value, settings_location location, const char* name, bool tooltip); +} // namespace settings + +namespace settings +{ +void add_stepper(int priority, config::Int& value, settings_location location, const char* name, bool tooltip); +void add_stepper(int priority, config::Unsigned& value, settings_location location, const char* name, bool tooltip); +} // namespace settings + +namespace settings +{ +void add_keybind(int priority, config::KeyBind& value, settings_location location, const char* name); +} // namespace settings + +namespace settings +{ +void add_gamepad_axis(int priority, config::GamepadAxis& value, settings_location location, const char* name); +void add_gamepad_button(int priority, config::GamepadButton& value, settings_location location, const char* name); +} // namespace settings + +namespace settings +{ +void add_language_select(int priority, settings_location location, const char* name); +} // namespace settings + +#endif // CLIENT_SETTINGS_HH diff --git a/src/game/client/splash.cc b/game/client/gui/splash.cc index f9c616b..d6615ec 100644 --- a/src/game/client/splash.cc +++ b/game/client/gui/splash.cc @@ -1,17 +1,18 @@ #include "client/pch.hh" -#include "client/splash.hh" +#include "client/gui/splash.hh" -#include "core/cmdline.hh" -#include "core/constexpr.hh" -#include "core/epoch.hh" -#include "core/resource.hh" +#include "core/io/cmdline.hh" +#include "core/math/constexpr.hh" +#include "core/resource/resource.hh" +#include "core/utils/epoch.hh" + +#include "client/gui/gui_screen.hh" +#include "client/gui/language.hh" +#include "client/io/glfw.hh" +#include "client/resource/texture_gui.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/language.hh" -#include "client/texture_gui.hh" constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; @@ -26,24 +27,24 @@ static float texture_alpha; static std::uint64_t end_time; static std::string current_text; -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { end_time = UINT64_C(0); } -static void on_glfw_mouse_button(const GlfwMouseButtonEvent& event) +static void on_glfw_mouse_button(const io::GlfwMouseButtonEvent& event) { end_time = UINT64_C(0); } -static void on_glfw_scroll(const GlfwScrollEvent& event) +static void on_glfw_scroll(const io::GlfwScrollEvent& event) { end_time = UINT64_C(0); } -void client_splash::init(void) +void gui::client_splash::init(void) { - if(cmdline::contains("nosplash")) { + if(io::cmdline::contains("nosplash")) { texture = nullptr; texture_aspect = 0.0f; texture_alpha = 0.0f; @@ -68,7 +69,7 @@ void client_splash::init(void) } } -void client_splash::init_late(void) +void gui::client_splash::init_late(void) { if(!texture) { // We don't have to waste time @@ -76,30 +77,30 @@ void client_splash::init_late(void) return; } - end_time = epoch::microseconds() + DELAY_MICROSECONDS; + end_time = utils::unix_microseconds() + DELAY_MICROSECONDS; - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); - globals::dispatcher.sink<GlfwMouseButtonEvent>().connect<&on_glfw_mouse_button>(); - globals::dispatcher.sink<GlfwScrollEvent>().connect<&on_glfw_scroll>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwMouseButtonEvent>().connect<&on_glfw_mouse_button>(); + globals::dispatcher.sink<io::GlfwScrollEvent>().connect<&on_glfw_scroll>(); - current_text = language::resolve("splash.skip_prompt"); + current_text = gui::language::resolve("splash.skip_prompt"); while(!glfwWindowShouldClose(globals::window)) { - const std::uint64_t curtime = epoch::microseconds(); + const std::uint64_t curtime = utils::unix_microseconds(); const std::uint64_t remains = end_time - curtime; if(curtime >= end_time) { break; } - texture_alpha = vx::smoothstep(0.25f, 0.6f, static_cast<float>(remains) / static_cast<float>(DELAY_MICROSECONDS)); + texture_alpha = math::smoothstep(0.25f, 0.6f, static_cast<float>(remains) / static_cast<float>(DELAY_MICROSECONDS)); - client_splash::render(); + gui::client_splash::render(); } - globals::dispatcher.sink<GlfwKeyEvent>().disconnect<&on_glfw_key>(); - globals::dispatcher.sink<GlfwMouseButtonEvent>().disconnect<&on_glfw_mouse_button>(); - globals::dispatcher.sink<GlfwScrollEvent>().disconnect<&on_glfw_scroll>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().disconnect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwMouseButtonEvent>().disconnect<&on_glfw_mouse_button>(); + globals::dispatcher.sink<io::GlfwScrollEvent>().disconnect<&on_glfw_scroll>(); texture = nullptr; texture_aspect = 0.0f; @@ -107,7 +108,7 @@ void client_splash::init_late(void) end_time = UINT64_C(0); } -void client_splash::render(void) +void gui::client_splash::render(void) { if(!texture) { // We don't have to waste time diff --git a/src/game/client/splash.hh b/game/client/gui/splash.hh index e930470..7a032ad 100644 --- a/src/game/client/splash.hh +++ b/game/client/gui/splash.hh @@ -2,11 +2,11 @@ #define CLIENT_SPLASH_HH 1 #pragma once -namespace client_splash +namespace gui::client_splash { void init(void); void init_late(void); void render(void); -} // namespace client_splash +} // namespace gui::client_splash #endif // CLIENT_SPLASH_HH diff --git a/src/game/client/status_lines.cc b/game/client/gui/status_lines.cc index ea7293c..054f971 100644 --- a/src/game/client/status_lines.cc +++ b/game/client/gui/status_lines.cc @@ -1,20 +1,21 @@ #include "client/pch.hh" -#include "client/status_lines.hh" +#include "client/gui/status_lines.hh" + +#include "client/gui/imdraw_ext.hh" #include "client/globals.hh" -#include "client/imdraw_ext.hh" -static float line_offsets[STATUS_COUNT]; -static ImFont* line_fonts[STATUS_COUNT]; +static float line_offsets[gui::STATUS_COUNT]; +static ImFont* line_fonts[gui::STATUS_COUNT]; -static ImVec4 line_text_colors[STATUS_COUNT]; -static ImVec4 line_shadow_colors[STATUS_COUNT]; -static std::string line_strings[STATUS_COUNT]; -static std::uint64_t line_spawns[STATUS_COUNT]; -static float line_fadeouts[STATUS_COUNT]; +static ImVec4 line_text_colors[gui::STATUS_COUNT]; +static ImVec4 line_shadow_colors[gui::STATUS_COUNT]; +static std::string line_strings[gui::STATUS_COUNT]; +static std::uint64_t line_spawns[gui::STATUS_COUNT]; +static float line_fadeouts[gui::STATUS_COUNT]; -void status_lines::init(void) +void gui::status_lines::init(void) { for(unsigned int i = 0U; i < STATUS_COUNT; ++i) { line_text_colors[i] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); @@ -25,13 +26,13 @@ void status_lines::init(void) } } -void status_lines::init_late(void) +void gui::status_lines::init_late(void) { line_offsets[STATUS_DEBUG] = 64.0f; line_offsets[STATUS_HOTBAR] = 40.0f; } -void status_lines::layout(void) +void gui::status_lines::layout(void) { line_fonts[STATUS_DEBUG] = globals::font_debug; line_fonts[STATUS_HOTBAR] = globals::font_chat; @@ -56,11 +57,11 @@ void status_lines::layout(void) auto color_U32 = ImGui::GetColorU32(ImVec4(color.x, color.y, color.z, color.w * alpha)); auto shadow_U32 = ImGui::GetColorU32(ImVec4(shadow.x, shadow.y, shadow.z, color.w * alpha)); - imdraw_ext::text_shadow(text, pos, color_U32, shadow_U32, font, draw_list); + gui::imdraw_ext::text_shadow(text, pos, color_U32, shadow_U32, font, draw_list); } } -void status_lines::set(unsigned int line, const std::string& text, const ImVec4& color, float fadeout) +void gui::status_lines::set(unsigned int line, const std::string& text, const ImVec4& color, float fadeout) { line_text_colors[line] = ImVec4(color.x, color.y, color.z, color.w); line_shadow_colors[line] = ImVec4(color.x * 0.1f, color.y * 0.1f, color.z * 0.1f, color.w); @@ -69,7 +70,7 @@ void status_lines::set(unsigned int line, const std::string& text, const ImVec4& line_fadeouts[line] = fadeout; } -void status_lines::unset(unsigned int line) +void gui::status_lines::unset(unsigned int line) { line_text_colors[line] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); line_shadow_colors[line] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/src/game/client/status_lines.hh b/game/client/gui/status_lines.hh index 88bf986..907cdfc 100644 --- a/src/game/client/status_lines.hh +++ b/game/client/gui/status_lines.hh @@ -2,21 +2,24 @@ #define CLIENT_STATUS_LINES_HH 1 #pragma once +namespace gui +{ constexpr static unsigned int STATUS_DEBUG = 0x0000; // generic debug line constexpr static unsigned int STATUS_HOTBAR = 0x0001; // hotbar item line constexpr static unsigned int STATUS_COUNT = 0x0002; +} // namespace gui -namespace status_lines +namespace gui::status_lines { void init(void); void init_late(void); void layout(void); -} // namespace status_lines +} // namespace gui::status_lines -namespace status_lines +namespace gui::status_lines { void set(unsigned int line, const std::string& text, const ImVec4& color, float fadeout); void unset(unsigned int line); -} // namespace status_lines +} // namespace gui::status_lines #endif // CLIENT_STATUS_LINES_HH diff --git a/src/game/client/window_title.cc b/game/client/gui/window_title.cc index 517429d..5a5aca2 100644 --- a/src/game/client/window_title.cc +++ b/game/client/gui/window_title.cc @@ -1,6 +1,6 @@ #include "client/pch.hh" -#include "client/window_title.hh" +#include "client/gui/window_title.hh" #include "core/version.hh" @@ -8,7 +8,7 @@ #include "client/globals.hh" -void window_title::update(void) +void gui::window_title::update(void) { std::string title; diff --git a/src/game/client/window_title.hh b/game/client/gui/window_title.hh index 268cad6..a6fe4ec 100644 --- a/src/game/client/window_title.hh +++ b/game/client/gui/window_title.hh @@ -2,9 +2,9 @@ #define CLIENT_WINDOW_TITLE_HH 1 #pragma once -namespace window_title +namespace gui::window_title { void update(void); -} // namespace window_title +} // namespace gui::window_title #endif // CLIENT_WINDOW_TITLE_HH diff --git a/game/client/io/CMakeLists.txt b/game/client/io/CMakeLists.txt new file mode 100644 index 0000000..82bc422 --- /dev/null +++ b/game/client/io/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/gamepad.cc" + "${CMAKE_CURRENT_LIST_DIR}/gamepad.hh" + "${CMAKE_CURRENT_LIST_DIR}/glfw.hh") diff --git a/src/game/client/gamepad.cc b/game/client/io/gamepad.cc index 6cbcb3f..9910950 100644 --- a/src/game/client/gamepad.cc +++ b/game/client/io/gamepad.cc @@ -1,14 +1,17 @@ #include "client/pch.hh" -#include "client/gamepad.hh" +#include "client/io/gamepad.hh" -#include "core/cmdline.hh" -#include "core/config.hh" -#include "core/constexpr.hh" +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/io/cmdline.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" + +#include "client/gui/settings.hh" +#include "client/io/glfw.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/settings.hh" #include "client/toggles.hh" constexpr static int INVALID_GAMEPAD_ID = INT_MAX; @@ -18,16 +21,16 @@ constexpr static float GAMEPAD_AXIS_EVENT_THRESHOLD = 0.5f; static int active_gamepad_id; -bool gamepad::available = false; -ConfigFloat gamepad::deadzone(0.00f, 0.00f, 0.66f); -ConfigBoolean gamepad::active(false); -GLFWgamepadstate gamepad::state; -GLFWgamepadstate gamepad::last_state; +bool io::gamepad::available = false; +config::Float io::gamepad::deadzone(0.00f, 0.00f, 0.66f); +config::Boolean io::gamepad::active(false); +GLFWgamepadstate io::gamepad::state; +GLFWgamepadstate io::gamepad::last_state; static void on_toggle_enable(const ToggleEnabledEvent& event) { if(event.type == TOGGLE_USE_GAMEPAD) { - gamepad::active.set_value(true); + io::gamepad::active.set_value(true); return; } } @@ -35,22 +38,25 @@ static void on_toggle_enable(const ToggleEnabledEvent& event) static void on_toggle_disable(const ToggleDisabledEvent& event) { if(event.type == TOGGLE_USE_GAMEPAD) { - gamepad::active.set_value(false); + io::gamepad::active.set_value(false); return; } } -static void on_glfw_joystick_event(const GlfwJoystickEvent& event) +static void on_glfw_joystick_event(const io::GlfwJoystickEvent& event) { if((event.event_type == GLFW_CONNECTED) && glfwJoystickIsGamepad(event.joystick_id) && (active_gamepad_id == INVALID_GAMEPAD_ID)) { - gamepad::available = true; + io::gamepad::available = true; active_gamepad_id = event.joystick_id; - for(int i = 0; i < NUM_AXES; gamepad::last_state.axes[i++] = 0.0f) - ; - for(int i = 0; i < NUM_BUTTONS; gamepad::last_state.buttons[i++] = GLFW_RELEASE) - ; + for(int i = 0; i < NUM_AXES; io::gamepad::last_state.axes[i++] = 0.0f) { + // empty + } + + for(int i = 0; i < NUM_BUTTONS; io::gamepad::last_state.buttons[i++] = GLFW_RELEASE) { + // empty + } spdlog::info("gamepad: detected gamepad: {}", glfwGetGamepadName(event.joystick_id)); @@ -58,13 +64,13 @@ static void on_glfw_joystick_event(const GlfwJoystickEvent& event) } if((event.event_type == GLFW_DISCONNECTED) && (active_gamepad_id == event.joystick_id)) { - gamepad::available = false; + io::gamepad::available = false; active_gamepad_id = INVALID_GAMEPAD_ID; - for(int i = 0; i < NUM_AXES; gamepad::last_state.axes[i++] = 0.0f) + for(int i = 0; i < NUM_AXES; io::gamepad::last_state.axes[i++] = 0.0f) ; - for(int i = 0; i < NUM_BUTTONS; gamepad::last_state.buttons[i++] = GLFW_RELEASE) + for(int i = 0; i < NUM_BUTTONS; io::gamepad::last_state.buttons[i++] = GLFW_RELEASE) ; spdlog::warn("gamepad: disconnected"); @@ -73,19 +79,19 @@ static void on_glfw_joystick_event(const GlfwJoystickEvent& event) } } -void gamepad::init(void) +void io::gamepad::init(void) { - gamepad::available = false; + io::gamepad::available = false; active_gamepad_id = INVALID_GAMEPAD_ID; - globals::client_config.add_value("gamepad.deadzone", gamepad::deadzone); - globals::client_config.add_value("gamepad.active", gamepad::active); + globals::client_config.add_value("gamepad.deadzone", io::gamepad::deadzone); + globals::client_config.add_value("gamepad.active", io::gamepad::active); - settings::add_checkbox(0, gamepad::active, settings_location::GAMEPAD, "gamepad.active", true); - settings::add_slider(1, gamepad::deadzone, settings_location::GAMEPAD, "gamepad.deadzone", true, "%.03f"); + settings::add_checkbox(0, io::gamepad::active, settings_location::GAMEPAD, "gamepad.active", true); + settings::add_slider(1, io::gamepad::deadzone, settings_location::GAMEPAD, "gamepad.deadzone", true, "%.03f"); - auto mappings_path = cmdline::get("gpmap", "misc/gamecontrollerdb.txt"); + auto mappings_path = io::cmdline::get("gpmap", "misc/gamecontrollerdb.txt"); auto mappings_file = PHYSFS_openRead(mappings_path); if(mappings_file) { @@ -98,14 +104,17 @@ void gamepad::init(void) for(int joystick = 0; joystick <= GLFW_JOYSTICK_LAST; joystick += 1) { if(glfwJoystickIsGamepad(joystick)) { - gamepad::available = true; + io::gamepad::available = true; active_gamepad_id = joystick; - for(int i = 0; i < NUM_AXES; gamepad::last_state.axes[i++] = 0.0f) - ; - for(int i = 0; i < NUM_BUTTONS; gamepad::last_state.buttons[i++] = GLFW_RELEASE) - ; + for(int i = 0; i < NUM_AXES; io::gamepad::last_state.axes[i++] = 0.0f) { + // empty + } + + for(int i = 0; i < NUM_BUTTONS; io::gamepad::last_state.buttons[i++] = GLFW_RELEASE) { + // empty + } spdlog::info("gamepad: detected gamepad: {}", glfwGetGamepadName(joystick)); @@ -113,27 +122,30 @@ void gamepad::init(void) } } - for(int i = 0; i < NUM_AXES; gamepad::state.axes[i++] = 0.0f) - ; - for(int i = 0; i < NUM_BUTTONS; gamepad::state.buttons[i++] = GLFW_RELEASE) - ; + for(int i = 0; i < NUM_AXES; io::gamepad::state.axes[i++] = 0.0f) { + // empty + } + + for(int i = 0; i < NUM_BUTTONS; io::gamepad::state.buttons[i++] = GLFW_RELEASE) { + // empty + } globals::dispatcher.sink<ToggleEnabledEvent>().connect<&on_toggle_enable>(); globals::dispatcher.sink<ToggleDisabledEvent>().connect<&on_toggle_disable>(); globals::dispatcher.sink<GlfwJoystickEvent>().connect<&on_glfw_joystick_event>(); } -void gamepad::update_late(void) +void io::gamepad::update_late(void) { if(active_gamepad_id == INVALID_GAMEPAD_ID) { // No active gamepad found return; } - if(glfwGetGamepadState(active_gamepad_id, &gamepad::state)) { + if(glfwGetGamepadState(active_gamepad_id, &io::gamepad::state)) { for(int i = 0; i < NUM_AXES; ++i) { - if((vx::abs(gamepad::state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD) - && (vx::abs(gamepad::last_state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD)) { + if((math::abs(io::gamepad::state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD) + && (math::abs(io::gamepad::last_state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD)) { GamepadAxisEvent event; event.action = GLFW_PRESS; event.axis = i; @@ -141,8 +153,8 @@ void gamepad::update_late(void) continue; } - if((vx::abs(gamepad::state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD) - && (vx::abs(gamepad::last_state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD)) { + if((math::abs(io::gamepad::state.axes[i]) <= GAMEPAD_AXIS_EVENT_THRESHOLD) + && (math::abs(io::gamepad::last_state.axes[i]) > GAMEPAD_AXIS_EVENT_THRESHOLD)) { GamepadAxisEvent event; event.action = GLFW_RELEASE; event.axis = i; @@ -152,17 +164,17 @@ void gamepad::update_late(void) } for(int i = 0; i < NUM_BUTTONS; ++i) { - if(gamepad::state.buttons[i] == gamepad::last_state.buttons[i]) { + if(io::gamepad::state.buttons[i] == io::gamepad::last_state.buttons[i]) { // Nothing happens continue; } GamepadButtonEvent event; - event.action = gamepad::state.buttons[i]; + event.action = io::gamepad::state.buttons[i]; event.button = i; globals::dispatcher.enqueue(event); } } - gamepad::last_state = gamepad::state; + io::gamepad::last_state = io::gamepad::state; } diff --git a/src/game/client/gamepad.hh b/game/client/io/gamepad.hh index 692422e..4200037 100644 --- a/src/game/client/gamepad.hh +++ b/game/client/io/gamepad.hh @@ -2,29 +2,37 @@ #define CLIENT_GAMEPAD_HH 1 #pragma once +namespace io +{ constexpr static int INVALID_GAMEPAD_AXIS = INT_MAX; constexpr static int INVALID_GAMEPAD_BUTTON = INT_MAX; +} // namespace io -class ConfigBoolean; -class ConfigFloat; +namespace config +{ +class Boolean; +class Float; +} // namespace config struct GLFWgamepadstate; -namespace gamepad +namespace io::gamepad { extern bool available; -extern ConfigFloat deadzone; -extern ConfigBoolean active; +extern config::Float deadzone; +extern config::Boolean active; extern GLFWgamepadstate state; extern GLFWgamepadstate last_state; -} // namespace gamepad +} // namespace io::gamepad -namespace gamepad +namespace io::gamepad { void init(void); void update_late(void); -} // namespace gamepad +} // namespace io::gamepad +namespace io +{ // This simulates buttons using axes. When an axis // value exceeds 1.5 times the deadzone, the event is // queued with a GLFW_PRESS action, when it falls back @@ -41,5 +49,6 @@ struct GamepadButtonEvent final { int action; int button; }; +} // namespace io #endif // CLIENT_GAMEPAD_HH diff --git a/src/game/client/glfw.hh b/game/client/io/glfw.hh index d978429..ea3f4a7 100644 --- a/src/game/client/glfw.hh +++ b/game/client/io/glfw.hh @@ -2,6 +2,8 @@ #define CLIENTFW 1 #pragma once +namespace io +{ struct GlfwCursorPosEvent final { glm::fvec2 pos; }; @@ -33,5 +35,6 @@ struct GlfwScrollEvent final { float dx; float dy; }; +} // namespace io #endif // CLIENTFW diff --git a/src/game/client/main.cc b/game/client/main.cc index 817e09a..04ee832 100644 --- a/src/game/client/main.cc +++ b/game/client/main.cc @@ -1,24 +1,26 @@ #include "client/pch.hh" -#include "core/binfile.hh" -#include "core/cmdline.hh" -#include "core/config.hh" -#include "core/epoch.hh" -#include "core/image.hh" -#include "core/resource.hh" +#include "core/io/cmdline.hh" +#include "core/io/config_map.hh" +#include "core/resource/binfile.hh" +#include "core/resource/image.hh" +#include "core/resource/resource.hh" +#include "core/utils/epoch.hh" + #include "core/version.hh" #include "shared/game.hh" #include "shared/splash.hh" #include "shared/threading.hh" +#include "client/gui/window_title.hh" +#include "client/io/glfw.hh" +#include "client/resource/sound_effect.hh" +#include "client/resource/texture_gui.hh" + #include "client/const.hh" #include "client/game.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/sound_effect.hh" -#include "client/texture_gui.hh" -#include "client/window_title.hh" #if defined(_WIN32) extern "C" __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; @@ -42,7 +44,7 @@ static void on_glfw_cursor_enter(GLFWwindow* window, int entered) static void on_glfw_cursor_pos(GLFWwindow* window, double xpos, double ypos) { - GlfwCursorPosEvent event; + io::GlfwCursorPosEvent event; event.pos.x = static_cast<float>(xpos); event.pos.y = static_cast<float>(ypos); globals::dispatcher.trigger(event); @@ -63,7 +65,7 @@ static void on_glfw_framebuffer_size(GLFWwindow* window, int width, int height) globals::height = height; globals::aspect = static_cast<float>(width) / static_cast<float>(height); - GlfwFramebufferSizeEvent fb_event; + io::GlfwFramebufferSizeEvent fb_event; fb_event.size.x = globals::width; fb_event.size.y = globals::height; fb_event.aspect = globals::aspect; @@ -72,7 +74,7 @@ static void on_glfw_framebuffer_size(GLFWwindow* window, int width, int height) static void on_glfw_key(GLFWwindow* window, int key, int scancode, int action, int mods) { - GlfwKeyEvent event; + io::GlfwKeyEvent event; event.key = key; event.scancode = scancode; event.action = action; @@ -84,7 +86,7 @@ static void on_glfw_key(GLFWwindow* window, int key, int scancode, int action, i static void on_glfw_joystick(int joystick_id, int event_type) { - GlfwJoystickEvent event; + io::GlfwJoystickEvent event; event.joystick_id = joystick_id; event.event_type = event_type; globals::dispatcher.trigger(event); @@ -97,7 +99,7 @@ static void on_glfw_monitor_event(GLFWmonitor* monitor, int event) static void on_glfw_mouse_button(GLFWwindow* window, int button, int action, int mods) { - GlfwMouseButtonEvent event; + io::GlfwMouseButtonEvent event; event.button = button; event.action = action; event.mods = mods; @@ -108,7 +110,7 @@ static void on_glfw_mouse_button(GLFWwindow* window, int button, int action, int static void on_glfw_scroll(GLFWwindow* window, double dx, double dy) { - GlfwScrollEvent event; + io::GlfwScrollEvent event; event.dx = static_cast<float>(dx); event.dy = static_cast<float>(dy); globals::dispatcher.trigger(event); @@ -135,17 +137,17 @@ static void on_termination_signal(int) int main(int argc, char** argv) { - cmdline::create(argc, argv); + io::cmdline::create(argc, argv); #if defined(_WIN32) #if defined(NDEBUG) - if(GetConsoleWindow() && !cmdline::contains("debug")) { + if(GetConsoleWindow() && !io::cmdline::contains("debug")) { // Hide the console window on release builds // unless explicitly specified to preserve it instead FreeConsole(); } #else - if(GetConsoleWindow() && cmdline::contains("nodebug")) { + if(GetConsoleWindow() && io::cmdline::contains("nodebug")) { // Hide the console window on debug builds when // explicitly specified by the user to hide it FreeConsole(); @@ -196,7 +198,7 @@ int main(int argc, char** argv) } if(GLAD_GL_KHR_debug) { - if(!cmdline::contains("nodebug")) { + if(!io::cmdline::contains("nodebug")) { glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(&on_opengl_message, nullptr); @@ -250,7 +252,7 @@ int main(int argc, char** argv) glfwSetWindowIcon(globals::window, 1, &icon_image); } - if(cmdline::contains("nosound")) { + if(io::cmdline::contains("nosound")) { spdlog::warn("client: sound disabled [per command line]"); globals::sound_dev = nullptr; globals::sound_ctx = nullptr; @@ -283,7 +285,7 @@ int main(int argc, char** argv) splash::init_client(); - window_title::update(); + gui::window_title::update(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; @@ -294,7 +296,7 @@ int main(int argc, char** argv) globals::fixed_frametime_us = UINT64_MAX; globals::fixed_framecount = 0; - globals::curtime = epoch::microseconds(); + globals::curtime = utils::unix_microseconds(); globals::window_frametime = 0.0f; globals::window_frametime_avg = 0.0f; @@ -304,10 +306,10 @@ int main(int argc, char** argv) int vmode_width = DEFAULT_WIDTH; int vmode_height = DEFAULT_HEIGHT; - if(auto vmode = cmdline::get("mode")) { + if(auto vmode = io::cmdline::get("mode")) { std::sscanf(vmode, "%dx%d", &vmode_width, &vmode_height); - vmode_height = vx::max(vmode_height, MIN_HEIGHT); - vmode_width = vx::max(vmode_width, MIN_WIDTH); + vmode_height = math::max(vmode_height, MIN_HEIGHT); + vmode_width = math::max(vmode_width, MIN_WIDTH); } glfwSetWindowSize(globals::window, vmode_width, vmode_height); @@ -328,7 +330,7 @@ int main(int argc, char** argv) auto last_curtime = globals::curtime; while(!glfwWindowShouldClose(globals::window)) { - globals::curtime = epoch::microseconds(); + globals::curtime = utils::unix_microseconds(); globals::window_frametime_us = globals::curtime - last_curtime; globals::window_frametime = static_cast<float>(globals::window_frametime_us) / 1000000.0f; diff --git a/src/game/client/pch.hh b/game/client/pch.hh index 98c5e52..98c5e52 100644 --- a/src/game/client/pch.hh +++ b/game/client/pch.hh diff --git a/src/game/client/program.cc b/game/client/program.cc index 8d4403a..7b908af 100644 --- a/src/game/client/program.cc +++ b/game/client/program.cc @@ -2,7 +2,7 @@ #include "client/program.hh" -#include "core/strtools.hh" +#include "core/utils/string.hh" // This fills up the array of source lines and figures out // which lines are to be dynamically resolved as variant macros @@ -116,8 +116,8 @@ bool GL_Program::update(void) for(const auto& macro : frag_variants) frag_source[macro.line] = std::format("#define {} {}", macro.name, macro.value); - const std::string vsource = strtools::join(vert_source, "\r\n"); - const std::string fsource = strtools::join(frag_source, "\r\n"); + std::string vsource(utils::join(vert_source, "\r\n")); + std::string fsource(utils::join(frag_source, "\r\n")); GLuint vert = compile_shader(vert_path.c_str(), vsource.c_str(), GL_VERTEX_SHADER); GLuint frag = compile_shader(frag_path.c_str(), fsource.c_str(), GL_FRAGMENT_SHADER); diff --git a/src/game/client/program.hh b/game/client/program.hh index ce2538b..ce2538b 100644 --- a/src/game/client/program.hh +++ b/game/client/program.hh diff --git a/src/game/client/receive.cc b/game/client/receive.cc index 742d3af..ce7b6ab 100644 --- a/src/game/client/receive.cc +++ b/game/client/receive.cc @@ -2,23 +2,25 @@ #include "client/receive.hh" -#include "shared/dimension.hh" -#include "shared/head.hh" -#include "shared/player.hh" +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + #include "shared/protocol.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "client/chat.hh" -#include "client/factory.hh" +#include "client/entity/factory.hh" +#include "client/gui/chat.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/message_box.hh" +#include "client/gui/window_title.hh" +#include "client/sound/sound.hh" + #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/message_box.hh" #include "client/session.hh" -#include "client/sound.hh" -#include "client/window_title.hh" -static bool synchronize_entity_id(Dimension* dimension, entt::entity entity) +static bool synchronize_entity_id(world::Dimension* dimension, entt::entity entity) { if(dimension->entities.valid(entity)) { // Entity ID already exists @@ -36,12 +38,12 @@ static bool synchronize_entity_id(Dimension* dimension, entt::entity entity) spdlog::critical("receive: entity desync: network {} resolved as client {}", static_cast<std::uint64_t>(entity), static_cast<std::uint64_t>(created)); - message_box::reset(); - message_box::set_title("disconnected.disconnected"); - message_box::set_subtitle("protocol.entity_id_desync"); - message_box::add_button("disconnected.back", [](void) { + gui::message_box::reset(); + gui::message_box::set_title("disconnected.disconnected"); + gui::message_box::set_subtitle("protocol.entity_id_desync"); + gui::message_box::add_button("disconnected.back", [](void) { globals::gui_screen = GUI_PLAY_MENU; - window_title::update(); + gui::window_title::update(); }); globals::gui_screen = GUI_MESSAGE_BOX; @@ -58,7 +60,7 @@ static void on_dimension_info_packet(const protocol::DimensionInfo& packet) globals::player = entt::null; } - globals::dimension = new Dimension(packet.name.c_str(), packet.gravity); + globals::dimension = new world::Dimension(packet.name.c_str(), packet.gravity); } } @@ -68,7 +70,7 @@ static void on_chunk_voxels_packet(const protocol::ChunkVoxels& packet) auto chunk = globals::dimension->create_chunk(packet.chunk); chunk->set_voxels(packet.voxels); - ChunkUpdateEvent event; + world::ChunkUpdateEvent event; event.dimension = globals::dimension; event.cpos = packet.chunk; event.chunk = chunk; @@ -83,8 +85,8 @@ static void on_entity_head_packet(const protocol::EntityHead& packet) { if(session::peer && globals::dimension) { if(synchronize_entity_id(globals::dimension, packet.entity)) { - auto& component = globals::dimension->entities.get_or_emplace<HeadComponent>(packet.entity); - auto& prevcomp = globals::dimension->entities.get_or_emplace<HeadComponentPrev>(packet.entity); + auto& component = globals::dimension->entities.get_or_emplace<entity::Head>(packet.entity); + auto& prevcomp = globals::dimension->entities.get_or_emplace<entity::client::HeadPrev>(packet.entity); // Store the previous component state prevcomp.angles = component.angles; @@ -100,8 +102,8 @@ static void on_entity_transform_packet(const protocol::EntityTransform& packet) { if(session::peer && globals::dimension) { if(synchronize_entity_id(globals::dimension, packet.entity)) { - auto& component = globals::dimension->entities.get_or_emplace<TransformComponent>(packet.entity); - auto& prevcomp = globals::dimension->entities.get_or_emplace<TransformComponentPrev>(packet.entity); + auto& component = globals::dimension->entities.get_or_emplace<entity::Transform>(packet.entity); + auto& prevcomp = globals::dimension->entities.get_or_emplace<entity::client::TransformPrev>(packet.entity); // Store the previous component state prevcomp.angles = component.angles; @@ -120,7 +122,7 @@ static void on_entity_velocity_packet(const protocol::EntityVelocity& packet) { if(session::peer && globals::dimension) { if(synchronize_entity_id(globals::dimension, packet.entity)) { - auto& component = globals::dimension->entities.get_or_emplace<VelocityComponent>(packet.entity); + auto& component = globals::dimension->entities.get_or_emplace<entity::Velocity>(packet.entity); component.value = packet.value; } } @@ -130,7 +132,7 @@ static void on_entity_player_packet(const protocol::EntityPlayer& packet) { if(session::peer && globals::dimension) { if(synchronize_entity_id(globals::dimension, packet.entity)) { - client_factory::create_player(globals::dimension, packet.entity); + entity::client::create_player(globals::dimension, packet.entity); } } } @@ -139,14 +141,14 @@ static void on_spawn_player_packet(const protocol::SpawnPlayer& packet) { if(session::peer && globals::dimension) { if(synchronize_entity_id(globals::dimension, packet.entity)) { - client_factory::create_player(globals::dimension, packet.entity); + entity::client::create_player(globals::dimension, packet.entity); globals::player = packet.entity; globals::gui_screen = GUI_SCREEN_NONE; - client_chat::refresh_timings(); + gui::client_chat::refresh_timings(); - window_title::update(); + gui::window_title::update(); } } } diff --git a/src/game/client/receive.hh b/game/client/receive.hh index 1b793b0..1b793b0 100644 --- a/src/game/client/receive.hh +++ b/game/client/receive.hh diff --git a/game/client/resource/CMakeLists.txt b/game/client/resource/CMakeLists.txt new file mode 100644 index 0000000..baf2311 --- /dev/null +++ b/game/client/resource/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/sound_effect.cc" + "${CMAKE_CURRENT_LIST_DIR}/sound_effect.hh" + "${CMAKE_CURRENT_LIST_DIR}/texture_gui.cc" + "${CMAKE_CURRENT_LIST_DIR}/texture_gui.hh") diff --git a/src/game/client/sound_effect.cc b/game/client/resource/sound_effect.cc index 182f49d..e3a0a6a 100644 --- a/src/game/client/sound_effect.cc +++ b/game/client/resource/sound_effect.cc @@ -1,8 +1,8 @@ #include "client/pch.hh" -#include "client/sound_effect.hh" +#include "client/resource/sound_effect.hh" -#include "core/resource.hh" +#include "core/resource/resource.hh" #include "client/globals.hh" diff --git a/src/game/client/sound_effect.hh b/game/client/resource/sound_effect.hh index 8b1372b..8b1372b 100644 --- a/src/game/client/sound_effect.hh +++ b/game/client/resource/sound_effect.hh diff --git a/src/game/client/texture_gui.cc b/game/client/resource/texture_gui.cc index 265f6c2..a931352 100644 --- a/src/game/client/texture_gui.cc +++ b/game/client/resource/texture_gui.cc @@ -1,9 +1,9 @@ #include "client/pch.hh" -#include "client/texture_gui.hh" +#include "client/resource/texture_gui.hh" -#include "core/image.hh" -#include "core/resource.hh" +#include "core/resource/image.hh" +#include "core/resource/resource.hh" static emhash8::HashMap<std::string, resource_ptr<TextureGUI>> resource_map; diff --git a/src/game/client/texture_gui.hh b/game/client/resource/texture_gui.hh index 855596e..855596e 100644 --- a/src/game/client/texture_gui.hh +++ b/game/client/resource/texture_gui.hh diff --git a/src/game/client/screenshot.cc b/game/client/screenshot.cc index a42a815..08d8521 100644 --- a/src/game/client/screenshot.cc +++ b/game/client/screenshot.cc @@ -2,25 +2,26 @@ #include "client/screenshot.hh" -#include "core/config.hh" -#include "core/epoch.hh" +#include "core/io/config_map.hh" +#include "core/utils/epoch.hh" + +#include "client/config/keybind.hh" +#include "client/gui/chat.hh" +#include "client/gui/language.hh" +#include "client/gui/settings.hh" +#include "client/io/glfw.hh" -#include "client/chat.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/keybind.hh" -#include "client/language.hh" -#include "client/settings.hh" #include "client/toggles.hh" -static ConfigKeyBind screenshot_key(GLFW_KEY_F2); +static config::KeyBind screenshot_key(GLFW_KEY_F2); static void stbi_png_physfs_callback(void* context, void* data, int size) { PHYSFS_writeBytes(reinterpret_cast<PHYSFS_File*>(context), data, size); } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if(!globals::gui_keybind_ptr && !toggles::is_sequence_await) { if(screenshot_key.equals(event.key) && (event.action == GLFW_PRESS)) { @@ -36,7 +37,7 @@ void screenshot::init(void) settings::add_keybind(0, screenshot_key, settings_location::KEYBOARD_MISC, "key.screenshot"); - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); } void screenshot::take(void) @@ -62,7 +63,7 @@ void screenshot::take(void) glPixelStorei(GL_PACK_ALIGNMENT, old_pack_alignment); const auto directory = std::string("screenshots"); - const auto filename = std::format("{}.png", epoch::microseconds()); + const auto filename = std::format("{}.png", utils::unix_microseconds()); const auto filepath = std::format("{}/{}", directory, filename); PHYSFS_mkdir(directory.c_str()); @@ -73,7 +74,7 @@ void screenshot::take(void) spdlog::info("screenshot: wrote {}", filepath); - client_chat::print(std::format("{} {}", language::resolve("chat.screenshot_message"), filename)); + gui::client_chat::print(std::format("{} {}", gui::language::resolve("chat.screenshot_message"), filename)); PHYSFS_close(file); } diff --git a/src/game/client/screenshot.hh b/game/client/screenshot.hh index 7cb8cd5..7cb8cd5 100644 --- a/src/game/client/screenshot.hh +++ b/game/client/screenshot.hh diff --git a/src/game/client/session.cc b/game/client/session.cc index 171ef5f..d3f4859 100644 --- a/src/game/client/session.cc +++ b/game/client/session.cc @@ -2,28 +2,30 @@ #include "client/session.hh" -#include "core/config.hh" -#include "core/crc64.hh" +#include "core/config/string.hh" +#include "core/math/crc64.hh" + +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" +#include "shared/world/item_registry.hh" +#include "shared/world/voxel_registry.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/head.hh" -#include "shared/item_registry.hh" -#include "shared/player.hh" #include "shared/protocol.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "shared/voxel_registry.hh" -#include "client/camera.hh" -#include "client/chat.hh" -#include "client/chunk_visibility.hh" +#include "client/entity/camera.hh" +#include "client/gui/chat.hh" +#include "client/gui/gui_screen.hh" +#include "client/gui/message_box.hh" +#include "client/gui/progress_bar.hh" +#include "client/gui/window_title.hh" +#include "client/world/chunk_visibility.hh" + #include "client/game.hh" #include "client/globals.hh" -#include "client/gui_screen.hh" -#include "client/message_box.hh" -#include "client/progress_bar.hh" -#include "client/window_title.hh" ENetPeer* session::peer = nullptr; std::uint16_t session::client_index = UINT16_MAX; @@ -33,7 +35,7 @@ static std::uint64_t server_password_hash = UINT64_MAX; static void set_fixed_tickrate(std::uint16_t tickrate) { - globals::fixed_frametime_us = 1000000U / vx::clamp<std::uint64_t>(tickrate, 10U, 300U); + globals::fixed_frametime_us = 1000000U / math::clamp<std::uint64_t>(tickrate, 10U, 300U); globals::fixed_frametime = static_cast<float>(globals::fixed_frametime_us) / 1000000.0f; globals::fixed_accumulator = 0; } @@ -49,7 +51,7 @@ static void on_login_response_packet(const protocol::LoginResponse& packet) set_fixed_tickrate(packet.server_tickrate); - progress_bar::set_title("connecting.loading_world"); + gui::progress_bar::set_title("connecting.loading_world"); } static void on_disconnect_packet(const protocol::Disconnect& packet) @@ -58,7 +60,7 @@ static void on_disconnect_packet(const protocol::Disconnect& packet) spdlog::info("session: disconnected: {}", packet.reason); - client_chat::clear(); + gui::client_chat::clear(); session::peer = nullptr; session::client_index = UINT16_MAX; @@ -74,12 +76,12 @@ static void on_disconnect_packet(const protocol::Disconnect& packet) globals::player = entt::null; globals::dimension = nullptr; - message_box::reset(); - message_box::set_title("disconnected.disconnected"); - message_box::set_subtitle(packet.reason.c_str()); - message_box::add_button("disconnected.back", [](void) { + gui::message_box::reset(); + gui::message_box::set_title("disconnected.disconnected"); + gui::message_box::set_subtitle(packet.reason.c_str()); + gui::message_box::add_button("disconnected.back", [](void) { globals::gui_screen = GUI_PLAY_MENU; - window_title::update(); + gui::window_title::update(); }); globals::gui_screen = GUI_MESSAGE_BOX; @@ -95,7 +97,7 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet) if(chunk->get_voxel(index) != packet.voxel) { chunk->set_voxel(packet.voxel, index); - ChunkUpdateEvent event; + world::ChunkUpdateEvent event; event.dimension = globals::dimension; event.chunk = chunk; event.cpos = cpos; @@ -112,7 +114,7 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet) // NOTE: [session] is a good place for this since [receive] // handles entity data sent by the server and [session] handles // everything else network related that is not player movement -static void on_voxel_set(const VoxelSetEvent& event) +static void on_voxel_set(const world::VoxelSetEvent& event) { if(session::peer) { // Propagate changes to the server @@ -141,7 +143,7 @@ void session::init(void) globals::dispatcher.sink<protocol::Disconnect>().connect<&on_disconnect_packet>(); globals::dispatcher.sink<protocol::SetVoxel>().connect<&on_set_voxel_packet>(); - globals::dispatcher.sink<VoxelSetEvent>().connect<&on_voxel_set>(); + globals::dispatcher.sink<world::VoxelSetEvent>().connect<&on_voxel_set>(); } void session::shutdown(void) @@ -158,18 +160,18 @@ void session::invalidate(void) if(session::peer) { enet_peer_reset(session::peer); - message_box::reset(); - message_box::set_title("disconnected.disconnected"); - message_box::set_subtitle("enet.peer_connection_timeout"); - message_box::add_button("disconnected.back", [](void) { + gui::message_box::reset(); + gui::message_box::set_title("disconnected.disconnected"); + gui::message_box::set_subtitle("enet.peer_connection_timeout"); + gui::message_box::add_button("disconnected.back", [](void) { globals::gui_screen = GUI_PLAY_MENU; - window_title::update(); + gui::window_title::update(); }); globals::gui_screen = GUI_MESSAGE_BOX; } - client_chat::clear(); + gui::client_chat::clear(); session::peer = nullptr; session::client_index = UINT16_MAX; @@ -200,17 +202,17 @@ void session::connect(const char* host, std::uint16_t port, const char* password globals::fixed_frametime = 0.0f; globals::fixed_accumulator = 0; - server_password_hash = crc64::get(password); + server_password_hash = math::crc64(password); if(!session::peer) { server_password_hash = UINT64_MAX; - message_box::reset(); - message_box::set_title("disconnected.disconnected"); - message_box::set_subtitle("enet.peer_connection_failed"); - message_box::add_button("disconnected.back", [](void) { + gui::message_box::reset(); + gui::message_box::set_title("disconnected.disconnected"); + gui::message_box::set_subtitle("enet.peer_connection_failed"); + gui::message_box::add_button("disconnected.back", [](void) { globals::gui_screen = GUI_PLAY_MENU; - window_title::update(); + gui::window_title::update(); }); globals::gui_screen = GUI_MESSAGE_BOX; @@ -218,9 +220,9 @@ void session::connect(const char* host, std::uint16_t port, const char* password return; } - progress_bar::reset(); - progress_bar::set_title("connecting.connecting"); - progress_bar::set_button("connecting.cancel_button", [](void) { + gui::progress_bar::reset(); + gui::progress_bar::set_title("connecting.connecting"); + gui::progress_bar::set_button("connecting.cancel_button", [](void) { enet_peer_disconnect(session::peer, 0); session::peer = nullptr; @@ -269,7 +271,7 @@ void session::disconnect(const char* reason) globals::player = entt::null; globals::dimension = nullptr; - client_chat::clear(); + gui::client_chat::clear(); } } @@ -277,8 +279,8 @@ void session::send_login_request(void) { protocol::LoginRequest packet; packet.version = protocol::VERSION; - packet.voxel_registry_checksum = voxel_registry::calcualte_checksum(); - packet.item_registry_checksum = item_registry::calcualte_checksum(); + packet.voxel_registry_checksum = world::voxel_registry::calcualte_checksum(); + packet.item_registry_checksum = world::item_registry::calcualte_checksum(); packet.password_hash = server_password_hash; packet.username = client_game::username.get(); @@ -286,7 +288,7 @@ void session::send_login_request(void) server_password_hash = UINT64_MAX; - progress_bar::set_title("connecting.logging_in"); + gui::progress_bar::set_title("connecting.logging_in"); globals::gui_screen = GUI_PROGRESS_BAR; } diff --git a/src/game/client/session.hh b/game/client/session.hh index a763d18..a763d18 100644 --- a/src/game/client/session.hh +++ b/game/client/session.hh diff --git a/game/client/sound/CMakeLists.txt b/game/client/sound/CMakeLists.txt new file mode 100644 index 0000000..4b577d3 --- /dev/null +++ b/game/client/sound/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/sound.cc" + "${CMAKE_CURRENT_LIST_DIR}/sound.hh") diff --git a/src/game/client/sound.cc b/game/client/sound/sound.cc index 10f77c0..fa3cd19 100644 --- a/src/game/client/sound.cc +++ b/game/client/sound/sound.cc @@ -1,27 +1,30 @@ #include "client/pch.hh" -#include "client/sound.hh" +#include "client/sound/sound.hh" -#include "core/config.hh" -#include "core/constexpr.hh" -#include "core/resource.hh" +#include "core/config/number.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" +#include "core/resource/resource.hh" + +#include "shared/world/dimension.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" #include "shared/protocol.hh" -#include "client/camera.hh" +#include "client/entity/camera.hh" +#include "client/entity/sound_emitter.hh" +#include "client/gui/settings.hh" +#include "client/resource/sound_effect.hh" + #include "client/const.hh" #include "client/globals.hh" #include "client/session.hh" -#include "client/settings.hh" -#include "client/sound_effect.hh" -#include "client/sound_emitter.hh" -ConfigFloat sound::volume_master(100.0f, 0.0f, 100.0f); -ConfigFloat sound::volume_effects(100.0f, 0.0f, 100.0f); -ConfigFloat sound::volume_music(100.0f, 0.0f, 100.0f); -ConfigFloat sound::volume_ui(100.0f, 0.0f, 100.0f); +config::Float sound::volume_master(100.0f, 0.0f, 100.0f); +config::Float sound::volume_effects(100.0f, 0.0f, 100.0f); +config::Float sound::volume_music(100.0f, 0.0f, 100.0f); +config::Float sound::volume_ui(100.0f, 0.0f, 100.0f); static ALuint generic_source; static ALuint player_source; @@ -84,11 +87,11 @@ void sound::shutdown(void) void sound::update(void) { - auto effects_gain = vx::clamp(0.01f * sound::volume_effects.get_value(), 0.0f, 1.0f); + auto effects_gain = math::clamp(0.01f * sound::volume_effects.get_value(), 0.0f, 1.0f); alSourcef(generic_source, AL_GAIN, effects_gain); alSourcef(player_source, AL_GAIN, effects_gain); - auto ui_gain = vx::clamp(0.01f * sound::volume_ui.get_value(), 0.0f, 1.0f); + auto ui_gain = math::clamp(0.01f * sound::volume_ui.get_value(), 0.0f, 1.0f); alSourcef(ui_source, AL_GAIN, ui_gain); } @@ -137,7 +140,7 @@ void sound::play_generic(resource_ptr<SoundEffect> sound, bool looping, float pi if(sfx_generic) { alSourcei(generic_source, AL_BUFFER, sfx_generic->buffer); alSourcei(generic_source, AL_LOOPING, looping); - alSourcef(generic_source, AL_PITCH, vx::clamp(pitch, MIN_PITCH, MAX_PITCH)); + alSourcef(generic_source, AL_PITCH, math::clamp(pitch, MIN_PITCH, MAX_PITCH)); alSourcePlay(generic_source); } } @@ -145,7 +148,7 @@ void sound::play_generic(resource_ptr<SoundEffect> sound, bool looping, float pi void sound::play_entity(entt::entity entity, resource_ptr<SoundEffect> sound, bool looping, float pitch) { if(globals::dimension && globals::dimension->entities.valid(entity)) { - if(auto emitter = globals::dimension->entities.try_get<SoundEmitterComponent>(entity)) { + if(auto emitter = globals::dimension->entities.try_get<entity::SoundEmitter>(entity)) { alSourceRewind(emitter->source); emitter->sound = sound; @@ -153,7 +156,7 @@ void sound::play_entity(entt::entity entity, resource_ptr<SoundEffect> sound, bo if(emitter->sound) { alSourcei(emitter->source, AL_BUFFER, emitter->sound->buffer); alSourcei(emitter->source, AL_LOOPING, looping); - alSourcef(emitter->source, AL_PITCH, vx::clamp(pitch, MIN_PITCH, MAX_PITCH)); + alSourcef(emitter->source, AL_PITCH, math::clamp(pitch, MIN_PITCH, MAX_PITCH)); alSourcePlay(emitter->source); } } @@ -179,7 +182,7 @@ void sound::play_player(resource_ptr<SoundEffect> sound, bool looping, float pit if(sfx_player) { alSourcei(player_source, AL_BUFFER, sfx_player->buffer); alSourcei(player_source, AL_LOOPING, looping); - alSourcef(player_source, AL_PITCH, vx::clamp(pitch, MIN_PITCH, MAX_PITCH)); + alSourcef(player_source, AL_PITCH, math::clamp(pitch, MIN_PITCH, MAX_PITCH)); alSourcePlay(player_source); } } @@ -193,7 +196,7 @@ void sound::play_ui(resource_ptr<SoundEffect> sound, bool looping, float pitch) if(sfx_ui) { alSourcei(ui_source, AL_BUFFER, sfx_ui->buffer); alSourcei(ui_source, AL_LOOPING, looping); - alSourcef(ui_source, AL_PITCH, vx::clamp(pitch, MIN_PITCH, MAX_PITCH)); + alSourcef(ui_source, AL_PITCH, math::clamp(pitch, MIN_PITCH, MAX_PITCH)); alSourcePlay(ui_source); } }
\ No newline at end of file diff --git a/src/game/client/sound.hh b/game/client/sound/sound.hh index bba9916..5eb3ed7 100644 --- a/src/game/client/sound.hh +++ b/game/client/sound/sound.hh @@ -2,17 +2,21 @@ #define CLIENT_SOUND_HH 1 #pragma once -#include "core/resource.hh" +#include "core/resource/resource.hh" + +namespace config +{ +class Float; +} // namespace config -class ConfigFloat; struct SoundEffect; namespace sound { -extern ConfigFloat volume_master; -extern ConfigFloat volume_effects; -extern ConfigFloat volume_music; -extern ConfigFloat volume_ui; +extern config::Float volume_master; +extern config::Float volume_effects; +extern config::Float volume_music; +extern config::Float volume_ui; } // namespace sound namespace sound diff --git a/src/game/client/toggles.cc b/game/client/toggles.cc index f40d6ed..f5d9561 100644 --- a/src/game/client/toggles.cc +++ b/game/client/toggles.cc @@ -2,14 +2,15 @@ #include "client/toggles.hh" -#include "core/config.hh" +#include "core/io/config_map.hh" + +#include "client/gui/chat.hh" +#include "client/gui/language.hh" +#include "client/io/gamepad.hh" +#include "client/io/glfw.hh" -#include "client/chat.hh" #include "client/const.hh" -#include "client/gamepad.hh" -#include "client/glfw.hh" #include "client/globals.hh" -#include "client/language.hh" struct ToggleInfo final { const char* description; @@ -25,9 +26,9 @@ static void print_toggle_state(const ToggleInfo& info) { if(info.description) { if(info.is_enabled) { - client_chat::print(std::format("[toggles] {} ON", info.description)); + gui::client_chat::print(std::format("[toggles] {} ON", info.description)); } else { - client_chat::print(std::format("[toggles] {} OFF", info.description)); + gui::client_chat::print(std::format("[toggles] {} OFF", info.description)); } } } @@ -45,7 +46,7 @@ static void toggle_value(ToggleInfo& info, toggle_type type) print_toggle_state(info); } -static void on_glfw_key(const GlfwKeyEvent& event) +static void on_glfw_key(const io::GlfwKeyEvent& event) { if(globals::gui_keybind_ptr) { // The UI keybind subsystem has the authority @@ -72,7 +73,7 @@ static void on_glfw_key(const GlfwKeyEvent& event) // This causes the language subsystem // to re-parse the JSON file essentially // causing the game to soft-reload language - language::set(language::get_current()); + gui::language::set(gui::language::get_current()); return; } @@ -115,7 +116,7 @@ void toggles::init(void) toggle_infos[TOGGLE_WIREFRAME].is_enabled = true; #endif - globals::dispatcher.sink<GlfwKeyEvent>().connect<&on_glfw_key>(); + globals::dispatcher.sink<io::GlfwKeyEvent>().connect<&on_glfw_key>(); } void toggles::init_late(void) diff --git a/src/game/client/toggles.hh b/game/client/toggles.hh index 86ef7ea..86ef7ea 100644 --- a/src/game/client/toggles.hh +++ b/game/client/toggles.hh diff --git a/src/game/client/vclient.ico b/game/client/vclient.ico Binary files differindex 5f3ccd2..5f3ccd2 100644 --- a/src/game/client/vclient.ico +++ b/game/client/vclient.ico diff --git a/src/game/client/vclient.rc b/game/client/vclient.rc index 0268ca0..0268ca0 100644 --- a/src/game/client/vclient.rc +++ b/game/client/vclient.rc diff --git a/game/client/world/CMakeLists.txt b/game/client/world/CMakeLists.txt new file mode 100644 index 0000000..fdf96bf --- /dev/null +++ b/game/client/world/CMakeLists.txt @@ -0,0 +1,21 @@ +target_sources(vclient PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/chunk_mesher.cc" + "${CMAKE_CURRENT_LIST_DIR}/chunk_mesher.hh" + "${CMAKE_CURRENT_LIST_DIR}/chunk_quad.hh" + "${CMAKE_CURRENT_LIST_DIR}/chunk_renderer.cc" + "${CMAKE_CURRENT_LIST_DIR}/chunk_renderer.hh" + "${CMAKE_CURRENT_LIST_DIR}/chunk_vbo.hh" + "${CMAKE_CURRENT_LIST_DIR}/chunk_visibility.cc" + "${CMAKE_CURRENT_LIST_DIR}/chunk_visibility.hh" + "${CMAKE_CURRENT_LIST_DIR}/outline.cc" + "${CMAKE_CURRENT_LIST_DIR}/outline.hh" + "${CMAKE_CURRENT_LIST_DIR}/player_target.cc" + "${CMAKE_CURRENT_LIST_DIR}/player_target.hh" + "${CMAKE_CURRENT_LIST_DIR}/skybox.cc" + "${CMAKE_CURRENT_LIST_DIR}/skybox.hh" + "${CMAKE_CURRENT_LIST_DIR}/voxel_anims.cc" + "${CMAKE_CURRENT_LIST_DIR}/voxel_anims.hh" + "${CMAKE_CURRENT_LIST_DIR}/voxel_atlas.cc" + "${CMAKE_CURRENT_LIST_DIR}/voxel_atlas.hh" + "${CMAKE_CURRENT_LIST_DIR}/voxel_sounds.cc" + "${CMAKE_CURRENT_LIST_DIR}/voxel_sounds.hh") diff --git a/src/game/client/chunk_mesher.cc b/game/client/world/chunk_mesher.cc index 24abfa3..5e70626 100644 --- a/src/game/client/chunk_mesher.cc +++ b/game/client/world/chunk_mesher.cc @@ -1,21 +1,23 @@ #include "client/pch.hh" -#include "client/chunk_mesher.hh" +#include "client/world/chunk_mesher.hh" -#include "core/crc64.hh" +#include "core/math/crc64.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" +#include "shared/world/voxel_registry.hh" -#include "shared/chunk.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" #include "shared/threading.hh" -#include "shared/voxel_registry.hh" -#include "client/chunk_quad.hh" +#include "client/world/chunk_quad.hh" +#include "client/world/voxel_atlas.hh" + #include "client/globals.hh" #include "client/session.hh" -#include "client/voxel_atlas.hh" -using QuadBuilder = std::vector<ChunkQuad>; +using QuadBuilder = std::vector<world::ChunkQuad>; using CachedChunkCoord = unsigned short; constexpr static CachedChunkCoord CPOS_ITSELF = 0x0000; @@ -35,9 +37,9 @@ static const CachedChunkCoord get_cached_cpos(const chunk_pos& pivot, const chun if(pivot != cpos) { chunk_pos delta = pivot - cpos; - delta[0] = vx::clamp<std::int64_t>(delta[0], -1, 1); - delta[1] = vx::clamp<std::int64_t>(delta[1], -1, 1); - delta[2] = vx::clamp<std::int64_t>(delta[2], -1, 1); + delta[0] = math::clamp<std::int64_t>(delta[0], -1, 1); + delta[1] = math::clamp<std::int64_t>(delta[1], -1, 1); + delta[2] = math::clamp<std::int64_t>(delta[2], -1, 1); if(delta[0]) { return nx[delta[0] + 1]; @@ -51,34 +53,34 @@ static const CachedChunkCoord get_cached_cpos(const chunk_pos& pivot, const chun return CPOS_ITSELF; } -static voxel_facing get_facing(voxel_face face, voxel_type type) +static world::voxel_facing get_facing(world::voxel_face face, world::voxel_type type) { - if(type == voxel_type::CROSS) { + if(type == world::voxel_type::CROSS) { switch(face) { - case voxel_face::CROSS_NESW: - return voxel_facing::NESW; - case voxel_face::CROSS_NWSE: - return voxel_facing::NWSE; + case world::voxel_face::CROSS_NESW: + return world::voxel_facing::NESW; + case world::voxel_face::CROSS_NWSE: + return world::voxel_facing::NWSE; default: - return voxel_facing::NORTH; + return world::voxel_facing::NORTH; } } switch(face) { - case voxel_face::CUBE_NORTH: - return voxel_facing::NORTH; - case voxel_face::CUBE_SOUTH: - return voxel_facing::SOUTH; - case voxel_face::CUBE_EAST: - return voxel_facing::EAST; - case voxel_face::CUBE_WEST: - return voxel_facing::WEST; - case voxel_face::CUBE_TOP: - return voxel_facing::UP; - case voxel_face::CUBE_BOTTOM: - return voxel_facing::DOWN; + case world::voxel_face::CUBE_NORTH: + return world::voxel_facing::NORTH; + case world::voxel_face::CUBE_SOUTH: + return world::voxel_facing::SOUTH; + case world::voxel_face::CUBE_EAST: + return world::voxel_facing::EAST; + case world::voxel_face::CUBE_WEST: + return world::voxel_facing::WEST; + case world::voxel_face::CUBE_TOP: + return world::voxel_facing::UP; + case world::voxel_face::CUBE_BOTTOM: + return world::voxel_facing::DOWN; default: - return voxel_facing::NORTH; + return world::voxel_facing::NORTH; } } @@ -90,14 +92,15 @@ public: virtual void finalize(void) override; private: - bool vis_test(voxel_id voxel, const VoxelInfo* info, const local_pos& lpos) const; - void push_quad_a(const VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, voxel_face face); - void push_quad_v(const VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, voxel_face face, std::size_t entropy); - void make_cube(voxel_id voxel, const VoxelInfo* info, const local_pos& lpos, voxel_vis vis, std::size_t entropy); + bool vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const; + void push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face); + void push_quad_v( + const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, std::size_t entropy); + void make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, std::size_t entropy); void cache_chunk(const chunk_pos& cpos); private: - std::array<VoxelStorage, NUM_CACHED_CPOS> m_cache; + std::array<world::VoxelStorage, NUM_CACHED_CPOS> m_cache; std::vector<QuadBuilder> m_quads_b; // blending std::vector<QuadBuilder> m_quads_s; // solid entt::entity m_entity; @@ -120,8 +123,8 @@ GL_MeshingTask::GL_MeshingTask(entt::entity entity, const chunk_pos& cpos) void GL_MeshingTask::process(void) { - m_quads_b.resize(voxel_atlas::plane_count()); - m_quads_s.resize(voxel_atlas::plane_count()); + m_quads_b.resize(world::voxel_atlas::plane_count()); + m_quads_s.resize(world::voxel_atlas::plane_count()); const auto& voxels = m_cache.at(CPOS_ITSELF); @@ -135,7 +138,7 @@ void GL_MeshingTask::process(void) const auto voxel = voxels[i]; const auto lpos = coord::to_local(i); - const auto info = voxel_registry::find(voxel); + const auto info = world::voxel_registry::find(voxel); if(info == nullptr) { // Either a NULL_VOXEL_ID or something went @@ -143,35 +146,35 @@ void GL_MeshingTask::process(void) continue; } - voxel_vis vis = 0; + world::voxel_vis vis = 0; if(vis_test(voxel, info, lpos + DIR_NORTH<local_pos::value_type>)) { - vis |= VIS_NORTH; + vis |= world::VIS_NORTH; } if(vis_test(voxel, info, lpos + DIR_SOUTH<local_pos::value_type>)) { - vis |= VIS_SOUTH; + vis |= world::VIS_SOUTH; } if(vis_test(voxel, info, lpos + DIR_EAST<local_pos::value_type>)) { - vis |= VIS_EAST; + vis |= world::VIS_EAST; } if(vis_test(voxel, info, lpos + DIR_WEST<local_pos::value_type>)) { - vis |= VIS_WEST; + vis |= world::VIS_WEST; } if(vis_test(voxel, info, lpos + DIR_UP<local_pos::value_type>)) { - vis |= VIS_UP; + vis |= world::VIS_UP; } if(vis_test(voxel, info, lpos + DIR_DOWN<local_pos::value_type>)) { - vis |= VIS_DOWN; + vis |= world::VIS_DOWN; } const auto vpos = coord::to_voxel(m_cpos, lpos); const auto entropy_src = vpos[0] * vpos[1] * vpos[2]; - const auto entropy = crc64::get(&entropy_src, sizeof(entropy_src)); + const auto entropy = math::crc64(&entropy_src, sizeof(entropy_src)); // FIXME: handle different voxel types make_cube(voxel, info, lpos, vis, entropy); @@ -186,7 +189,7 @@ void GL_MeshingTask::finalize(void) return; } - auto& component = globals::dimension->chunks.emplace_or_replace<ChunkMeshComponent>(m_entity); + auto& component = globals::dimension->chunks.emplace_or_replace<world::ChunkMesh>(m_entity); const std::size_t plane_count_nb = m_quads_s.size(); const std::size_t plane_count_b = m_quads_b.size(); @@ -198,8 +201,8 @@ void GL_MeshingTask::finalize(void) component.quad_b.resize(plane_count_b); for(std::size_t plane = 0; plane < plane_count_nb; ++plane) { - QuadBuilder& builder = m_quads_s[plane]; - ChunkVBO& buffer = component.quad_nb[plane]; + auto& builder = m_quads_s[plane]; + auto& buffer = component.quad_nb[plane]; if(builder.empty()) { if(buffer.handle) { @@ -213,15 +216,15 @@ void GL_MeshingTask::finalize(void) } glBindBuffer(GL_ARRAY_BUFFER, buffer.handle); - glBufferData(GL_ARRAY_BUFFER, sizeof(ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(world::ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); buffer.size = builder.size(); has_no_submeshes_nb = false; } } for(std::size_t plane = 0; plane < plane_count_b; ++plane) { - QuadBuilder& builder = m_quads_b[plane]; - ChunkVBO& buffer = component.quad_b[plane]; + auto& builder = m_quads_b[plane]; + auto& buffer = component.quad_b[plane]; if(builder.empty()) { if(buffer.handle) { @@ -235,18 +238,18 @@ void GL_MeshingTask::finalize(void) } glBindBuffer(GL_ARRAY_BUFFER, buffer.handle); - glBufferData(GL_ARRAY_BUFFER, sizeof(ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(world::ChunkQuad) * builder.size(), builder.data(), GL_STATIC_DRAW); buffer.size = builder.size(); has_no_submeshes_b = false; } } if(has_no_submeshes_b && has_no_submeshes_nb) { - globals::dimension->chunks.remove<ChunkMeshComponent>(m_entity); + globals::dimension->chunks.remove<world::ChunkMesh>(m_entity); } } -bool GL_MeshingTask::vis_test(voxel_id voxel, const VoxelInfo* info, const local_pos& lpos) const +bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const { const auto pvpos = coord::to_voxel(m_cpos, lpos); const auto pcpos = coord::to_chunk(pvpos); @@ -263,7 +266,7 @@ bool GL_MeshingTask::vis_test(voxel_id voxel, const VoxelInfo* info, const local result = true; } else if(neighbour == voxel) { result = false; - } else if(auto neighbour_info = voxel_registry::find(neighbour)) { + } else if(auto neighbour_info = world::voxel_registry::find(neighbour)) { if(neighbour_info->blending != info->blending) { // Voxel types that use blending are semi-transparent; // this means they're rendered using a different setup @@ -279,10 +282,10 @@ bool GL_MeshingTask::vis_test(voxel_id voxel, const VoxelInfo* info, const local return result; } -void GL_MeshingTask::push_quad_a(const VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, voxel_face face) +void GL_MeshingTask::push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face) { - const voxel_facing facing = get_facing(face, info->type); - const VoxelTexture& vtex = info->textures[static_cast<std::size_t>(face)]; + const world::voxel_facing facing = get_facing(face, info->type); + const world::VoxelTexture& vtex = info->textures[static_cast<std::size_t>(face)]; if(info->blending) { m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); @@ -291,10 +294,11 @@ void GL_MeshingTask::push_quad_a(const VoxelInfo* info, const glm::fvec3& pos, c } } -void GL_MeshingTask::push_quad_v(const VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, voxel_face face, std::size_t entropy) +void GL_MeshingTask::push_quad_v( + const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, std::size_t entropy) { - const voxel_facing facing = get_facing(face, info->type); - const VoxelTexture& vtex = info->textures[static_cast<std::size_t>(face)]; + const world::voxel_facing facing = get_facing(face, info->type); + const world::VoxelTexture& vtex = info->textures[static_cast<std::size_t>(face)]; const std::size_t entropy_mod = entropy % vtex.paths.size(); if(info->blending) { @@ -304,58 +308,59 @@ void GL_MeshingTask::push_quad_v(const VoxelInfo* info, const glm::fvec3& pos, c } } -void GL_MeshingTask::make_cube(voxel_id voxel, const VoxelInfo* info, const local_pos& lpos, voxel_vis vis, std::size_t entropy) +void GL_MeshingTask::make_cube( + voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, std::size_t entropy) { const glm::fvec3 fpos = glm::fvec3(lpos); const glm::fvec2 fsize = glm::fvec2(1.0f, 1.0f); if(info->animated) { - if(vis & VIS_NORTH) { - push_quad_a(info, fpos, fsize, voxel_face::CUBE_NORTH); + if(vis & world::VIS_NORTH) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_NORTH); } - if(vis & VIS_SOUTH) { - push_quad_a(info, fpos, fsize, voxel_face::CUBE_SOUTH); + if(vis & world::VIS_SOUTH) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_SOUTH); } - if(vis & VIS_EAST) { - push_quad_a(info, fpos, fsize, voxel_face::CUBE_EAST); + if(vis & world::VIS_EAST) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_EAST); } - if(vis & VIS_WEST) { - push_quad_a(info, fpos, fsize, voxel_face::CUBE_WEST); + if(vis & world::VIS_WEST) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_WEST); } - if(vis & VIS_UP) { - push_quad_a(info, fpos, fsize, voxel_face::CUBE_TOP); + if(vis & world::VIS_UP) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_TOP); } - if(vis & VIS_DOWN) { - push_quad_a(info, fpos, fsize, voxel_face::CUBE_BOTTOM); + if(vis & world::VIS_DOWN) { + push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM); } } else { - if(vis & VIS_NORTH) { - push_quad_v(info, fpos, fsize, voxel_face::CUBE_NORTH, entropy); + if(vis & world::VIS_NORTH) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_NORTH, entropy); } - if(vis & VIS_SOUTH) { - push_quad_v(info, fpos, fsize, voxel_face::CUBE_SOUTH, entropy); + if(vis & world::VIS_SOUTH) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_SOUTH, entropy); } - if(vis & VIS_EAST) { - push_quad_v(info, fpos, fsize, voxel_face::CUBE_EAST, entropy); + if(vis & world::VIS_EAST) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_EAST, entropy); } - if(vis & VIS_WEST) { - push_quad_v(info, fpos, fsize, voxel_face::CUBE_WEST, entropy); + if(vis & world::VIS_WEST) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_WEST, entropy); } - if(vis & VIS_UP) { - push_quad_v(info, fpos, fsize, voxel_face::CUBE_TOP, entropy); + if(vis & world::VIS_UP) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_TOP, entropy); } - if(vis & VIS_DOWN) { - push_quad_v(info, fpos, fsize, voxel_face::CUBE_BOTTOM, entropy); + if(vis & world::VIS_DOWN) { + push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM, entropy); } } } @@ -373,7 +378,7 @@ void GL_MeshingTask::cache_chunk(const chunk_pos& cpos) // Bogus internal flag component struct NeedsMeshingComponent final {}; -static void on_chunk_create(const ChunkCreateEvent& event) +static void on_chunk_create(const world::ChunkCreateEvent& event) { const std::array<chunk_pos, 6> neighbours = { event.cpos + DIR_NORTH<chunk_pos::value_type>, @@ -387,14 +392,14 @@ static void on_chunk_create(const ChunkCreateEvent& event) globals::dimension->chunks.emplace_or_replace<NeedsMeshingComponent>(event.chunk->get_entity()); for(const chunk_pos& cpos : neighbours) { - if(const Chunk* chunk = globals::dimension->find_chunk(cpos)) { + if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { globals::dimension->chunks.emplace_or_replace<NeedsMeshingComponent>(chunk->get_entity()); continue; } } } -static void on_chunk_update(const ChunkUpdateEvent& event) +static void on_chunk_update(const world::ChunkUpdateEvent& event) { const std::array<chunk_pos, 6> neighbours = { event.cpos + DIR_NORTH<chunk_pos::value_type>, @@ -408,14 +413,14 @@ static void on_chunk_update(const ChunkUpdateEvent& event) globals::dimension->chunks.emplace_or_replace<NeedsMeshingComponent>(event.chunk->get_entity()); for(const chunk_pos& cpos : neighbours) { - if(const Chunk* chunk = globals::dimension->find_chunk(cpos)) { + if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { globals::dimension->chunks.emplace_or_replace<NeedsMeshingComponent>(chunk->get_entity()); continue; } } } -static void on_voxel_set(const VoxelSetEvent& event) +static void on_voxel_set(const world::VoxelSetEvent& event) { globals::dimension->chunks.emplace_or_replace<NeedsMeshingComponent>(event.chunk->get_entity()); @@ -437,25 +442,25 @@ static void on_voxel_set(const VoxelSetEvent& event) } for(const chunk_pos& cpos : neighbours) { - if(const Chunk* chunk = globals::dimension->find_chunk(cpos)) { + if(const world::Chunk* chunk = globals::dimension->find_chunk(cpos)) { globals::dimension->chunks.emplace_or_replace<NeedsMeshingComponent>(chunk->get_entity()); continue; } } } -void chunk_mesher::init(void) +void world::chunk_mesher::init(void) { globals::dispatcher.sink<ChunkCreateEvent>().connect<&on_chunk_create>(); globals::dispatcher.sink<ChunkUpdateEvent>().connect<&on_chunk_update>(); globals::dispatcher.sink<VoxelSetEvent>().connect<&on_voxel_set>(); } -void chunk_mesher::shutdown(void) +void world::chunk_mesher::shutdown(void) { } -void chunk_mesher::update(void) +void world::chunk_mesher::update(void) { if(session::is_ingame()) { const auto group = globals::dimension->chunks.group<NeedsMeshingComponent>(entt::get<ChunkComponent>); diff --git a/src/game/client/chunk_mesher.hh b/game/client/world/chunk_mesher.hh index ca234f0..14df019 100644 --- a/src/game/client/chunk_mesher.hh +++ b/game/client/world/chunk_mesher.hh @@ -2,18 +2,21 @@ #define CLIENT_CHUNK_MESHER_HH 1 #pragma once -#include "client/chunk_vbo.hh" +#include "client/world/chunk_vbo.hh" -struct ChunkMeshComponent final { +namespace world +{ +struct ChunkMesh final { std::vector<ChunkVBO> quad_nb; std::vector<ChunkVBO> quad_b; }; +} // namespace world -namespace chunk_mesher +namespace world::chunk_mesher { void init(void); void shutdown(void); void update(void); -} // namespace chunk_mesher +} // namespace world::chunk_mesher #endif // CLIENT_CHUNK_MESHER_HH diff --git a/src/game/client/chunk_quad.hh b/game/client/world/chunk_quad.hh index cb2c03d..9b3ca47 100644 --- a/src/game/client/chunk_quad.hh +++ b/game/client/world/chunk_quad.hh @@ -2,14 +2,19 @@ #define CLIENT_CHUNK_QUAD_HH 1 #pragma once -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" -#include "shared/voxel_registry.hh" +#include "shared/world/voxel_registry.hh" +namespace world +{ // [0] XXXXXXXXYYYYYYYYZZZZZZZZWWWWHHHH // [1] FFFFTTTTTTTTTTTAAAAA------------ using ChunkQuad = std::array<std::uint32_t, 2>; +} // namespace world +namespace world +{ constexpr inline static ChunkQuad make_chunk_quad( const glm::fvec3& position, const glm::fvec2& size, voxel_facing facing, std::size_t texture, std::size_t frames) { @@ -35,5 +40,6 @@ constexpr inline static ChunkQuad make_chunk_quad( return result; } +} // namespace world #endif // CLIENT_CHUNK_QUAD_HH diff --git a/src/game/client/chunk_renderer.cc b/game/client/world/chunk_renderer.cc index a7f3148..0962010 100644 --- a/src/game/client/chunk_renderer.cc +++ b/game/client/world/chunk_renderer.cc @@ -1,32 +1,36 @@ #include "client/pch.hh" -#include "client/chunk_renderer.hh" +#include "client/world/chunk_renderer.hh" -#include "core/config.hh" +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/io/config_map.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" -#include "shared/chunk.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "client/camera.hh" -#include "client/chunk_mesher.hh" -#include "client/chunk_quad.hh" +#include "client/entity/camera.hh" +#include "client/gui/settings.hh" +#include "client/world/chunk_mesher.hh" +#include "client/world/chunk_quad.hh" +#include "client/world/outline.hh" +#include "client/world/skybox.hh" +#include "client/world/voxel_anims.hh" +#include "client/world/voxel_atlas.hh" + #include "client/game.hh" #include "client/globals.hh" -#include "client/outline.hh" #include "client/program.hh" -#include "client/settings.hh" -#include "client/skybox.hh" #include "client/toggles.hh" -#include "client/voxel_anims.hh" -#include "client/voxel_atlas.hh" // ONLY TOUCH THESE IF THE RESPECTIVE SHADER // VARIANT MACRO DECLARATIONS LAYOUT CHANGED AS WELL constexpr static unsigned int WORLD_CURVATURE = 0U; constexpr static unsigned int WORLD_FOG = 1U; -static ConfigBoolean depth_sort_chunks(true); +static config::Boolean depth_sort_chunks(true); static GL_Program quad_program; static std::size_t u_quad_vproj_matrix; @@ -38,7 +42,7 @@ static std::size_t u_quad_textures; static GLuint quad_vaobj; static GLuint quad_vbo; -void chunk_renderer::init(void) +void world::chunk_renderer::init(void) { globals::client_config.add_value("chunk_renderer.depth_sort_chunks", depth_sort_chunks); @@ -75,14 +79,14 @@ void chunk_renderer::init(void) glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(glm::fvec3), nullptr); } -void chunk_renderer::shutdown(void) +void world::chunk_renderer::shutdown(void) { glDeleteBuffers(1, &quad_vbo); glDeleteVertexArrays(1, &quad_vaobj); quad_program.destroy(); } -void chunk_renderer::render(void) +void world::chunk_renderer::render(void) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -107,17 +111,17 @@ void chunk_renderer::render(void) GLuint timings[3]; timings[0] = globals::window_frametime; timings[1] = globals::window_frametime_avg; - timings[2] = voxel_anims::frame; + timings[2] = world::voxel_anims::frame; - const auto group = globals::dimension->chunks.group<ChunkComponent>(entt::get<ChunkMeshComponent>); + const auto group = globals::dimension->chunks.group<ChunkComponent>(entt::get<world::ChunkMesh>); if(depth_sort_chunks.get_value()) { // FIXME: speed! sorting every frame doesn't look // like a good idea. Can we store the group elsewhere and // still have all the up-to-date chunk things inside? group.sort([](entt::entity ea, entt::entity eb) { - const auto dir_a = globals::dimension->chunks.get<ChunkComponent>(ea).cpos - camera::position_chunk; - const auto dir_b = globals::dimension->chunks.get<ChunkComponent>(eb).cpos - camera::position_chunk; + const auto dir_a = globals::dimension->chunks.get<ChunkComponent>(ea).cpos - entity::camera::position_chunk; + const auto dir_b = globals::dimension->chunks.get<ChunkComponent>(eb).cpos - entity::camera::position_chunk; const auto da = dir_a[0] * dir_a[0] + dir_a[1] * dir_a[1] + dir_a[2] * dir_a[2]; const auto db = dir_b[0] * dir_b[0] + dir_b[1] * dir_b[1] + dir_b[2] * dir_b[2]; @@ -126,17 +130,17 @@ void chunk_renderer::render(void) }); } - for(std::size_t plane_id = 0; plane_id < voxel_atlas::plane_count(); ++plane_id) { + for(std::size_t plane_id = 0; plane_id < world::voxel_atlas::plane_count(); ++plane_id) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D_ARRAY, voxel_atlas::plane_texture(plane_id)); + glBindTexture(GL_TEXTURE_2D_ARRAY, world::voxel_atlas::plane_texture(plane_id)); glBindVertexArray(quad_vaobj); glUseProgram(quad_program.handle); - glUniformMatrix4fv(quad_program.uniforms[u_quad_vproj_matrix].location, 1, false, glm::value_ptr(camera::matrix)); + glUniformMatrix4fv(quad_program.uniforms[u_quad_vproj_matrix].location, 1, false, glm::value_ptr(entity::camera::matrix)); glUniform3uiv(quad_program.uniforms[u_quad_timings].location, 1, timings); - glUniform4fv(quad_program.uniforms[u_quad_fog_color].location, 1, glm::value_ptr(skybox::fog_color)); - glUniform1f(quad_program.uniforms[u_quad_view_distance].location, camera::view_distance.get_value() * CHUNK_SIZE); + glUniform4fv(quad_program.uniforms[u_quad_fog_color].location, 1, glm::value_ptr(world::skybox::fog_color)); + glUniform1f(quad_program.uniforms[u_quad_view_distance].location, entity::camera::view_distance.get_value() * CHUNK_SIZE); glUniform1i(quad_program.uniforms[u_quad_textures].location, 0); // GL_TEXTURE0 glDisable(GL_BLEND); @@ -147,7 +151,7 @@ void chunk_renderer::render(void) for(const auto [entity, chunk, mesh] : group.each()) { if(plane_id < mesh.quad_nb.size() && mesh.quad_nb[plane_id].handle && mesh.quad_nb[plane_id].size) { - const auto wpos = coord::to_fvec3(chunk.cpos - camera::position_chunk); + const auto wpos = coord::to_fvec3(chunk.cpos - entity::camera::position_chunk); glUniform3fv(quad_program.uniforms[u_quad_world_position].location, 1, glm::value_ptr(wpos)); glBindBuffer(GL_ARRAY_BUFFER, mesh.quad_nb[plane_id].handle); @@ -168,7 +172,7 @@ void chunk_renderer::render(void) for(const auto [entity, chunk, mesh] : group.each()) { if(plane_id < mesh.quad_b.size() && mesh.quad_b[plane_id].handle && mesh.quad_b[plane_id].size) { - const auto wpos = coord::to_fvec3(chunk.cpos - camera::position_chunk); + const auto wpos = coord::to_fvec3(chunk.cpos - entity::camera::position_chunk); glUniform3fv(quad_program.uniforms[u_quad_world_position].location, 1, glm::value_ptr(wpos)); glBindBuffer(GL_ARRAY_BUFFER, mesh.quad_b[plane_id].handle); @@ -186,11 +190,11 @@ void chunk_renderer::render(void) } if(toggles::get(TOGGLE_CHUNK_AABB)) { - outline::prepare(); + world::outline::prepare(); for(const auto [entity, chunk, mesh] : group.each()) { const auto size = glm::fvec3(CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); - outline::cube(chunk.cpos, glm::fvec3(0.0f, 0.0f, 0.0f), size, 1.0f, glm::fvec4(1.0f, 1.0f, 0.0f, 1.0f)); + world::outline::cube(chunk.cpos, glm::fvec3(0.0f, 0.0f, 0.0f), size, 1.0f, glm::fvec4(1.0f, 1.0f, 0.0f, 1.0f)); } } } diff --git a/src/game/client/chunk_renderer.hh b/game/client/world/chunk_renderer.hh index e005191..fdc5eea 100644 --- a/src/game/client/chunk_renderer.hh +++ b/game/client/world/chunk_renderer.hh @@ -2,11 +2,11 @@ #define CLIENT_CHUNK_RENDERER_HH 1 #pragma once -namespace chunk_renderer +namespace world::chunk_renderer { void init(void); void shutdown(void); void render(void); -} // namespace chunk_renderer +} // namespace world::chunk_renderer #endif // CLIENT_CHUNK_RENDERER_HH diff --git a/src/game/client/chunk_vbo.hh b/game/client/world/chunk_vbo.hh index 899abc6..586c313 100644 --- a/src/game/client/chunk_vbo.hh +++ b/game/client/world/chunk_vbo.hh @@ -2,6 +2,8 @@ #define CLIENT_CHUNK_VBO_HH 1 #pragma once +namespace world +{ class ChunkVBO final { public: std::size_t size; @@ -19,5 +21,6 @@ public: } } }; +} // namespace world #endif // CLIENT_CHUNK_VBO_HH diff --git a/src/game/client/chunk_visibility.cc b/game/client/world/chunk_visibility.cc index f832529..8f76755 100644 --- a/src/game/client/chunk_visibility.cc +++ b/game/client/world/chunk_visibility.cc @@ -1,16 +1,18 @@ #include "client/pch.hh" -#include "client/chunk_visibility.hh" +#include "client/world/chunk_visibility.hh" -#include "core/config.hh" -#include "core/vectors.hh" +#include "core/config/number.hh" +#include "core/math/vectors.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/chunk_aabb.hh" +#include "shared/world/dimension.hh" -#include "shared/chunk.hh" -#include "shared/chunk_aabb.hh" -#include "shared/dimension.hh" #include "shared/protocol.hh" -#include "client/camera.hh" +#include "client/entity/camera.hh" + #include "client/globals.hh" #include "client/session.hh" @@ -20,8 +22,8 @@ // we throttle the client's ever increasing itch for new chunks constexpr static unsigned int MAX_CHUNKS_REQUESTS_PER_FRAME = 16U; -static ChunkAABB current_view_box; -static ChunkAABB previous_view_box; +static world::ChunkAABB current_view_box; +static world::ChunkAABB previous_view_box; static std::vector<chunk_pos> requests; static void update_requests(void) @@ -39,16 +41,16 @@ static void update_requests(void) } std::sort(requests.begin(), requests.end(), [](const chunk_pos& cpos_a, const chunk_pos& cpos_b) { - auto da = vx::distance2(cpos_a, camera::position_chunk); - auto db = vx::distance2(cpos_b, camera::position_chunk); + auto da = math::distance2(cpos_a, entity::camera::position_chunk); + auto db = math::distance2(cpos_b, entity::camera::position_chunk); return da > db; }); } -void chunk_visibility::update_late(void) +void world::chunk_visibility::update_late(void) { - current_view_box.min = camera::position_chunk - static_cast<chunk_pos::value_type>(camera::view_distance.get_value()); - current_view_box.max = camera::position_chunk + static_cast<chunk_pos::value_type>(camera::view_distance.get_value()); + current_view_box.min = entity::camera::position_chunk - static_cast<chunk_pos::value_type>(entity::camera::view_distance.get_value()); + current_view_box.max = entity::camera::position_chunk + static_cast<chunk_pos::value_type>(entity::camera::view_distance.get_value()); if(!session::is_ingame()) { // This makes sure the previous view box diff --git a/src/game/client/chunk_visibility.hh b/game/client/world/chunk_visibility.hh index 9c0eed1..a57baa1 100644 --- a/src/game/client/chunk_visibility.hh +++ b/game/client/world/chunk_visibility.hh @@ -2,11 +2,9 @@ #define CLIENT_CHUNK_VISIBILITY_HH 1 #pragma once -#include "shared/types.hh" - -namespace chunk_visibility +namespace world::chunk_visibility { void update_late(void); -} // namespace chunk_visibility +} // namespace world::chunk_visibility #endif // CLIENT_CHUNK_VISIBILITY_HH diff --git a/src/game/client/outline.cc b/game/client/world/outline.cc index bde179b..396d297 100644 --- a/src/game/client/outline.cc +++ b/game/client/world/outline.cc @@ -1,12 +1,14 @@ #include "client/pch.hh" -#include "client/outline.hh" +#include "client/world/outline.hh" -#include "core/config.hh" +#include "core/config/boolean.hh" +#include "core/config/number.hh" #include "shared/coord.hh" -#include "client/camera.hh" +#include "client/entity/camera.hh" + #include "client/const.hh" #include "client/game.hh" #include "client/program.hh" @@ -26,7 +28,7 @@ static GLuint vaobj; static GLuint cube_vbo; static GLuint line_vbo; -void outline::init(void) +void world::outline::init(void) { if(!program.setup("shaders/outline.vert", "shaders/outline.frag")) { spdlog::critical("outline: program setup failed"); @@ -88,7 +90,7 @@ void outline::init(void) glVertexAttribDivisor(0, 0); } -void outline::shutdown(void) +void world::outline::shutdown(void) { glDeleteVertexArrays(1, &vaobj); glDeleteBuffers(1, &line_vbo); @@ -96,7 +98,7 @@ void outline::shutdown(void) program.destroy(); } -void outline::prepare(void) +void world::outline::prepare(void) { program.set_variant_vert(WORLD_CURVATURE, client_game::world_curvature.get_value()); @@ -109,17 +111,17 @@ void outline::prepare(void) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glUseProgram(program.handle); - glUniformMatrix4fv(program.uniforms[u_vpmatrix].location, 1, false, glm::value_ptr(camera::matrix)); - glUniform1f(program.uniforms[u_viewdist].location, CHUNK_SIZE * camera::view_distance.get_value()); + glUniformMatrix4fv(program.uniforms[u_vpmatrix].location, 1, false, glm::value_ptr(entity::camera::matrix)); + glUniform1f(program.uniforms[u_viewdist].location, CHUNK_SIZE * entity::camera::view_distance.get_value()); glBindVertexArray(vaobj); glEnableVertexAttribArray(0); glVertexAttribDivisor(0, 0); } -void outline::cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) +void world::outline::cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) { - auto patch_cpos = cpos - camera::position_chunk; + auto patch_cpos = cpos - entity::camera::position_chunk; glLineWidth(thickness); @@ -132,9 +134,9 @@ void outline::cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fve glDrawArrays(GL_LINES, 0, 24); } -void outline::line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) +void world::outline::line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color) { - auto patch_cpos = cpos - camera::position_chunk; + auto patch_cpos = cpos - entity::camera::position_chunk; glLineWidth(thickness); diff --git a/src/game/client/outline.hh b/game/client/world/outline.hh index 6d7583e..5cef7ed 100644 --- a/src/game/client/outline.hh +++ b/game/client/world/outline.hh @@ -4,17 +4,17 @@ #include "shared/types.hh" -namespace outline +namespace world::outline { void init(void); void shutdown(void); void prepare(void); -} // namespace outline +} // namespace world::outline -namespace outline +namespace world::outline { void cube(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color); void line(const chunk_pos& cpos, const glm::fvec3& fpos, const glm::fvec3& size, float thickness, const glm::fvec4& color); -} // namespace outline +} // namespace world::outline #endif // CLIENT_OUTLINE_HH diff --git a/game/client/world/player_target.cc b/game/client/world/player_target.cc new file mode 100644 index 0000000..6da8129 --- /dev/null +++ b/game/client/world/player_target.cc @@ -0,0 +1,68 @@ +#include "client/pch.hh" + +#include "client/world/player_target.hh" + +#include "shared/world/dimension.hh" +#include "shared/world/ray_dda.hh" + +#include "shared/coord.hh" + +#include "client/entity/camera.hh" +#include "client/world/outline.hh" + +#include "client/game.hh" +#include "client/globals.hh" +#include "client/session.hh" + +constexpr static float MAX_REACH = 16.0f; + +voxel_id world::player_target::voxel; +voxel_pos world::player_target::coord; +voxel_pos world::player_target::normal; +const world::VoxelInfo* world::player_target::info; + +void world::player_target::init(void) +{ + world::player_target::voxel = NULL_VOXEL_ID; + world::player_target::coord = voxel_pos(); + world::player_target::normal = voxel_pos(); + world::player_target::info = nullptr; +} + +void world::player_target::update(void) +{ + if(session::is_ingame()) { + RayDDA ray(globals::dimension, entity::camera::position_chunk, entity::camera::position_local, entity::camera::direction); + + do { + world::player_target::voxel = ray.step(); + + if(world::player_target::voxel != NULL_VOXEL_ID) { + world::player_target::coord = ray.vpos; + world::player_target::normal = ray.vnormal; + world::player_target::info = world::voxel_registry::find(world::player_target::voxel); + break; + } + + world::player_target::coord = voxel_pos(); + world::player_target::normal = voxel_pos(); + world::player_target::info = nullptr; + } while(ray.distance < MAX_REACH); + } else { + world::player_target::voxel = NULL_VOXEL_ID; + world::player_target::coord = voxel_pos(); + world::player_target::normal = voxel_pos(); + world::player_target::info = nullptr; + } +} + +void world::player_target::render(void) +{ + if((world::player_target::voxel != NULL_VOXEL_ID) && !client_game::hide_hud) { + auto cpos = coord::to_chunk(world::player_target::coord); + auto fpos = coord::to_local(world::player_target::coord); + + world::outline::prepare(); + world::outline::cube(cpos, glm::fvec3(fpos), glm::fvec3(1.0f), 2.0f, glm::fvec4(0.0f, 0.0f, 0.0f, 1.0f)); + } +} diff --git a/src/game/client/player_target.hh b/game/client/world/player_target.hh index d49c1d2..33d90fb 100644 --- a/src/game/client/player_target.hh +++ b/game/client/world/player_target.hh @@ -2,21 +2,21 @@ #define CLIENT_PLAYER_TARGET_HH 1 #pragma once -#include "shared/voxel_registry.hh" +#include "shared/world/voxel_registry.hh" -namespace player_target +namespace world::player_target { extern voxel_id voxel; extern voxel_pos coord; extern voxel_pos normal; extern const VoxelInfo* info; -} // namespace player_target +} // namespace world::player_target -namespace player_target +namespace world::player_target { void init(void); void update(void); void render(void); -} // namespace player_target +} // namespace world::player_target #endif // CLIENT_PLAYER_TARGET_HH diff --git a/game/client/world/skybox.cc b/game/client/world/skybox.cc new file mode 100644 index 0000000..5e52fa4 --- /dev/null +++ b/game/client/world/skybox.cc @@ -0,0 +1,11 @@ +#include "client/pch.hh" + +#include "client/world/skybox.hh" + +glm::fvec3 world::skybox::fog_color; + +void world::skybox::init(void) +{ + // https://convertingcolors.com/hex-color-B1F3FF.html + world::skybox::fog_color = glm::fvec3(0.690f, 0.950f, 1.000f); +} diff --git a/src/game/client/skybox.hh b/game/client/world/skybox.hh index 9a23029..6dd82f9 100644 --- a/src/game/client/skybox.hh +++ b/game/client/world/skybox.hh @@ -2,14 +2,14 @@ #define CLIENT_SKYBOX_HH 1 #pragma once -namespace skybox +namespace world::skybox { extern glm::fvec3 fog_color; -} // namespace skybox +} // namespace world::skybox -namespace skybox +namespace world::skybox { void init(void); -} // namespace skybox +} // namespace world::skybox #endif // CLIENT_SKYBOX_HH diff --git a/game/client/world/voxel_anims.cc b/game/client/world/voxel_anims.cc new file mode 100644 index 0000000..9e5e035 --- /dev/null +++ b/game/client/world/voxel_anims.cc @@ -0,0 +1,31 @@ +#include "client/pch.hh" + +#include "client/world/voxel_anims.hh" + +#include "core/config/number.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" + +#include "client/globals.hh" + +static config::Unsigned base_framerate(16U, 1U, 16U); + +std::uint64_t world::voxel_anims::nextframe = 0U; +std::uint32_t world::voxel_anims::frame = 0U; + +void world::voxel_anims::init(void) +{ + globals::client_config.add_value("voxel_anims.base_framerate", base_framerate); + + world::voxel_anims::nextframe = 0U; + world::voxel_anims::frame = 0U; +} + +void world::voxel_anims::update(void) +{ + if(globals::curtime >= world::voxel_anims::nextframe) { + world::voxel_anims::nextframe = + globals::curtime + static_cast<std::uint64_t>(1000000.0 / static_cast<float>(base_framerate.get_value())); + world::voxel_anims::frame += 1U; + } +} diff --git a/src/game/client/voxel_anims.hh b/game/client/world/voxel_anims.hh index fe0b75f..5e9c56f 100644 --- a/src/game/client/voxel_anims.hh +++ b/game/client/world/voxel_anims.hh @@ -2,16 +2,16 @@ #define CLIENT_VOXEL_ANIMS_HH 1 #pragma once -namespace voxel_anims +namespace world::voxel_anims { extern std::uint64_t nextframe; extern std::uint32_t frame; -} // namespace voxel_anims +} // namespace world::voxel_anims -namespace voxel_anims +namespace world::voxel_anims { void init(void); void update(void); -} // namespace voxel_anims +} // namespace world::voxel_anims #endif // CLIENT_VOXEL_ANIMS_HH diff --git a/src/game/client/voxel_atlas.cc b/game/client/world/voxel_atlas.cc index ef996e2..c327deb 100644 --- a/src/game/client/voxel_atlas.cc +++ b/game/client/world/voxel_atlas.cc @@ -1,15 +1,15 @@ #include "client/pch.hh" -#include "client/voxel_atlas.hh" +#include "client/world/voxel_atlas.hh" -#include "core/constexpr.hh" -#include "core/crc64.hh" -#include "core/image.hh" -#include "core/resource.hh" +#include "core/math/constexpr.hh" +#include "core/math/crc64.hh" +#include "core/resource/image.hh" +#include "core/resource/resource.hh" struct AtlasPlane final { std::unordered_map<std::size_t, std::size_t> lookup; - std::vector<AtlasStrip> strips; + std::vector<world::AtlasStrip> strips; std::size_t layer_count_max; std::size_t layer_count; std::size_t plane_id; @@ -29,8 +29,8 @@ static std::size_t vector_hash(const std::vector<std::string>& strings) { std::size_t source = 0; for(const std::string& str : strings) - source += crc64::get(str); - return crc64::get(&source, sizeof(source)); + source += math::crc64(str); + return math::crc64(&source, sizeof(source)); } static void plane_setup(AtlasPlane& plane) @@ -44,7 +44,7 @@ static void plane_setup(AtlasPlane& plane) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); } -static AtlasStrip* plane_lookup(AtlasPlane& plane, std::size_t hash_value) +static world::AtlasStrip* plane_lookup(AtlasPlane& plane, std::size_t hash_value) { const auto it = plane.lookup.find(hash_value); @@ -55,9 +55,9 @@ static AtlasStrip* plane_lookup(AtlasPlane& plane, std::size_t hash_value) return nullptr; } -static AtlasStrip* plane_new_strip(AtlasPlane& plane, const std::vector<std::string>& paths, std::size_t hash_value) +static world::AtlasStrip* plane_new_strip(AtlasPlane& plane, const std::vector<std::string>& paths, std::size_t hash_value) { - AtlasStrip strip = {}; + world::AtlasStrip strip = {}; strip.offset = plane.layer_count; strip.plane = plane.plane_id; @@ -84,23 +84,23 @@ static AtlasStrip* plane_new_strip(AtlasPlane& plane, const std::vector<std::str return &plane.strips[index]; } -void voxel_atlas::create(int width, int height, std::size_t count) +void world::voxel_atlas::create(int width, int height, std::size_t count) { GLint max_plane_layers; - atlas_width = 1 << vx::log2(width); - atlas_height = 1 << vx::log2(height); + atlas_width = 1 << math::log2(width); + atlas_height = 1 << math::log2(height); // Clipping this at OpenGL 4.5 limit of 2048 is important due to // how voxel quad meshes are packed in memory: each texture index is // confined in 11 bits so having bigger atlas planes makes no sense; glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max_plane_layers); - max_plane_layers = vx::clamp(max_plane_layers, 256, 2048); + max_plane_layers = math::clamp(max_plane_layers, 256, 2048); for(long i = count; i > 0L; i -= max_plane_layers) { AtlasPlane plane = {}; plane.plane_id = planes.size(); - plane.layer_count_max = vx::min<std::size_t>(max_plane_layers, i); + plane.layer_count_max = math::min<std::size_t>(max_plane_layers, i); plane.layer_count = 0; const std::size_t save_id = plane.plane_id; @@ -113,7 +113,7 @@ void voxel_atlas::create(int width, int height, std::size_t count) spdlog::debug("voxel_atlas: max_plane_layers={}", max_plane_layers); } -void voxel_atlas::destroy(void) +void world::voxel_atlas::destroy(void) { for(const AtlasPlane& plane : planes) glDeleteTextures(1, &plane.gl_texture); @@ -122,12 +122,12 @@ void voxel_atlas::destroy(void) planes.clear(); } -std::size_t voxel_atlas::plane_count(void) +std::size_t world::voxel_atlas::plane_count(void) { return planes.size(); } -GLuint voxel_atlas::plane_texture(std::size_t plane_id) +GLuint world::voxel_atlas::plane_texture(std::size_t plane_id) { if(plane_id < planes.size()) { return planes[plane_id].gl_texture; @@ -136,7 +136,7 @@ GLuint voxel_atlas::plane_texture(std::size_t plane_id) } } -void voxel_atlas::generate_mipmaps(void) +void world::voxel_atlas::generate_mipmaps(void) { for(const AtlasPlane& plane : planes) { glBindTexture(GL_TEXTURE_2D_ARRAY, plane.gl_texture); @@ -144,7 +144,7 @@ void voxel_atlas::generate_mipmaps(void) } } -AtlasStrip* voxel_atlas::find_or_load(const std::vector<std::string>& paths) +world::AtlasStrip* world::voxel_atlas::find_or_load(const std::vector<std::string>& paths) { const std::size_t hash_value = vector_hash(paths); @@ -167,7 +167,7 @@ AtlasStrip* voxel_atlas::find_or_load(const std::vector<std::string>& paths) return nullptr; } -AtlasStrip* voxel_atlas::find(const std::vector<std::string>& paths) +world::AtlasStrip* world::voxel_atlas::find(const std::vector<std::string>& paths) { const std::size_t hash_value = vector_hash(paths); diff --git a/src/game/client/voxel_atlas.hh b/game/client/world/voxel_atlas.hh index 724b482..f94c6bd 100644 --- a/src/game/client/voxel_atlas.hh +++ b/game/client/world/voxel_atlas.hh @@ -2,28 +2,31 @@ #define CLIENT_VOXEL_ATLAS_HH 1 #pragma once +namespace world +{ struct AtlasStrip final { std::size_t offset; std::size_t plane; }; +} // namespace world -namespace voxel_atlas +namespace world::voxel_atlas { void create(int width, int height, std::size_t count); void destroy(void); -} // namespace voxel_atlas +} // namespace world::voxel_atlas -namespace voxel_atlas +namespace world::voxel_atlas { std::size_t plane_count(void); GLuint plane_texture(std::size_t plane_id); void generate_mipmaps(void); -} // namespace voxel_atlas +} // namespace world::voxel_atlas -namespace voxel_atlas +namespace world::voxel_atlas { AtlasStrip* find_or_load(const std::vector<std::string>& paths); AtlasStrip* find(const std::vector<std::string>& paths); -} // namespace voxel_atlas +} // namespace world::voxel_atlas #endif // CLIENT_VOXEL_ATLAS_HH diff --git a/src/game/client/voxel_sounds.cc b/game/client/world/voxel_sounds.cc index 996dc61..235a851 100644 --- a/src/game/client/voxel_sounds.cc +++ b/game/client/world/voxel_sounds.cc @@ -1,15 +1,15 @@ #include "client/pch.hh" -#include "client/voxel_sounds.hh" +#include "client/world/voxel_sounds.hh" -#include "client/sound_effect.hh" +#include "client/resource/sound_effect.hh" -constexpr static std::size_t NUM_SURFACES = static_cast<std::size_t>(voxel_surface::COUNT); +constexpr static std::size_t NUM_SURFACES = static_cast<std::size_t>(world::voxel_surface::COUNT); static std::vector<resource_ptr<SoundEffect>> footsteps_sounds[NUM_SURFACES]; static std::mt19937_64 randomizer; -static void add_footsteps_effect(voxel_surface surface, const char* name) +static void add_footsteps_effect(world::voxel_surface surface, const char* name) { if(auto effect = resource::load<SoundEffect>(name)) { auto surface_index = static_cast<std::size_t>(surface); @@ -17,7 +17,7 @@ static void add_footsteps_effect(voxel_surface surface, const char* name) } } -static resource_ptr<SoundEffect> get_footsteps_effect(voxel_surface surface) +static resource_ptr<SoundEffect> get_footsteps_effect(world::voxel_surface surface) { auto surface_index = static_cast<std::size_t>(surface); @@ -37,7 +37,7 @@ static resource_ptr<SoundEffect> get_footsteps_effect(voxel_surface surface) return sounds.at(dist(randomizer)); } -void voxel_sounds::init(void) +void world::voxel_sounds::init(void) { add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default1.wav"); add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default2.wav"); @@ -60,14 +60,14 @@ void voxel_sounds::init(void) add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood3.wav"); } -void voxel_sounds::shutdown(void) +void world::voxel_sounds::shutdown(void) { for(std::size_t i = 0; i < NUM_SURFACES; ++i) { footsteps_sounds[i].clear(); } } -resource_ptr<SoundEffect> voxel_sounds::get_footsteps(voxel_surface surface) +resource_ptr<SoundEffect> world::voxel_sounds::get_footsteps(voxel_surface surface) { if(auto effect = get_footsteps_effect(surface)) { return effect; @@ -80,7 +80,7 @@ resource_ptr<SoundEffect> voxel_sounds::get_footsteps(voxel_surface surface) return nullptr; } -resource_ptr<SoundEffect> voxel_sounds::get_placebreak(voxel_surface surface) +resource_ptr<SoundEffect> world::voxel_sounds::get_placebreak(voxel_surface surface) { return nullptr; } diff --git a/src/game/client/voxel_sounds.hh b/game/client/world/voxel_sounds.hh index 86a5e99..d6f860f 100644 --- a/src/game/client/voxel_sounds.hh +++ b/game/client/world/voxel_sounds.hh @@ -2,22 +2,22 @@ #define CLIENT_VOXEL_SOUNDS_HH 1 #pragma once -#include "core/resource.hh" +#include "core/resource/resource.hh" -#include "shared/voxel_registry.hh" +#include "shared/world/voxel_registry.hh" struct SoundEffect; -namespace voxel_sounds +namespace world::voxel_sounds { void init(void); void shutdown(void); -} // namespace voxel_sounds +} // namespace world::voxel_sounds -namespace voxel_sounds +namespace world::voxel_sounds { resource_ptr<SoundEffect> get_footsteps(voxel_surface surface); resource_ptr<SoundEffect> get_placebreak(voxel_surface surface); -} // namespace voxel_sounds +} // namespace world::voxel_sounds #endif // CLIENT_VOXEL_SOUNDS_HH diff --git a/src/game/server/CMakeLists.txt b/game/server/CMakeLists.txt index bd0eff3..a4b5fd9 100644 --- a/src/game/server/CMakeLists.txt +++ b/game/server/CMakeLists.txt @@ -5,10 +5,7 @@ add_executable(vserver "${CMAKE_CURRENT_LIST_DIR}/game.hh" "${CMAKE_CURRENT_LIST_DIR}/globals.cc" "${CMAKE_CURRENT_LIST_DIR}/globals.hh" - "${CMAKE_CURRENT_LIST_DIR}/inhabited.hh" "${CMAKE_CURRENT_LIST_DIR}/main.cc" - "${CMAKE_CURRENT_LIST_DIR}/overworld.cc" - "${CMAKE_CURRENT_LIST_DIR}/overworld.hh" "${CMAKE_CURRENT_LIST_DIR}/pch.hh" "${CMAKE_CURRENT_LIST_DIR}/receive.cc" "${CMAKE_CURRENT_LIST_DIR}/receive.hh" @@ -16,21 +13,16 @@ add_executable(vserver "${CMAKE_CURRENT_LIST_DIR}/sessions.hh" "${CMAKE_CURRENT_LIST_DIR}/status.cc" "${CMAKE_CURRENT_LIST_DIR}/status.hh" - "${CMAKE_CURRENT_LIST_DIR}/universe.cc" - "${CMAKE_CURRENT_LIST_DIR}/universe.hh" - "${CMAKE_CURRENT_LIST_DIR}/unloader.cc" - "${CMAKE_CURRENT_LIST_DIR}/unloader.hh" "${CMAKE_CURRENT_LIST_DIR}/whitelist.cc" - "${CMAKE_CURRENT_LIST_DIR}/whitelist.hh" - "${CMAKE_CURRENT_LIST_DIR}/worldgen.cc" - "${CMAKE_CURRENT_LIST_DIR}/worldgen.hh") + "${CMAKE_CURRENT_LIST_DIR}/whitelist.hh") target_compile_features(vserver PUBLIC cxx_std_20) -target_include_directories(vserver PUBLIC "${DEPS_INCLUDE_DIR}") -target_include_directories(vserver PRIVATE "${PROJECT_SOURCE_DIR}/src") -target_include_directories(vserver PRIVATE "${PROJECT_SOURCE_DIR}/src/game") +target_include_directories(vserver PRIVATE "${PROJECT_SOURCE_DIR}") +target_include_directories(vserver PRIVATE "${PROJECT_SOURCE_DIR}/game") target_precompile_headers(vserver PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") target_link_libraries(vserver PUBLIC shared) +add_subdirectory(world) + if(WIN32) enable_language(RC) target_sources(vserver PRIVATE "${CMAKE_CURRENT_LIST_DIR}/vserver.rc") diff --git a/src/game/server/chat.cc b/game/server/chat.cc index 1634c59..1634c59 100644 --- a/src/game/server/chat.cc +++ b/game/server/chat.cc diff --git a/src/game/server/chat.hh b/game/server/chat.hh index 524ea78..524ea78 100644 --- a/src/game/server/chat.hh +++ b/game/server/chat.hh diff --git a/src/game/server/game.cc b/game/server/game.cc index cdf1459..231c87b 100644 --- a/src/game/server/game.cc +++ b/game/server/game.cc @@ -2,42 +2,46 @@ #include "server/game.hh" -#include "core/cmdline.hh" -#include "core/config.hh" -#include "core/constexpr.hh" -#include "core/crc64.hh" -#include "core/epoch.hh" - -#include "shared/collision.hh" -#include "shared/dimension.hh" +#include "core/config/number.hh" +#include "core/config/string.hh" +#include "core/io/cmdline.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" +#include "core/math/crc64.hh" +#include "core/utils/epoch.hh" + +#include "shared/entity/collision.hh" +#include "shared/entity/gravity.hh" +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/stasis.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + #include "shared/game_items.hh" #include "shared/game_voxels.hh" -#include "shared/gravity.hh" -#include "shared/head.hh" -#include "shared/player.hh" #include "shared/protocol.hh" #include "shared/splash.hh" -#include "shared/stasis.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" + +#include "server/world/universe.hh" +#include "server/world/unloader.hh" +#include "server/world/worldgen.hh" #include "server/chat.hh" #include "server/globals.hh" #include "server/receive.hh" #include "server/sessions.hh" #include "server/status.hh" -#include "server/universe.hh" -#include "server/unloader.hh" #include "server/whitelist.hh" -#include "server/worldgen.hh" -ConfigUnsigned server_game::view_distance(4U, 4U, 32U); +config::Unsigned server_game::view_distance(4U, 4U, 32U); std::uint64_t server_game::password_hash = UINT64_MAX; -static ConfigNumber<enet_uint16> listen_port(protocol::PORT, 1024U, UINT16_MAX); -static ConfigUnsigned status_peers(2U, 1U, 16U); -static ConfigString password_string(""); +static config::Number<enet_uint16> listen_port(protocol::PORT, 1024U, UINT16_MAX); +static config::Unsigned status_peers(2U, 1U, 16U); +static config::String password_string(""); void server_game::init(void) { @@ -57,15 +61,15 @@ void server_game::init(void) server_chat::init(); server_recieve::init(); - worldgen::init(); + world::worldgen::init(); - unloader::init(); - universe::init(); + world::unloader::init(); + world::universe::init(); } void server_game::init_late(void) { - server_game::password_hash = crc64::get(password_string.get()); + server_game::password_hash = math::crc64(password_string.get()); sessions::init_late(); @@ -88,8 +92,8 @@ void server_game::init_late(void) game_voxels::populate(); game_items::populate(); - unloader::init_late(); - universe::init_late(); + world::unloader::init_late(); + world::universe::init_late(); sessions::init_post_universe(); } @@ -108,18 +112,18 @@ void server_game::shutdown(void) enet_host_service(globals::server_host, nullptr, 500); enet_host_destroy(globals::server_host); - universe::shutdown(); + world::universe::shutdown(); } void server_game::fixed_update(void) { // FIXME: threading for(auto dimension : globals::dimensions) { - CollisionComponent::fixed_update(dimension.second); - VelocityComponent::fixed_update(dimension.second); - TransformComponent::fixed_update(dimension.second); - GravityComponent::fixed_update(dimension.second); - StasisComponent::fixed_update(dimension.second); + entity::Collision::fixed_update(dimension.second); + entity::Velocity::fixed_update(dimension.second); + entity::Transform::fixed_update(dimension.second); + entity::Gravity::fixed_update(dimension.second); + entity::Stasis::fixed_update(dimension.second); } } @@ -143,6 +147,6 @@ void server_game::fixed_update_late(void) // FIXME: threading for(auto dimension : globals::dimensions) { - unloader::fixed_update_late(dimension.second); + world::unloader::fixed_update_late(dimension.second); } } diff --git a/src/game/server/game.hh b/game/server/game.hh index 9ff21d9..2d40be7 100644 --- a/src/game/server/game.hh +++ b/game/server/game.hh @@ -2,11 +2,14 @@ #define SERVER_GAME_HH 1 #pragma once -class ConfigUnsigned; +namespace config +{ +class Unsigned; +} // namespace config namespace server_game { -extern ConfigUnsigned view_distance; +extern config::Unsigned view_distance; } // namespace server_game namespace server_game diff --git a/src/game/server/globals.cc b/game/server/globals.cc index 883588b..7d79e4d 100644 --- a/src/game/server/globals.cc +++ b/game/server/globals.cc @@ -2,11 +2,11 @@ #include "server/globals.hh" -#include "core/config.hh" +#include "core/io/config_map.hh" #include "shared/protocol.hh" -Config globals::server_config; +io::ConfigMap globals::server_config; ENetHost* globals::server_host; @@ -14,5 +14,5 @@ bool globals::is_running; unsigned int globals::tickrate; std::uint64_t globals::tickrate_dt; -Dimension* globals::spawn_dimension; -std::unordered_map<std::string, Dimension*> globals::dimensions; +world::Dimension* globals::spawn_dimension; +std::unordered_map<std::string, world::Dimension*> globals::dimensions; diff --git a/src/game/server/globals.hh b/game/server/globals.hh index 3ae2226..fdfc359 100644 --- a/src/game/server/globals.hh +++ b/game/server/globals.hh @@ -4,13 +4,19 @@ #include "shared/globals.hh" -class Config; +namespace io +{ +class ConfigMap; +} // namespace io +namespace world +{ class Dimension; +} // namespace world namespace globals { -extern Config server_config; +extern io::ConfigMap server_config; extern ENetHost* server_host; @@ -18,8 +24,8 @@ extern bool is_running; extern unsigned int tickrate; extern std::uint64_t tickrate_dt; -extern Dimension* spawn_dimension; -extern std::unordered_map<std::string, Dimension*> dimensions; +extern world::Dimension* spawn_dimension; +extern std::unordered_map<std::string, world::Dimension*> dimensions; } // namespace globals #endif // SERVER_GLOBALS_HH diff --git a/src/game/server/main.cc b/game/server/main.cc index 535982a..fd940e2 100644 --- a/src/game/server/main.cc +++ b/game/server/main.cc @@ -1,12 +1,14 @@ #include "server/pch.hh" -#include "core/binfile.hh" -#include "core/cmdline.hh" -#include "core/config.hh" -#include "core/constexpr.hh" -#include "core/epoch.hh" -#include "core/image.hh" -#include "core/resource.hh" +#include "core/config/number.hh" +#include "core/io/cmdline.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" +#include "core/resource/binfile.hh" +#include "core/resource/image.hh" +#include "core/resource/resource.hh" +#include "core/utils/epoch.hh" + #include "core/version.hh" #include "shared/game.hh" @@ -16,7 +18,7 @@ #include "server/game.hh" #include "server/globals.hh" -static ConfigUnsigned server_tickrate(protocol::TICKRATE, 10U, 300U); +static config::Unsigned server_tickrate(protocol::TICKRATE, 10U, 300U); static void on_termination_signal(int) { @@ -26,7 +28,7 @@ static void on_termination_signal(int) int main(int argc, char** argv) { - cmdline::create(argc, argv); + io::cmdline::create(argc, argv); shared_game::init(argc, argv); @@ -37,7 +39,7 @@ int main(int argc, char** argv) globals::fixed_frametime_us = 0; globals::fixed_framecount = 0; - globals::curtime = epoch::microseconds(); + globals::curtime = utils::unix_microseconds(); globals::is_running = true; @@ -60,7 +62,7 @@ int main(int argc, char** argv) std::uint64_t last_curtime = globals::curtime; while(globals::is_running) { - globals::curtime = epoch::microseconds(); + globals::curtime = utils::unix_microseconds(); globals::fixed_frametime_us = globals::curtime - last_curtime; globals::fixed_frametime = static_cast<float>(globals::fixed_frametime_us) / 1000000.0f; diff --git a/src/game/server/pch.hh b/game/server/pch.hh index 810a7e4..810a7e4 100644 --- a/src/game/server/pch.hh +++ b/game/server/pch.hh diff --git a/src/game/server/receive.cc b/game/server/receive.cc index 7674122..1945243 100644 --- a/src/game/server/receive.cc +++ b/game/server/receive.cc @@ -2,28 +2,30 @@ #include "server/receive.hh" -#include "core/config.hh" +#include "core/config/number.hh" + +#include "shared/entity/head.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/chunk_aabb.hh" +#include "shared/world/dimension.hh" -#include "shared/chunk_aabb.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/head.hh" #include "shared/protocol.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" + +#include "server/world/inhabited.hh" +#include "server/world/universe.hh" +#include "server/world/worldgen.hh" #include "server/game.hh" #include "server/globals.hh" -#include "server/inhabited.hh" #include "server/sessions.hh" -#include "server/universe.hh" -#include "server/worldgen.hh" static void on_entity_transform_packet(const protocol::EntityTransform& packet) { if(auto session = sessions::find(packet.peer)) { if(session->dimension && session->dimension->entities.valid(session->player_entity)) { - auto& component = session->dimension->entities.emplace_or_replace<TransformComponent>(session->player_entity); + auto& component = session->dimension->entities.emplace_or_replace<entity::Transform>(session->player_entity); component.angles = packet.angles; component.chunk = packet.chunk; component.local = packet.local; @@ -45,7 +47,7 @@ static void on_entity_velocity_packet(const protocol::EntityVelocity& packet) { if(auto session = sessions::find(packet.peer)) { if(session->dimension && session->dimension->entities.valid(session->player_entity)) { - auto& component = session->dimension->entities.emplace_or_replace<VelocityComponent>(session->player_entity); + auto& component = session->dimension->entities.emplace_or_replace<entity::Velocity>(session->player_entity); component.value = packet.value; protocol::EntityVelocity response; @@ -63,7 +65,7 @@ static void on_entity_head_packet(const protocol::EntityHead& packet) { if(auto session = sessions::find(packet.peer)) { if(session->dimension && session->dimension->entities.valid(session->player_entity)) { - auto& component = session->dimension->entities.emplace_or_replace<HeadComponent>(session->player_entity); + auto& component = session->dimension->entities.emplace_or_replace<entity::Head>(session->player_entity); component.angles = packet.angles; protocol::EntityHead response; @@ -85,7 +87,7 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet) auto lpos = coord::to_local(packet.vpos); auto index = coord::to_index(lpos); - if(worldgen::is_generating(session->dimension, cpos)) { + if(world::worldgen::is_generating(session->dimension, cpos)) { // The chunk is currently being generated; // ignore all requests from players to build there return; @@ -101,7 +103,7 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet) chunk->set_voxel(packet.voxel, index); - session->dimension->chunks.emplace_or_replace<InhabitedComponent>(chunk->get_entity()); + session->dimension->chunks.emplace_or_replace<world::Inhabited>(chunk->get_entity()); protocol::SetVoxel response; response.vpos = packet.vpos; @@ -122,19 +124,19 @@ static void on_request_chunk_packet(const protocol::RequestChunk& packet) return; } - if(auto transform = session->dimension->entities.try_get<TransformComponent>(session->player_entity)) { - ChunkAABB view_box; + if(auto transform = session->dimension->entities.try_get<entity::Transform>(session->player_entity)) { + world::ChunkAABB view_box; view_box.min = transform->chunk - static_cast<chunk_pos::value_type>(server_game::view_distance.get_value()); view_box.max = transform->chunk + static_cast<chunk_pos::value_type>(server_game::view_distance.get_value()); if(view_box.contains(packet.cpos)) { - if(auto chunk = universe::load_chunk(session->dimension, packet.cpos)) { + if(auto chunk = world::universe::load_chunk(session->dimension, packet.cpos)) { protocol::ChunkVoxels response; response.chunk = packet.cpos; response.voxels = chunk->get_voxels(); protocol::send(packet.peer, protocol::encode(response)); } else { - worldgen::request_chunk(session, packet.cpos); + world::worldgen::request_chunk(session, packet.cpos); } } } diff --git a/src/game/server/receive.hh b/game/server/receive.hh index 17333d0..17333d0 100644 --- a/src/game/server/receive.hh +++ b/game/server/receive.hh diff --git a/src/game/server/sessions.cc b/game/server/sessions.cc index 3024939..df98c75 100644 --- a/src/game/server/sessions.cc +++ b/game/server/sessions.cc @@ -2,22 +2,25 @@ #include "server/sessions.hh" -#include "core/config.hh" -#include "core/constexpr.hh" -#include "core/crc64.hh" -#include "core/strtools.hh" +#include "core/config/boolean.hh" +#include "core/config/number.hh" +#include "core/io/config_map.hh" +#include "core/math/constexpr.hh" +#include "core/math/crc64.hh" +#include "core/utils/string.hh" + +#include "shared/entity/factory.hh" +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" +#include "shared/world/item_registry.hh" +#include "shared/world/voxel_registry.hh" -#include "shared/chunk.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/factory.hh" -#include "shared/head.hh" -#include "shared/item_registry.hh" -#include "shared/player.hh" #include "shared/protocol.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "shared/voxel_registry.hh" #include "server/game.hh" #include "server/globals.hh" @@ -25,14 +28,14 @@ class DimensionListener final { public: - explicit DimensionListener(Dimension* dimension); + explicit DimensionListener(world::Dimension* dimension); void on_destroy_entity(const entt::registry& registry, entt::entity entity); private: - Dimension* dimension; + world::Dimension* dimension; }; -ConfigUnsigned sessions::max_players(8U, 1U, 128U); +config::Unsigned sessions::max_players(8U, 1U, 128U); unsigned int sessions::num_players = 0U; static emhash8::HashMap<std::string, Session*> username_map; @@ -58,14 +61,14 @@ static void on_login_request_packet(const protocol::LoginRequest& packet) // FIXME: calculate voxel registry checksum ahead of time // instead of figuring it out every time a new player connects - if(packet.voxel_registry_checksum != voxel_registry::calcualte_checksum()) { + if(packet.voxel_registry_checksum != world::voxel_registry::calcualte_checksum()) { protocol::Disconnect response; response.reason = "protocol.voxel_registry_checksum"; protocol::send(packet.peer, protocol::encode(response)); return; } - if(packet.item_registry_checksum != item_registry::calcualte_checksum()) { + if(packet.item_registry_checksum != world::item_registry::calcualte_checksum()) { protocol::Disconnect response; response.reason = "protocol.item_registry_checksum"; protocol::send(packet.peer, protocol::encode(response)); @@ -123,14 +126,14 @@ static void on_login_request_packet(const protocol::LoginRequest& packet) // anything here and just straight up spawing the player and await them // to receive all the chunks and entites they feel like requesting for(auto entity : globals::spawn_dimension->entities.view<entt::entity>()) { - if(const auto head = globals::spawn_dimension->entities.try_get<HeadComponent>(entity)) { + if(const auto head = globals::spawn_dimension->entities.try_get<entity::Head>(entity)) { protocol::EntityHead head_packet; head_packet.entity = entity; head_packet.angles = head->angles; protocol::send(session->peer, protocol::encode(head_packet)); } - if(const auto transform = globals::spawn_dimension->entities.try_get<TransformComponent>(entity)) { + if(const auto transform = globals::spawn_dimension->entities.try_get<entity::Transform>(entity)) { protocol::EntityTransform transform_packet; transform_packet.entity = entity; transform_packet.angles = transform->angles; @@ -139,14 +142,14 @@ static void on_login_request_packet(const protocol::LoginRequest& packet) protocol::send(session->peer, protocol::encode(transform_packet)); } - if(const auto velocity = globals::spawn_dimension->entities.try_get<VelocityComponent>(entity)) { + if(const auto velocity = globals::spawn_dimension->entities.try_get<entity::Velocity>(entity)) { protocol::EntityVelocity velocity_packet; velocity_packet.entity = entity; velocity_packet.value = velocity->value; protocol::send(session->peer, protocol::encode(velocity_packet)); } - if(globals::spawn_dimension->entities.all_of<PlayerComponent>(entity)) { + if(globals::spawn_dimension->entities.all_of<entity::Player>(entity)) { protocol::EntityPlayer player_packet; player_packet.entity = entity; protocol::send(session->peer, protocol::encode(player_packet)); @@ -155,11 +158,11 @@ static void on_login_request_packet(const protocol::LoginRequest& packet) session->dimension = globals::spawn_dimension; session->player_entity = globals::spawn_dimension->entities.create(); - shared_factory::create_player(globals::spawn_dimension, session->player_entity); + entity::shared::create_player(globals::spawn_dimension, session->player_entity); - const auto& head = globals::spawn_dimension->entities.get<HeadComponent>(session->player_entity); - const auto& transform = globals::spawn_dimension->entities.get<TransformComponent>(session->player_entity); - const auto& velocity = globals::spawn_dimension->entities.get<VelocityComponent>(session->player_entity); + const auto& head = globals::spawn_dimension->entities.get<entity::Head>(session->player_entity); + const auto& transform = globals::spawn_dimension->entities.get<entity::Transform>(session->player_entity); + const auto& velocity = globals::spawn_dimension->entities.get<entity::Velocity>(session->player_entity); protocol::EntityHead head_packet; head_packet.entity = session->player_entity; @@ -229,7 +232,7 @@ static void on_disconnect_packet(const protocol::Disconnect& packet) // NOTE: [sessions] is a good place for this since [receive] // handles entity data sent by players and [sessions] handles // everything else network related that is not player movement -static void on_voxel_set(const VoxelSetEvent& event) +static void on_voxel_set(const world::VoxelSetEvent& event) { protocol::SetVoxel packet; packet.vpos = coord::to_voxel(event.cpos, event.lpos); @@ -238,7 +241,7 @@ static void on_voxel_set(const VoxelSetEvent& event) protocol::broadcast(globals::server_host, protocol::encode(packet)); } -DimensionListener::DimensionListener(Dimension* dimension) +DimensionListener::DimensionListener(world::Dimension* dimension) { this->dimension = dimension; } @@ -257,7 +260,7 @@ void sessions::init(void) globals::dispatcher.sink<protocol::LoginRequest>().connect<&on_login_request_packet>(); globals::dispatcher.sink<protocol::Disconnect>().connect<&on_disconnect_packet>(); - globals::dispatcher.sink<VoxelSetEvent>().connect<&on_voxel_set>(); + globals::dispatcher.sink<world::VoxelSetEvent>().connect<&on_voxel_set>(); } void sessions::init_late(void) @@ -297,7 +300,7 @@ Session* sessions::create(ENetPeer* peer, const char* client_username) { for(unsigned int i = 0U; i < sessions::max_players.get_value(); ++i) { if(!sessions_vector[i].peer) { - std::uint64_t client_identity = crc64::get(client_username); + std::uint64_t client_identity = math::crc64(client_username); sessions_vector[i].client_index = i; sessions_vector[i].client_identity = client_identity; @@ -387,7 +390,7 @@ void sessions::destroy(Session* session) } } -void sessions::broadcast(const Dimension* dimension, ENetPacket* packet) +void sessions::broadcast(const world::Dimension* dimension, ENetPacket* packet) { for(const auto& session : sessions_vector) { if(session.peer && (session.dimension == dimension)) { @@ -396,7 +399,7 @@ void sessions::broadcast(const Dimension* dimension, ENetPacket* packet) } } -void sessions::broadcast(const Dimension* dimension, ENetPacket* packet, ENetPeer* except) +void sessions::broadcast(const world::Dimension* dimension, ENetPacket* packet, ENetPeer* except) { for(const auto& session : sessions_vector) { if(session.peer && (session.peer != except)) { diff --git a/src/game/server/sessions.hh b/game/server/sessions.hh index 2eee256..ee5b22e 100644 --- a/src/game/server/sessions.hh +++ b/game/server/sessions.hh @@ -2,22 +2,28 @@ #define SERVER_SESSIONS_HH 1 #pragma once +namespace world +{ class Dimension; +} // namespace world -class ConfigUnsigned; +namespace config +{ +class Unsigned; +} // namespace config struct Session final { std::uint16_t client_index; std::uint64_t client_identity; std::string client_username; entt::entity player_entity; - Dimension* dimension; + world::Dimension* dimension; ENetPeer* peer; }; namespace sessions { -extern ConfigUnsigned max_players; +extern config::Unsigned max_players; extern unsigned int num_players; } // namespace sessions @@ -41,8 +47,8 @@ void destroy(Session* session); namespace sessions { -void broadcast(const Dimension* dimension, ENetPacket* packet); -void broadcast(const Dimension* dimension, ENetPacket* packet, ENetPeer* except); +void broadcast(const world::Dimension* dimension, ENetPacket* packet); +void broadcast(const world::Dimension* dimension, ENetPacket* packet, ENetPeer* except); } // namespace sessions namespace sessions diff --git a/src/game/server/status.cc b/game/server/status.cc index 6b64719..ed73b23 100644 --- a/src/game/server/status.cc +++ b/game/server/status.cc @@ -2,7 +2,7 @@ #include "server/status.hh" -#include "core/config.hh" +#include "core/config/number.hh" #include "shared/protocol.hh" #include "shared/splash.hh" diff --git a/src/game/server/status.hh b/game/server/status.hh index 218eab0..218eab0 100644 --- a/src/game/server/status.hh +++ b/game/server/status.hh diff --git a/src/game/server/vserver.ico b/game/server/vserver.ico Binary files differindex 02ff006..02ff006 100644 --- a/src/game/server/vserver.ico +++ b/game/server/vserver.ico diff --git a/src/game/server/vserver.rc b/game/server/vserver.rc index b6828bf..b6828bf 100644 --- a/src/game/server/vserver.rc +++ b/game/server/vserver.rc diff --git a/src/game/server/whitelist.cc b/game/server/whitelist.cc index 69ac5a8..2da1bf4 100644 --- a/src/game/server/whitelist.cc +++ b/game/server/whitelist.cc @@ -2,9 +2,11 @@ #include "server/whitelist.hh" -#include "core/config.hh" -#include "core/crc64.hh" -#include "core/strtools.hh" +#include "core/config/boolean.hh" +#include "core/config/string.hh" +#include "core/io/config_map.hh" +#include "core/math/crc64.hh" +#include "core/utils/string.hh" #include "server/game.hh" #include "server/globals.hh" @@ -12,8 +14,8 @@ constexpr static const char* DEFAULT_FILENAME = "whitelist.txt"; constexpr static char SEPARATOR_CHAR = ':'; -ConfigBoolean whitelist::enabled(false); -ConfigString whitelist::filename(DEFAULT_FILENAME); +config::Boolean whitelist::enabled(false); +config::String whitelist::filename(DEFAULT_FILENAME); static emhash8::HashMap<std::string, std::uint64_t> whitelist_map; @@ -34,7 +36,7 @@ void whitelist::init_late(void) return; } - if(strtools::is_whitespace(whitelist::filename.get())) { + if(utils::is_whitespace(whitelist::filename.get())) { spdlog::warn("whitelist: enabled but filename is empty, using default ({})", DEFAULT_FILENAME); whitelist::filename.set(DEFAULT_FILENAME); } @@ -65,7 +67,7 @@ void whitelist::init_late(void) } else { const auto username = line.substr(0, location); const auto password = line.substr(location + 1); - whitelist_map[username] = crc64::get(password); + whitelist_map[username] = math::crc64(password); } } diff --git a/src/game/server/whitelist.hh b/game/server/whitelist.hh index b9008aa..8f909b9 100644 --- a/src/game/server/whitelist.hh +++ b/game/server/whitelist.hh @@ -2,13 +2,16 @@ #define SERVER_WHITELIST_HH 1 #pragma once -class ConfigBoolean; -class ConfigString; +namespace config +{ +class Boolean; +class String; +} // namespace config namespace whitelist { -extern ConfigBoolean enabled; -extern ConfigString filename; +extern config::Boolean enabled; +extern config::String filename; } // namespace whitelist namespace whitelist diff --git a/game/server/world/CMakeLists.txt b/game/server/world/CMakeLists.txt new file mode 100644 index 0000000..e8fd4be --- /dev/null +++ b/game/server/world/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(vserver PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/inhabited.hh" + "${CMAKE_CURRENT_LIST_DIR}/overworld.cc" + "${CMAKE_CURRENT_LIST_DIR}/overworld.hh" + "${CMAKE_CURRENT_LIST_DIR}/universe.cc" + "${CMAKE_CURRENT_LIST_DIR}/universe.hh" + "${CMAKE_CURRENT_LIST_DIR}/unloader.cc" + "${CMAKE_CURRENT_LIST_DIR}/unloader.hh" + "${CMAKE_CURRENT_LIST_DIR}/worldgen.cc" + "${CMAKE_CURRENT_LIST_DIR}/worldgen.hh") diff --git a/src/game/server/inhabited.hh b/game/server/world/inhabited.hh index 982fe5d..bbb7a03 100644 --- a/src/game/server/inhabited.hh +++ b/game/server/world/inhabited.hh @@ -2,6 +2,9 @@ #define SERVER_INHABITED_HH 1 #pragma once -struct InhabitedComponent final {}; +namespace world +{ +struct Inhabited final {}; +} // namespace world #endif // SERVER_INHABITED_HH diff --git a/src/game/server/overworld.cc b/game/server/world/overworld.cc index a280e06..3949b77 100644 --- a/src/game/server/overworld.cc +++ b/game/server/world/overworld.cc @@ -1,18 +1,19 @@ #include "server/pch.hh" -#include "server/overworld.hh" +#include "server/world/overworld.hh" -#include "core/vectors.hh" +#include "core/math/vectors.hh" + +#include "shared/world/voxel_storage.hh" #include "shared/coord.hh" #include "shared/game_voxels.hh" -#include "shared/voxel_storage.hh" // FIXME: load these from a file -static void compute_tree_feature(unsigned int height, Feature& feature, voxel_id log_voxel, voxel_id leaves_voxel) +static void compute_tree_feature(unsigned int height, world::Feature& feature, voxel_id log_voxel, voxel_id leaves_voxel) { // Ensure the tree height is too small - height = vx::max<unsigned int>(height, 4U); + height = math::max<unsigned int>(height, 4U); // Put down a single piece of dirt feature.push_back({ voxel_pos(0, -1, 0), game_voxels::dirt, true }); @@ -69,7 +70,7 @@ static void compute_tree_feature(unsigned int height, Feature& feature, voxel_id } } -Overworld::Overworld(const char* name) : Dimension(name, -30.0f) +world::Overworld::Overworld(const char* name) : Dimension(name, -30.0f) { m_bottommost_chunk.set_limits(-64, -4); m_terrain_variation.set_limits(16, 256); @@ -80,7 +81,7 @@ Overworld::Overworld(const char* name) : Dimension(name, -30.0f) compute_tree_feature(8U, m_feat_tree[3], game_voxels::oak_log, game_voxels::oak_leaves); } -void Overworld::init(Config& config) +void world::Overworld::init(io::ConfigMap& config) { m_terrain_variation.set_value(64); m_bottommost_chunk.set_value(-4); @@ -89,7 +90,7 @@ void Overworld::init(Config& config) config.add_value("overworld.bottommost_chunk", m_bottommost_chunk); } -void Overworld::init_late(std::uint64_t global_seed) +void world::Overworld::init_late(std::uint64_t global_seed) { std::mt19937 twister(global_seed); @@ -127,7 +128,7 @@ void Overworld::init_late(std::uint64_t global_seed) m_metamap.clear(); } -bool Overworld::generate(const chunk_pos& cpos, VoxelStorage& voxels) +bool world::Overworld::generate(const chunk_pos& cpos, VoxelStorage& voxels) { if(cpos.y <= m_bottommost_chunk.get_value()) { // If the player asks the generator @@ -158,14 +159,14 @@ bool Overworld::generate(const chunk_pos& cpos, VoxelStorage& voxels) return true; } -bool Overworld::is_inside_cave(const voxel_pos& vpos) +bool world::Overworld::is_inside_cave(const voxel_pos& vpos) { auto noise_a = fnlGetNoise3D(&m_fnl_caves_a, vpos.x, vpos.y * 2.0f, vpos.z); auto noise_b = fnlGetNoise3D(&m_fnl_caves_b, vpos.x, vpos.y * 2.0f, vpos.z); return (noise_a > 0.95f) && (noise_b > 0.85f); } -bool Overworld::is_inside_terrain(const voxel_pos& vpos) +bool world::Overworld::is_inside_terrain(const voxel_pos& vpos) { auto variation_noise = fnlGetNoise3D(&m_fnl_terrain, vpos.x, vpos.y, vpos.z); auto variation = m_terrain_variation.get_value() * (1.0f - (variation_noise * variation_noise)); @@ -173,7 +174,7 @@ bool Overworld::is_inside_terrain(const voxel_pos& vpos) return noise > 0.0f; } -const Overworld_Metadata& Overworld::get_or_create_metadata(const chunk_pos_xz& cpos) +const world::Overworld_Metadata& world::Overworld::get_or_create_metadata(const chunk_pos_xz& cpos) { auto it = m_metamap.find(cpos); @@ -214,14 +215,14 @@ const Overworld_Metadata& Overworld::get_or_create_metadata(const chunk_pos_xz& } auto nvdi_value = 0.5f + 0.5f * fnlGetNoise2D(&m_fnl_nvdi, cpos.x, cpos.y); - auto tree_density = (nvdi_value >= 0.33f) ? vx::floor<unsigned int>(nvdi_value * 4.0f) : 0U; + auto tree_density = (nvdi_value >= 0.33f) ? math::floor<unsigned int>(nvdi_value * 4.0f) : 0U; for(unsigned int i = 0U; i < tree_density; ++i) { auto lpos = local_pos((twister() % CHUNK_SIZE), (twister() % OW_NUM_TREES), (twister() % CHUNK_SIZE)); auto is_unique = true; for(const auto& check_lpos : metadata.trees) { - if(vx::distance2(check_lpos, lpos) <= 9) { + if(math::distance2(check_lpos, lpos) <= 9) { is_unique = false; break; } @@ -235,7 +236,7 @@ const Overworld_Metadata& Overworld::get_or_create_metadata(const chunk_pos_xz& return metadata; } -void Overworld::generate_terrain(const chunk_pos& cpos, VoxelStorage& voxels) +void world::Overworld::generate_terrain(const chunk_pos& cpos, VoxelStorage& voxels) { auto& metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); auto variation = m_terrain_variation.get_value(); @@ -261,7 +262,7 @@ void Overworld::generate_terrain(const chunk_pos& cpos, VoxelStorage& voxels) } } -void Overworld::generate_surface(const chunk_pos& cpos, VoxelStorage& voxels) +void world::Overworld::generate_surface(const chunk_pos& cpos, VoxelStorage& voxels) { auto& metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); auto variation = m_terrain_variation.get_value(); @@ -314,7 +315,7 @@ void Overworld::generate_surface(const chunk_pos& cpos, VoxelStorage& voxels) } } -void Overworld::generate_caves(const chunk_pos& cpos, VoxelStorage& voxels) +void world::Overworld::generate_caves(const chunk_pos& cpos, VoxelStorage& voxels) { auto& metadata = get_or_create_metadata(chunk_pos_xz(cpos.x, cpos.z)); auto variation = m_terrain_variation.get_value(); @@ -336,7 +337,7 @@ void Overworld::generate_caves(const chunk_pos& cpos, VoxelStorage& voxels) } } -void Overworld::generate_features(const chunk_pos& cpos, VoxelStorage& voxels) +void world::Overworld::generate_features(const chunk_pos& cpos, VoxelStorage& voxels) { const chunk_pos_xz tree_chunks[] = { chunk_pos_xz(cpos.x - 0, cpos.z - 1), @@ -350,7 +351,7 @@ void Overworld::generate_features(const chunk_pos& cpos, VoxelStorage& voxels) chunk_pos_xz(cpos.x + 1, cpos.z + 1), }; - for(unsigned int i = 0U; i < vx::array_size(tree_chunks); ++i) { + for(unsigned int i = 0U; i < math::array_size(tree_chunks); ++i) { const auto& cpos_xz = tree_chunks[i]; const auto& metadata = get_or_create_metadata(cpos_xz); diff --git a/src/game/server/overworld.hh b/game/server/world/overworld.hh index fa3eb42..4141105 100644 --- a/src/game/server/overworld.hh +++ b/game/server/world/overworld.hh @@ -2,27 +2,34 @@ #define SERVER_OVERWORLD_HH 1 #pragma once -#include "core/config.hh" +#include "core/config/number.hh" +#include "core/io/config_map.hh" + +#include "shared/world/dimension.hh" +#include "shared/world/feature.hh" #include "shared/const.hh" -#include "shared/dimension.hh" -#include "shared/feature.hh" constexpr static unsigned int OW_NUM_TREES = 4U; +namespace world +{ struct Overworld_Metadata final { - dimension_entropy_map entropy; - dimension_height_map heightmap; + world::dimension_entropy_map entropy; + world::dimension_height_map heightmap; std::vector<local_pos> trees; }; +} // namespace world +namespace world +{ class Overworld final : public Dimension { public: explicit Overworld(const char* name); virtual ~Overworld(void) = default; public: - virtual void init(Config& config) override; + virtual void init(io::ConfigMap& config) override; virtual void init_late(std::uint64_t global_seed) override; virtual bool generate(const chunk_pos& cpos, VoxelStorage& voxels) override; @@ -40,8 +47,8 @@ private: void generate_features(const chunk_pos& cpos, VoxelStorage& voxels); private: - ConfigInt m_terrain_variation; - ConfigInt m_bottommost_chunk; + config::Int m_terrain_variation; + config::Int m_bottommost_chunk; private: emhash8::HashMap<chunk_pos_xz, Overworld_Metadata> m_metamap; @@ -59,5 +66,6 @@ private: private: std::mutex m_mutex; }; +} // namespace world #endif // SERVER_OVERWORLD_HH diff --git a/src/game/server/universe.cc b/game/server/world/universe.cc index 91d8469..9b031ca 100644 --- a/src/game/server/universe.cc +++ b/game/server/world/universe.cc @@ -1,32 +1,35 @@ #include "server/pch.hh" -#include "server/universe.hh" +#include "server/world/universe.hh" -#include "core/buffer.hh" -#include "core/config.hh" -#include "core/epoch.hh" +#include "core/config/number.hh" +#include "core/config/string.hh" +#include "core/io/buffer.hh" +#include "core/io/config_map.hh" +#include "core/utils/epoch.hh" -#include "shared/chunk.hh" -#include "shared/dimension.hh" +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" + +#include "server/world/inhabited.hh" +#include "server/world/overworld.hh" #include "server/globals.hh" -#include "server/inhabited.hh" -#include "server/overworld.hh" struct DimensionMetadata final { std::string config_path; std::string zvox_dir; - Config config; + io::ConfigMap config; }; -static ConfigString universe_name("save"); +static config::String universe_name("save"); -static Config universe_config; -static ConfigUnsigned64 universe_config_seed; -static ConfigString universe_spawn_dimension("world"); +static io::ConfigMap universe_config; +static config::Unsigned64 universe_config_seed; +static config::String universe_spawn_dimension("world"); static std::string universe_config_path; -static std::unordered_map<Dimension*, DimensionMetadata*> metadata_map; +static std::unordered_map<world::Dimension*, DimensionMetadata*> metadata_map; static std::string make_chunk_filename(const DimensionMetadata* metadata, const chunk_pos& cpos) { @@ -36,7 +39,7 @@ static std::string make_chunk_filename(const DimensionMetadata* metadata, const return std::format("{}/{:08X}-{:08X}-{:08X}.zvox", metadata->zvox_dir, unsigned_x, unsigned_y, unsigned_z); } -static void add_new_dimension(Dimension* dimension) +static void add_new_dimension(world::Dimension* dimension) { if(globals::dimensions.count(dimension->get_name())) { spdlog::critical("universe: dimension named {} already exists", dimension->get_name()); @@ -70,11 +73,12 @@ static void add_new_dimension(Dimension* dimension) dimension->init_late(universe_config_seed.get_value()); } -static void internal_save_chunk(const DimensionMetadata* metadata, const Dimension* dimension, const chunk_pos& cpos, const Chunk* chunk) +static void internal_save_chunk( + const DimensionMetadata* metadata, const world::Dimension* dimension, const chunk_pos& cpos, const world::Chunk* chunk) { auto path = make_chunk_filename(metadata, cpos); - WriteBuffer buffer; + io::WriteBuffer buffer; chunk->get_voxels().serialize(buffer); if(auto file = buffer.to_file(path.c_str())) { @@ -83,11 +87,11 @@ static void internal_save_chunk(const DimensionMetadata* metadata, const Dimensi } } -void universe::init(void) +void world::universe::init(void) { // If the world is newly created, the seed will // be chosed based on the current system's view on UNIX time - universe_config_seed.set_value(epoch::microseconds()); + universe_config_seed.set_value(utils::unix_microseconds()); // We're going to read files from directory named with // the value of this config value. Since config is also @@ -98,7 +102,7 @@ void universe::init(void) universe_config.add_value("spawn_dimension", universe_spawn_dimension); } -void universe::init_late(void) +void world::universe::init_late(void) { const auto universe_dir = std::string(universe_name.get()); @@ -128,7 +132,7 @@ void universe::init_late(void) globals::spawn_dimension = spawn_dimension->second; } -void universe::shutdown(void) +void world::universe::shutdown(void) { for(const auto metadata : metadata_map) { metadata.second->config.save_file(metadata.second->config_path.c_str()); @@ -138,7 +142,7 @@ void universe::shutdown(void) metadata_map.clear(); for(const auto dimension : globals::dimensions) { - universe::save_all_chunks(dimension.second); + world::universe::save_all_chunks(dimension.second); delete dimension.second; } @@ -148,7 +152,7 @@ void universe::shutdown(void) universe_config.save_file(universe_config_path.c_str()); } -Chunk* universe::load_chunk(Dimension* dimension, const chunk_pos& cpos) +world::Chunk* world::universe::load_chunk(Dimension* dimension, const chunk_pos& cpos) { if(auto chunk = dimension->find_chunk(cpos)) { // Just return the existing chunk which is @@ -166,7 +170,7 @@ Chunk* universe::load_chunk(Dimension* dimension, const chunk_pos& cpos) if(auto file = PHYSFS_openRead(make_chunk_filename(metadata->second, cpos).c_str())) { VoxelStorage voxels; - ReadBuffer buffer(file); + io::ReadBuffer buffer(file); voxels.deserialize(buffer); PHYSFS_close(file); @@ -175,7 +179,7 @@ Chunk* universe::load_chunk(Dimension* dimension, const chunk_pos& cpos) chunk->set_voxels(voxels); // Make sure we're going to save it later - dimension->chunks.emplace_or_replace<InhabitedComponent>(chunk->get_entity()); + dimension->chunks.emplace_or_replace<Inhabited>(chunk->get_entity()); return chunk; } @@ -183,7 +187,7 @@ Chunk* universe::load_chunk(Dimension* dimension, const chunk_pos& cpos) return nullptr; } -void universe::save_chunk(Dimension* dimension, const chunk_pos& cpos) +void world::universe::save_chunk(Dimension* dimension, const chunk_pos& cpos) { auto metadata = metadata_map.find(dimension); @@ -198,9 +202,9 @@ void universe::save_chunk(Dimension* dimension, const chunk_pos& cpos) } } -void universe::save_all_chunks(Dimension* dimension) +void world::universe::save_all_chunks(Dimension* dimension) { - auto group = dimension->chunks.group(entt::get<ChunkComponent, InhabitedComponent>); + auto group = dimension->chunks.group(entt::get<ChunkComponent, Inhabited>); auto metadata = metadata_map.find(dimension); if(metadata == metadata_map.cend()) { diff --git a/src/game/server/universe.hh b/game/server/world/universe.hh index 0313437..164ff01 100644 --- a/src/game/server/universe.hh +++ b/game/server/world/universe.hh @@ -4,22 +4,26 @@ #include "shared/types.hh" +namespace world +{ class Chunk; class Dimension; +} // namespace world + class Session; -namespace universe +namespace world::universe { void init(void); void init_late(void); void shutdown(void); -} // namespace universe +} // namespace world::universe -namespace universe +namespace world::universe { Chunk* load_chunk(Dimension* dimension, const chunk_pos& cpos); void save_chunk(Dimension* dimension, const chunk_pos& cpos); void save_all_chunks(Dimension* dimension); -} // namespace universe +} // namespace world::universe #endif // SERVER_UNIVERSE_HH diff --git a/game/server/world/unloader.cc b/game/server/world/unloader.cc new file mode 100644 index 0000000..3600ea2 --- /dev/null +++ b/game/server/world/unloader.cc @@ -0,0 +1,77 @@ +#include "server/pch.hh" + +#include "server/world/unloader.hh" + +#include "core/config/number.hh" + +#include "shared/entity/player.hh" +#include "shared/entity/transform.hh" +#include "shared/world/chunk.hh" +#include "shared/world/chunk_aabb.hh" +#include "shared/world/dimension.hh" + +#include "server/world/inhabited.hh" +#include "server/world/universe.hh" + +#include "server/game.hh" +#include "server/globals.hh" + +static void on_chunk_update(const world::ChunkUpdateEvent& event) +{ + event.dimension->chunks.emplace_or_replace<world::Inhabited>(event.chunk->get_entity()); +} + +static void on_voxel_set(const world::VoxelSetEvent& event) +{ + event.dimension->chunks.emplace_or_replace<world::Inhabited>(event.chunk->get_entity()); +} + +void world::unloader::init(void) +{ + globals::dispatcher.sink<world::ChunkUpdateEvent>().connect<&on_chunk_update>(); + globals::dispatcher.sink<world::VoxelSetEvent>().connect<&on_voxel_set>(); +} + +void world::unloader::init_late(void) +{ +} + +void world::unloader::fixed_update_late(Dimension* dimension) +{ + auto group = dimension->entities.group(entt::get<entity::Player, entity::Transform>); + auto boxes = std::vector<ChunkAABB>(); + + for(const auto [entity, transform] : group.each()) { + ChunkAABB aabb; + aabb.min = transform.chunk - static_cast<chunk_pos::value_type>(server_game::view_distance.get_value()); + aabb.max = transform.chunk + static_cast<chunk_pos::value_type>(server_game::view_distance.get_value()); + boxes.push_back(aabb); + } + + auto view = dimension->chunks.view<ChunkComponent>(); + auto chunk_in_view = false; + + for(const auto [entity, chunk] : view.each()) { + chunk_in_view = false; + + for(const auto& aabb : boxes) { + if(aabb.contains(chunk.cpos)) { + chunk_in_view = true; + break; + } + } + + if(chunk_in_view) { + // The chunk is within view box of at least + // a single player; we shouldn't unload it now + continue; + } + + if(dimension->chunks.any_of<Inhabited>(entity)) { + // Only store inhabited chunks on disk + world::universe::save_chunk(dimension, chunk.cpos); + } + + dimension->remove_chunk(entity); + } +} diff --git a/src/game/server/unloader.hh b/game/server/world/unloader.hh index 6648a1f..a995152 100644 --- a/src/game/server/unloader.hh +++ b/game/server/world/unloader.hh @@ -2,13 +2,16 @@ #define SERVER_UNLOADER_HH 1 #pragma once +namespace world +{ class Dimension; +} // namespace world -namespace unloader +namespace world::unloader { void init(void); void init_late(void); void fixed_update_late(Dimension* dimension); -} // namespace unloader +} // namespace world::unloader #endif // SERVER_UNLOADER_HH diff --git a/src/game/server/worldgen.cc b/game/server/world/worldgen.cc index 5c74d47..99e9a78 100644 --- a/src/game/server/worldgen.cc +++ b/game/server/world/worldgen.cc @@ -1,36 +1,38 @@ #include "server/pch.hh" -#include "server/worldgen.hh" +#include "server/world/worldgen.hh" -#include "core/cmdline.hh" +#include "core/io/cmdline.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" -#include "shared/chunk.hh" -#include "shared/dimension.hh" #include "shared/protocol.hh" #include "shared/threading.hh" +#include "server/world/inhabited.hh" + #include "server/globals.hh" -#include "server/inhabited.hh" #include "server/sessions.hh" static bool aggressive_caching; -static emhash8::HashMap<Dimension*, emhash8::HashMap<chunk_pos, std::unordered_set<Session*>>> active_tasks; +static emhash8::HashMap<world::Dimension*, emhash8::HashMap<chunk_pos, std::unordered_set<Session*>>> active_tasks; class WorldgenTask final : public Task { public: - explicit WorldgenTask(Dimension* dimension, const chunk_pos& cpos); + explicit WorldgenTask(world::Dimension* dimension, const chunk_pos& cpos); virtual ~WorldgenTask(void) = default; virtual void process(void) override; virtual void finalize(void) override; private: - Dimension* m_dimension; - VoxelStorage m_voxels; + world::Dimension* m_dimension; + world::VoxelStorage m_voxels; chunk_pos m_cpos; }; -WorldgenTask::WorldgenTask(Dimension* dimension, const chunk_pos& cpos) +WorldgenTask::WorldgenTask(world::Dimension* dimension, const chunk_pos& cpos) { m_dimension = dimension; m_voxels.fill(rand()); // trolling @@ -72,7 +74,7 @@ void WorldgenTask::finalize(void) // it so that it is saved regardles of whether it was // modified by players or not. This isn't particularly // good for server-side disk usage but it might improve performance - m_dimension->chunks.emplace<InhabitedComponent>(chunk->get_entity()); + m_dimension->chunks.emplace<world::Inhabited>(chunk->get_entity()); } protocol::ChunkVoxels response; @@ -99,12 +101,12 @@ void WorldgenTask::finalize(void) } } -void worldgen::init(void) +void world::worldgen::init(void) { - aggressive_caching = cmdline::contains("aggressive-caching"); + aggressive_caching = io::cmdline::contains("aggressive-caching"); } -bool worldgen::is_generating(Dimension* dimension, const chunk_pos& cpos) +bool world::worldgen::is_generating(Dimension* dimension, const chunk_pos& cpos) { auto dim_tasks = active_tasks.find(dimension); @@ -123,7 +125,7 @@ bool worldgen::is_generating(Dimension* dimension, const chunk_pos& cpos) return true; } -void worldgen::request_chunk(Session* session, const chunk_pos& cpos) +void world::worldgen::request_chunk(Session* session, const chunk_pos& cpos) { if(session->dimension) { auto dim_tasks = active_tasks.find(session->dimension); diff --git a/src/game/server/worldgen.hh b/game/server/world/worldgen.hh index e355ab9..db5b91f 100644 --- a/src/game/server/worldgen.hh +++ b/game/server/world/worldgen.hh @@ -4,18 +4,22 @@ #include "shared/types.hh" +namespace world +{ class Dimension; +} // namespace world + class Session; -namespace worldgen +namespace world::worldgen { void init(void); -} // namespace worldgen +} // namespace world::worldgen -namespace worldgen +namespace world::worldgen { bool is_generating(Dimension* dimension, const chunk_pos& cpos); void request_chunk(Session* session, const chunk_pos& cpos); -} // namespace worldgen +} // namespace world::worldgen #endif // SERVER_WORLDGEN_HH diff --git a/game/shared/CMakeLists.txt b/game/shared/CMakeLists.txt new file mode 100644 index 0000000..8a6fb57 --- /dev/null +++ b/game/shared/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library(shared STATIC + "${CMAKE_CURRENT_LIST_DIR}/const.hh" + "${CMAKE_CURRENT_LIST_DIR}/coord.hh" + "${CMAKE_CURRENT_LIST_DIR}/game.cc" + "${CMAKE_CURRENT_LIST_DIR}/game.hh" + "${CMAKE_CURRENT_LIST_DIR}/game_items.cc" + "${CMAKE_CURRENT_LIST_DIR}/game_items.hh" + "${CMAKE_CURRENT_LIST_DIR}/game_voxels.cc" + "${CMAKE_CURRENT_LIST_DIR}/game_voxels.hh" + "${CMAKE_CURRENT_LIST_DIR}/globals.cc" + "${CMAKE_CURRENT_LIST_DIR}/globals.hh" + "${CMAKE_CURRENT_LIST_DIR}/pch.hh" + "${CMAKE_CURRENT_LIST_DIR}/protocol.cc" + "${CMAKE_CURRENT_LIST_DIR}/protocol.hh" + "${CMAKE_CURRENT_LIST_DIR}/splash.cc" + "${CMAKE_CURRENT_LIST_DIR}/splash.hh" + "${CMAKE_CURRENT_LIST_DIR}/threading.cc" + "${CMAKE_CURRENT_LIST_DIR}/threading.hh" + "${CMAKE_CURRENT_LIST_DIR}/types.hh") +target_compile_features(shared PUBLIC cxx_std_20) +target_include_directories(shared PRIVATE "${PROJECT_SOURCE_DIR}") +target_include_directories(shared PRIVATE "${PROJECT_SOURCE_DIR}/game") +target_precompile_headers(shared PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") +target_link_libraries(shared PUBLIC core enet entt FNL miniz parson thread_pool) + +add_subdirectory(entity) +add_subdirectory(world) diff --git a/src/game/shared/const.hh b/game/shared/const.hh index 4061481..0755848 100644 --- a/src/game/shared/const.hh +++ b/game/shared/const.hh @@ -2,12 +2,12 @@ #define SHARED_CONST_HH 1 #pragma once -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" constexpr static unsigned int CHUNK_SIZE = 16; constexpr static unsigned int CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE; constexpr static unsigned int CHUNK_VOLUME = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; -constexpr static unsigned int CHUNK_BITSHIFT = vx::log2(CHUNK_SIZE); +constexpr static unsigned int CHUNK_BITSHIFT = math::log2(CHUNK_SIZE); template<typename T> constexpr static glm::vec<3, T> DIR_NORTH = glm::vec<3, T>(0, 0, +1); diff --git a/src/game/shared/coord.hh b/game/shared/coord.hh index 72d2909..18d45b5 100644 --- a/src/game/shared/coord.hh +++ b/game/shared/coord.hh @@ -53,9 +53,9 @@ inline constexpr chunk_pos coord::to_chunk(const voxel_pos& vpos) inline constexpr local_pos coord::to_local(const voxel_pos& vpos) { return local_pos { - static_cast<local_pos::value_type>(vx::mod_signed<voxel_pos::value_type>(vpos.x, CHUNK_SIZE)), - static_cast<local_pos::value_type>(vx::mod_signed<voxel_pos::value_type>(vpos.y, CHUNK_SIZE)), - static_cast<local_pos::value_type>(vx::mod_signed<voxel_pos::value_type>(vpos.z, CHUNK_SIZE)), + static_cast<local_pos::value_type>(math::mod_signed<voxel_pos::value_type>(vpos.x, CHUNK_SIZE)), + static_cast<local_pos::value_type>(math::mod_signed<voxel_pos::value_type>(vpos.y, CHUNK_SIZE)), + static_cast<local_pos::value_type>(math::mod_signed<voxel_pos::value_type>(vpos.z, CHUNK_SIZE)), }; } diff --git a/game/shared/entity/CMakeLists.txt b/game/shared/entity/CMakeLists.txt new file mode 100644 index 0000000..36e45b6 --- /dev/null +++ b/game/shared/entity/CMakeLists.txt @@ -0,0 +1,16 @@ +target_sources(shared PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/collision.cc" + "${CMAKE_CURRENT_LIST_DIR}/collision.hh" + "${CMAKE_CURRENT_LIST_DIR}/factory.cc" + "${CMAKE_CURRENT_LIST_DIR}/factory.hh" + "${CMAKE_CURRENT_LIST_DIR}/gravity.cc" + "${CMAKE_CURRENT_LIST_DIR}/gravity.hh" + "${CMAKE_CURRENT_LIST_DIR}/grounded.hh" + "${CMAKE_CURRENT_LIST_DIR}/head.hh" + "${CMAKE_CURRENT_LIST_DIR}/player.hh" + "${CMAKE_CURRENT_LIST_DIR}/stasis.cc" + "${CMAKE_CURRENT_LIST_DIR}/stasis.hh" + "${CMAKE_CURRENT_LIST_DIR}/transform.cc" + "${CMAKE_CURRENT_LIST_DIR}/transform.hh" + "${CMAKE_CURRENT_LIST_DIR}/velocity.cc" + "${CMAKE_CURRENT_LIST_DIR}/velocity.hh") diff --git a/src/game/shared/collision.cc b/game/shared/entity/collision.cc index dd51dd5..190f6e7 100644 --- a/src/game/shared/collision.cc +++ b/game/shared/entity/collision.cc @@ -1,40 +1,41 @@ #include "shared/pch.hh" -#include "shared/collision.hh" +#include "shared/entity/collision.hh" -#include "core/constexpr.hh" +#include "core/math/constexpr.hh" + +#include "shared/entity/gravity.hh" +#include "shared/entity/grounded.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" +#include "shared/world/voxel_registry.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" #include "shared/globals.hh" -#include "shared/gravity.hh" -#include "shared/grounded.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -#include "shared/voxel_registry.hh" - -static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& collision, TransformComponent& transform, - VelocityComponent& velocity, voxel_surface& touch_surface) + +static int vgrid_collide(const world::Dimension* dimension, int d, entity::Collision& collision, entity::Transform& transform, + entity::Velocity& velocity, world::voxel_surface& touch_surface) { const auto move = globals::fixed_frametime * velocity.value[d]; - const auto move_sign = vx::sign<int>(move); + const auto move_sign = math::sign<int>(move); const auto& ref_aabb = collision.aabb; const auto current_aabb = ref_aabb.push(transform.local); - auto next_aabb = AABB(current_aabb); + auto next_aabb = math::AABB(current_aabb); next_aabb.min[d] += move; next_aabb.max[d] += move; local_pos lpos_min; - lpos_min.x = vx::floor<local_pos::value_type>(next_aabb.min.x); - lpos_min.y = vx::floor<local_pos::value_type>(next_aabb.min.y); - lpos_min.z = vx::floor<local_pos::value_type>(next_aabb.min.z); + lpos_min.x = math::floor<local_pos::value_type>(next_aabb.min.x); + lpos_min.y = math::floor<local_pos::value_type>(next_aabb.min.y); + lpos_min.z = math::floor<local_pos::value_type>(next_aabb.min.z); local_pos lpos_max; - lpos_max.x = vx::ceil<local_pos::value_type>(next_aabb.max.x); - lpos_max.y = vx::ceil<local_pos::value_type>(next_aabb.max.y); - lpos_max.z = vx::ceil<local_pos::value_type>(next_aabb.max.z); + lpos_max.x = math::ceil<local_pos::value_type>(next_aabb.max.x); + lpos_max.y = math::ceil<local_pos::value_type>(next_aabb.max.y); + lpos_max.z = math::ceil<local_pos::value_type>(next_aabb.max.z); // Other axes const int u = (d + 1) % 3; @@ -54,10 +55,10 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& dmax = lpos_min[d]; } - voxel_touch latch_touch = voxel_touch::NOTHING; + world::voxel_touch latch_touch = world::voxel_touch::NOTHING; glm::fvec3 latch_values = glm::fvec3(0.0f, 0.0f, 0.0f); - voxel_surface latch_surface = voxel_surface::UNKNOWN; - AABB latch_vbox; + world::voxel_surface latch_surface = world::voxel_surface::UNKNOWN; + math::AABB latch_vbox; for(auto i = dmin; i != dmax; i += ddir) { for(auto j = lpos_min[u]; j < lpos_max[u]; ++j) @@ -68,7 +69,7 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& lpos[v] = k; const auto vpos = coord::to_voxel(transform.chunk, lpos); - const auto info = voxel_registry::find(dimension->get_voxel(vpos)); + const auto info = world::voxel_registry::find(dimension->get_voxel(vpos)); if(info == nullptr) { // Don't collide with something @@ -76,7 +77,7 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& continue; } - AABB vbox; + math::AABB vbox; vbox.min = glm::fvec3(lpos); vbox.max = glm::fvec3(lpos) + 1.0f; @@ -86,7 +87,7 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& continue; } - if(info->touch_type == voxel_touch::SOLID) { + if(info->touch_type == world::voxel_touch::SOLID) { // Solid touch type makes a collision // response whenever it is encountered velocity.value[d] = 0.0f; @@ -97,7 +98,7 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& // In case of other touch types, they // are latched and the last ever touch // type is then responded to - if(info->touch_type != voxel_touch::NOTHING) { + if(info->touch_type != world::voxel_touch::NOTHING) { latch_touch = info->touch_type; latch_values = info->touch_values; latch_surface = info->surface; @@ -107,9 +108,9 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& } } - if(latch_touch != voxel_touch::NOTHING) { - if(latch_touch == voxel_touch::BOUNCE) { - const auto move_distance = vx::abs(current_aabb.min[d] - next_aabb.min[d]); + if(latch_touch != world::voxel_touch::NOTHING) { + if(latch_touch == world::voxel_touch::BOUNCE) { + const auto move_distance = math::abs(current_aabb.min[d] - next_aabb.min[d]); const auto threshold = 2.0f * globals::fixed_frametime; if(move_distance > threshold) { @@ -123,7 +124,7 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& return move_sign; } - if(latch_touch == voxel_touch::SINK) { + if(latch_touch == world::voxel_touch::SINK) { velocity.value[d] *= latch_values[d]; touch_surface = latch_surface; return move_sign; @@ -133,7 +134,7 @@ static int vgrid_collide(const Dimension* dimension, int d, CollisionComponent& return 0; } -void CollisionComponent::fixed_update(Dimension* dimension) +void entity::Collision::fixed_update(world::Dimension* dimension) { // FIXME: this isn't particularly accurate considering // some voxels might be passable and some other voxels @@ -144,23 +145,23 @@ void CollisionComponent::fixed_update(Dimension* dimension) // we shouldn't treat all voxels as full cubes if we want // to support slabs, stairs and non-full liquid voxels in the future - auto group = dimension->entities.group<CollisionComponent>(entt::get<TransformComponent, VelocityComponent>); + auto group = dimension->entities.group<entity::Collision>(entt::get<entity::Transform, entity::Velocity>); for(auto [entity, collision, transform, velocity] : group.each()) { - auto surface = voxel_surface::UNKNOWN; + auto surface = world::voxel_surface::UNKNOWN; auto vertical_move = vgrid_collide(dimension, 1, collision, transform, velocity, surface); - if(dimension->entities.any_of<GravityComponent>(entity)) { - if(vertical_move == vx::sign<int>(dimension->get_gravity())) { - dimension->entities.emplace_or_replace<GroundedComponent>(entity, GroundedComponent { surface }); + if(dimension->entities.any_of<entity::Gravity>(entity)) { + if(vertical_move == math::sign<int>(dimension->get_gravity())) { + dimension->entities.emplace_or_replace<entity::Grounded>(entity, entity::Grounded { surface }); } else { - dimension->entities.remove<GroundedComponent>(entity); + dimension->entities.remove<entity::Grounded>(entity); } } else { // The entity cannot be grounded because the component // setup of said entity should not let it comprehend the // concept of resting on the ground (it flies around) - dimension->entities.remove<GroundedComponent>(entity); + dimension->entities.remove<entity::Grounded>(entity); } vgrid_collide(dimension, 0, collision, transform, velocity, surface); diff --git a/game/shared/entity/collision.hh b/game/shared/entity/collision.hh new file mode 100644 index 0000000..536a066 --- /dev/null +++ b/game/shared/entity/collision.hh @@ -0,0 +1,25 @@ +#ifndef SHARED_ENTITY_COLLISION_HH +#define SHARED_ENTITY_COLLISION_HH 1 +#pragma once + +#include "core/math/aabb.hh" + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity +{ +struct Collision final { + math::AABB aabb; + +public: + // NOTE: entity::Collision::fixed_update must be called + // before entity::Transform::fixed_update and entity::Velocity::fixed_update + // because both transform and velocity may be updated internally + static void fixed_update(world::Dimension* dimension); +}; +} // namespace entity + +#endif // SHARED_ENTITY_COLLISION_HH diff --git a/game/shared/entity/factory.cc b/game/shared/entity/factory.cc new file mode 100644 index 0000000..c98306a --- /dev/null +++ b/game/shared/entity/factory.cc @@ -0,0 +1,36 @@ +#include "shared/pch.hh" + +#include "shared/entity/factory.hh" + +#include "shared/entity/collision.hh" +#include "shared/entity/gravity.hh" +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + +#include "shared/globals.hh" + +void entity::shared::create_player(world::Dimension* dimension, entt::entity entity) +{ + spdlog::debug("factory[{}]: assigning player components to {}", dimension->get_name(), static_cast<std::uint64_t>(entity)); + + auto& collision = dimension->entities.emplace_or_replace<entity::Collision>(entity); + collision.aabb.min = glm::fvec3(-0.4f, -1.6f, -0.4f); + collision.aabb.max = glm::fvec3(+0.4f, +0.2f, +0.4f); + + auto& head = dimension->entities.emplace_or_replace<entity::Head>(entity); + head.angles = glm::fvec3(0.0f, 0.0f, 0.0f); + head.offset = glm::fvec3(0.0f, 0.0f, 0.0f); + + dimension->entities.emplace_or_replace<entity::Player>(entity); + + auto& transform = dimension->entities.emplace_or_replace<entity::Transform>(entity); + transform.chunk = chunk_pos(0, 2, 0); + transform.local = glm::fvec3(0.0f, 0.0f, 0.0f); + transform.angles = glm::fvec3(0.0f, 0.0f, 0.0f); + + auto& velocity = dimension->entities.emplace_or_replace<entity::Velocity>(entity); + velocity.value = glm::fvec3(0.0f, 0.0f, 0.0f); +} diff --git a/game/shared/entity/factory.hh b/game/shared/entity/factory.hh new file mode 100644 index 0000000..c0e0716 --- /dev/null +++ b/game/shared/entity/factory.hh @@ -0,0 +1,15 @@ +#ifndef SHARED_ENTITY_FACTORY_HH +#define SHARED_ENTITY_FACTORY_HH 1 +#pragma once + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity::shared +{ +void create_player(world::Dimension* dimension, entt::entity entity); +} // namespace entity::shared + +#endif // SHARED_ENTITY_FACTORY_HH diff --git a/game/shared/entity/gravity.cc b/game/shared/entity/gravity.cc new file mode 100644 index 0000000..f5dd145 --- /dev/null +++ b/game/shared/entity/gravity.cc @@ -0,0 +1,19 @@ +#include "shared/pch.hh" + +#include "shared/entity/gravity.hh" + +#include "shared/entity/stasis.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/dimension.hh" + +#include "shared/globals.hh" + +void entity::Gravity::fixed_update(world::Dimension* dimension) +{ + auto fixed_acceleration = globals::fixed_frametime * dimension->get_gravity(); + auto group = dimension->entities.group<entity::Gravity>(entt::get<entity::Velocity>, entt::exclude<entity::Stasis>); + + for(auto [entity, velocity] : group.each()) { + velocity.value.y += fixed_acceleration; + } +} diff --git a/game/shared/entity/gravity.hh b/game/shared/entity/gravity.hh new file mode 100644 index 0000000..064f92d --- /dev/null +++ b/game/shared/entity/gravity.hh @@ -0,0 +1,18 @@ +#ifndef SHARED_ENTITY_GRAVITY_HH +#define SHARED_ENTITY_GRAVITY_HH 1 +#pragma once + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity +{ +struct Gravity final { +public: + static void fixed_update(world::Dimension* dimension); +}; +} // namespace entity + +#endif // SHARED_ENTITY_GRAVITY_HH diff --git a/game/shared/entity/grounded.hh b/game/shared/entity/grounded.hh new file mode 100644 index 0000000..22340db --- /dev/null +++ b/game/shared/entity/grounded.hh @@ -0,0 +1,16 @@ +#ifndef SHARED_ENTITY_GROUNDED +#define SHARED_ENTITY_GROUNDED 1 +#pragma once + +#include "shared/world/voxel_registry.hh" + +namespace entity +{ +// Assigned to entities which are grounded +// according to the collision and gravity system +struct Grounded final { + world::voxel_surface surface; +}; +} // namespace entity + +#endif // SHARED_ENTITY_GROUNDED diff --git a/game/shared/entity/head.hh b/game/shared/entity/head.hh new file mode 100644 index 0000000..1437207 --- /dev/null +++ b/game/shared/entity/head.hh @@ -0,0 +1,20 @@ +#ifndef SHARED_ENTITY_HEAD_HH +#define SHARED_ENTITY_HEAD_HH 1 +#pragma once + +namespace entity +{ +struct Head { + glm::fvec3 angles; + glm::fvec3 offset; +}; +} // namespace entity + +namespace entity::client +{ +// Client-side only - interpolated and previous head +struct HeadIntr final : public Head {}; +struct HeadPrev final : public Head {}; +} // namespace entity::client + +#endif // SHARED_ENTITY_HEAD_HH diff --git a/game/shared/entity/player.hh b/game/shared/entity/player.hh new file mode 100644 index 0000000..3c8be32 --- /dev/null +++ b/game/shared/entity/player.hh @@ -0,0 +1,10 @@ +#ifndef SHARED_ENTITY_PLAYER_HH +#define SHARED_ENTITY_PLAYER_HH 1 +#pragma once + +namespace entity +{ +struct Player final {}; +} // namespace entity + +#endif // SHARED_ENTITY_PLAYER_HH diff --git a/game/shared/entity/stasis.cc b/game/shared/entity/stasis.cc new file mode 100644 index 0000000..10e2e0c --- /dev/null +++ b/game/shared/entity/stasis.cc @@ -0,0 +1,19 @@ +#include "shared/pch.hh" + +#include "shared/entity/stasis.hh" + +#include "shared/entity/transform.hh" +#include "shared/world/dimension.hh" + +void entity::Stasis::fixed_update(world::Dimension* dimension) +{ + auto view = dimension->entities.view<entity::Transform>(); + + for(auto [entity, transform] : view.each()) { + if(dimension->find_chunk(transform.chunk)) { + dimension->entities.remove<entity::Stasis>(entity); + } else { + dimension->entities.emplace_or_replace<entity::Stasis>(entity); + } + } +} diff --git a/game/shared/entity/stasis.hh b/game/shared/entity/stasis.hh new file mode 100644 index 0000000..27c0131 --- /dev/null +++ b/game/shared/entity/stasis.hh @@ -0,0 +1,20 @@ +#ifndef SHARED_ENTITY_STASIS_HH +#define SHARED_ENTITY_STASIS_HH 1 +#pragma once + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity +{ +// Attached to entities with transform values +// out of bounds in a specific dimension +struct Stasis final { +public: + static void fixed_update(world::Dimension* dimension); +}; +} // namespace entity + +#endif // SHARED_ENTITY_STASIS_HH diff --git a/src/game/shared/transform.cc b/game/shared/entity/transform.cc index f4b661c..379ddd5 100644 --- a/src/game/shared/transform.cc +++ b/game/shared/entity/transform.cc @@ -1,11 +1,12 @@ #include "shared/pch.hh" -#include "shared/transform.hh" +#include "shared/entity/transform.hh" + +#include "shared/world/dimension.hh" #include "shared/const.hh" -#include "shared/dimension.hh" -constexpr inline static void update_component(unsigned int dim, TransformComponent& component) +constexpr inline static void update_component(unsigned int dim, entity::Transform& component) { if(component.local[dim] >= CHUNK_SIZE) { component.local[dim] -= CHUNK_SIZE; @@ -20,9 +21,9 @@ constexpr inline static void update_component(unsigned int dim, TransformCompone } } -void TransformComponent::fixed_update(Dimension* dimension) +void entity::Transform::fixed_update(world::Dimension* dimension) { - auto view = dimension->entities.view<TransformComponent>(); + auto view = dimension->entities.view<entity::Transform>(); for(auto [entity, transform] : view.each()) { update_component(0U, transform); diff --git a/game/shared/entity/transform.hh b/game/shared/entity/transform.hh new file mode 100644 index 0000000..b1fdcf4 --- /dev/null +++ b/game/shared/entity/transform.hh @@ -0,0 +1,34 @@ +#ifndef SHARED_ENTITY_TRANSFORM_HH +#define SHARED_ENTITY_TRANSFORM_HH 1 +#pragma once + +#include "shared/types.hh" + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity +{ +struct Transform { + chunk_pos chunk; + glm::fvec3 local; + glm::fvec3 angles; + +public: + // Updates entity::Transform values so that + // the local translation field is always within + // local coodrinates; [floating-point precision] + static void fixed_update(world::Dimension* dimension); +}; +} // namespace entity + +namespace entity::client +{ +// Client-side only - interpolated and previous transform +struct TransformIntr final : public Transform {}; +struct TransformPrev final : public Transform {}; +} // namespace entity::client + +#endif // SHARED_ENTITY_TRANSFORM_HH diff --git a/game/shared/entity/velocity.cc b/game/shared/entity/velocity.cc new file mode 100644 index 0000000..9e40688 --- /dev/null +++ b/game/shared/entity/velocity.cc @@ -0,0 +1,18 @@ +#include "shared/pch.hh" + +#include "shared/entity/velocity.hh" + +#include "shared/entity/stasis.hh" +#include "shared/entity/transform.hh" +#include "shared/world/dimension.hh" + +#include "shared/globals.hh" + +void entity::Velocity::fixed_update(world::Dimension* dimension) +{ + auto group = dimension->entities.group<entity::Velocity>(entt::get<entity::Transform>, entt::exclude<entity::Stasis>); + + for(auto [entity, velocity, transform] : group.each()) { + transform.local += velocity.value * globals::fixed_frametime; + } +} diff --git a/game/shared/entity/velocity.hh b/game/shared/entity/velocity.hh new file mode 100644 index 0000000..5d4ec03 --- /dev/null +++ b/game/shared/entity/velocity.hh @@ -0,0 +1,23 @@ +#ifndef SHARED_ENTITY_VELOCITY_HH +#define SHARED_ENTITY_VELOCITY_HH 1 +#pragma once + +namespace world +{ +class Dimension; +} // namespace world + +namespace entity +{ +struct Velocity final { + glm::fvec3 value; + +public: + // Updates entities entity::Transform values + // according to velocities multiplied by fixed_frametime. + // NOTE: This system was previously called inertial + static void fixed_update(world::Dimension* dimension); +}; +} // namespace entity + +#endif // SHARED_VELOCITY_HH diff --git a/src/game/shared/game.cc b/game/shared/game.cc index 6e0037b..f383397 100644 --- a/src/game/shared/game.cc +++ b/game/shared/game.cc @@ -2,11 +2,11 @@ #include "shared/game.hh" -#include "core/cmdline.hh" +#include "core/io/cmdline.hh" static std::filesystem::path get_gamepath(void) { - if(auto gamepath = cmdline::get("gamepath")) { + if(auto gamepath = io::cmdline::get("gamepath")) { // Allow users and third-party launchers to override // content location. Perhaps this would work to allow // for a Minecraft-like versioning approach? @@ -18,7 +18,7 @@ static std::filesystem::path get_gamepath(void) static std::filesystem::path get_userpath(void) { - if(auto userpath = cmdline::get("userpath")) { + if(auto userpath = io::cmdline::get("userpath")) { // Allow users and third-party launchers to override // user data location. Perhaps this would work to allow // for a Minecraft-like versioning approach? @@ -63,9 +63,9 @@ void shared_game::init(int argc, char** argv) constexpr auto default_loglevel = spdlog::level::trace; #endif - if(cmdline::contains("quiet")) { + if(io::cmdline::contains("quiet")) { logger->set_level(spdlog::level::warn); - } else if(cmdline::contains("verbose")) { + } else if(io::cmdline::contains("verbose")) { logger->set_level(spdlog::level::trace); } else { logger->set_level(default_loglevel); diff --git a/src/game/shared/game.hh b/game/shared/game.hh index 06c1f51..06c1f51 100644 --- a/src/game/shared/game.hh +++ b/game/shared/game.hh diff --git a/game/shared/game_items.cc b/game/shared/game_items.cc new file mode 100644 index 0000000..58f379f --- /dev/null +++ b/game/shared/game_items.cc @@ -0,0 +1,69 @@ +#include "shared/pch.hh" + +#include "shared/game_items.hh" + +#include "shared/world/item_registry.hh" + +#include "shared/game_voxels.hh" + +item_id game_items::stone = NULL_ITEM_ID; +item_id game_items::cobblestone = NULL_ITEM_ID; +item_id game_items::dirt = NULL_ITEM_ID; +item_id game_items::grass = NULL_ITEM_ID; +item_id game_items::oak_leaves = NULL_ITEM_ID; +item_id game_items::oak_planks = NULL_ITEM_ID; +item_id game_items::oak_log = NULL_ITEM_ID; +item_id game_items::glass = NULL_ITEM_ID; +item_id game_items::slime = NULL_ITEM_ID; +item_id game_items::mud = NULL_ITEM_ID; + +void game_items::populate(void) +{ + // Stone; a hardened slate rock + game_items::stone = + world::item_registry::construct("stone").set_texture("textures/item/stone.png").set_place_voxel(game_voxels::stone).build(); + + // Cobblestone; a bunch of small stones + game_items::cobblestone = + world::item_registry::construct("cobblestone") + .set_texture("textures/item/cobblestone.png") + .set_place_voxel(game_voxels::cobblestone) + .build(); + + // Dirt; it's very dirty + game_items::dirt = + world::item_registry::construct("dirt").set_texture("textures/item/dirt.png").set_place_voxel(game_voxels::dirt).build(); + + // Grass; literally just grassy dirt + game_items::grass = + world::item_registry::construct("grass").set_texture("textures/item/grass.png").set_place_voxel(game_voxels::grass).build(); + + // Oak leaves; they're bushy! + game_items::oak_leaves = + world::item_registry::construct("oak_leaves") + .set_texture("textures/item/oak_leaves.png") + .set_place_voxel(game_voxels::oak_leaves) + .build(); + + // Oak planks; watch for splinters! + game_items::oak_planks = + world::item_registry::construct("oak_planks") + .set_texture("textures/item/oak_planks.png") + .set_place_voxel(game_voxels::oak_planks) + .build(); + + // Oak log; a big wad of wood + game_items::oak_log = + world::item_registry::construct("oak_log").set_texture("textures/item/oak_log.png").set_place_voxel(game_voxels::oak_log).build(); + + // Glass; used for windowing + game_items::glass = + world::item_registry::construct("glass").set_texture("textures/item/glass.png").set_place_voxel(game_voxels::glass).build(); + + // Slime; it's bouncy! + game_items::slime = + world::item_registry::construct("slime").set_texture("textures/item/slime.png").set_place_voxel(game_voxels::slime).build(); + + // Mud; you sink in it! + game_items::mud = world::item_registry::construct("mud").set_texture("textures/item/mud.png").build(); +} diff --git a/src/game/shared/game_items.hh b/game/shared/game_items.hh index 2b5c19a..2b5c19a 100644 --- a/src/game/shared/game_items.hh +++ b/game/shared/game_items.hh diff --git a/src/game/shared/game_voxels.cc b/game/shared/game_voxels.cc index 0059335..2d9655b 100644 --- a/src/game/shared/game_voxels.cc +++ b/game/shared/game_voxels.cc @@ -2,7 +2,7 @@ #include "shared/game_voxels.hh" -#include "shared/voxel_registry.hh" +#include "shared/world/voxel_registry.hh" voxel_id game_voxels::cobblestone = NULL_VOXEL_ID; voxel_id game_voxels::dirt = NULL_VOXEL_ID; @@ -21,49 +21,49 @@ void game_voxels::populate(void) { // Stone; the backbone of the generated world game_voxels::stone = - voxel_registry::construct("stone", voxel_type::CUBE, false, false) + world::voxel_registry::construct("stone", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/stone_01.png") .add_texture_default("textures/voxel/stone_02.png") .add_texture_default("textures/voxel/stone_03.png") .add_texture_default("textures/voxel/stone_04.png") - .set_surface(voxel_surface::STONE) + .set_surface(world::voxel_surface::STONE) .build(); // Cobblestone; should drop when a stone is broken, might also be present in surface features game_voxels::cobblestone = - voxel_registry::construct("cobblestone", voxel_type::CUBE, false, false) + world::voxel_registry::construct("cobblestone", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/cobblestone_01.png") .add_texture_default("textures/voxel/cobblestone_02.png") - .set_surface(voxel_surface::STONE) + .set_surface(world::voxel_surface::STONE) .build(); // Dirt with a grass layer on top; the top layer of plains biome game_voxels::grass = - voxel_registry::construct("grass", voxel_type::CUBE, false, false) + world::voxel_registry::construct("grass", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/grass_side_01.png") .add_texture_default("textures/voxel/grass_side_02.png") - .add_texture(voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_01.png") - .add_texture(voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_02.png") - .add_texture(voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_03.png") - .add_texture(voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_04.png") - .add_texture(voxel_face::CUBE_TOP, "textures/voxel/grass_01.png") - .add_texture(voxel_face::CUBE_TOP, "textures/voxel/grass_02.png") - .set_surface(voxel_surface::GRASS) + .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_01.png") + .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_02.png") + .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_03.png") + .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/dirt_04.png") + .add_texture(world::voxel_face::CUBE_TOP, "textures/voxel/grass_01.png") + .add_texture(world::voxel_face::CUBE_TOP, "textures/voxel/grass_02.png") + .set_surface(world::voxel_surface::GRASS) .build(); // Dirt; the under-surface layer of some biomes game_voxels::dirt = - voxel_registry::construct("dirt", voxel_type::CUBE, false, false) + world::voxel_registry::construct("dirt", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/dirt_01.png") .add_texture_default("textures/voxel/dirt_02.png") .add_texture_default("textures/voxel/dirt_03.png") .add_texture_default("textures/voxel/dirt_04.png") - .set_surface(voxel_surface::DIRT) + .set_surface(world::voxel_surface::DIRT) .build(); // VTest; a test voxel to ensure animations work game_voxels::vtest = - voxel_registry::construct("vtest", voxel_type::CUBE, true, false) + world::voxel_registry::construct("vtest", world::voxel_type::CUBE, true, false) .add_texture_default("textures/voxel/vtest_F1.png") .add_texture_default("textures/voxel/vtest_F2.png") .add_texture_default("textures/voxel/vtest_F3.png") @@ -72,51 +72,55 @@ void game_voxels::populate(void) // VTest-CK; a pure blue chromakey I used to make the game's logo game_voxels::vtest_ck = - voxel_registry::construct("vtest_ck", voxel_type::CUBE, false, false).add_texture_default("textures/voxel/chromakey.png").build(); + world::voxel_registry::construct("vtest_ck", world::voxel_type::CUBE, false, false) + .add_texture_default("textures/voxel/chromakey.png") + .build(); // Oak leaves; greenery. TODO: add trees as surface features game_voxels::oak_leaves = - voxel_registry::construct("oak_leaves", voxel_type::CUBE, false, false) + world::voxel_registry::construct("oak_leaves", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/oak_leaves.png") - .set_surface(voxel_surface::GRASS) + .set_surface(world::voxel_surface::GRASS) .build(); // Oak planks; the thing that comes out of oak logs game_voxels::oak_planks = - voxel_registry::construct("oak_planks", voxel_type::CUBE, false, false) + world::voxel_registry::construct("oak_planks", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/oak_planks_01.png") .add_texture_default("textures/voxel/oak_planks_02.png") - .set_surface(voxel_surface::WOOD) + .set_surface(world::voxel_surface::WOOD) .build(); // Oak logs; greenery. TODO: add trees as surface features game_voxels::oak_log = - voxel_registry::construct("oak_log", voxel_type::CUBE, false, false) + world::voxel_registry::construct("oak_log", world::voxel_type::CUBE, false, false) .add_texture_default("textures/voxel/oak_wood_01.png") .add_texture_default("textures/voxel/oak_wood_02.png") - .add_texture(voxel_face::CUBE_BOTTOM, "textures/voxel/oak_wood_top.png") - .add_texture(voxel_face::CUBE_TOP, "textures/voxel/oak_wood_top.png") - .set_surface(voxel_surface::WOOD) + .add_texture(world::voxel_face::CUBE_BOTTOM, "textures/voxel/oak_wood_top.png") + .add_texture(world::voxel_face::CUBE_TOP, "textures/voxel/oak_wood_top.png") + .set_surface(world::voxel_surface::WOOD) .build(); // Glass; blend rendering test - game_voxels::glass = voxel_registry::construct("glass", voxel_type::CUBE, false, true) - .add_texture_default("textures/voxel/glass_01.png") - .set_surface(voxel_surface::GLASS) - .build(); + game_voxels::glass = + world::voxel_registry::construct("glass", world::voxel_type::CUBE, false, true) + .add_texture_default("textures/voxel/glass_01.png") + .set_surface(world::voxel_surface::GLASS) + .build(); // Slime; it's bouncy! - game_voxels::slime = voxel_registry::construct("slime", voxel_type::CUBE, false, true) - .set_touch(voxel_touch::BOUNCE, glm::fvec3(0.00f, 0.60f, 0.00f)) - .add_texture_default("textures/voxel/slime_01.png") - .build(); + game_voxels::slime = + world::voxel_registry::construct("slime", world::voxel_type::CUBE, false, true) + .set_touch(world::voxel_touch::BOUNCE, glm::fvec3(0.00f, 0.60f, 0.00f)) + .add_texture_default("textures/voxel/slime_01.png") + .build(); // Mud; you sink in it game_voxels::mud = - voxel_registry::construct("mud", voxel_type::CUBE, false, false) - .set_touch(voxel_touch::SINK, glm::fvec3(0.50f, 0.75f, 0.50f)) + world::voxel_registry::construct("mud", world::voxel_type::CUBE, false, false) + .set_touch(world::voxel_touch::SINK, glm::fvec3(0.50f, 0.75f, 0.50f)) .add_texture_default("textures/voxel/mud_01.png") .add_texture_default("textures/voxel/mud_02.png") - .set_surface(voxel_surface::DIRT) + .set_surface(world::voxel_surface::DIRT) .build(); } diff --git a/src/game/shared/game_voxels.hh b/game/shared/game_voxels.hh index e96e141..e96e141 100644 --- a/src/game/shared/game_voxels.hh +++ b/game/shared/game_voxels.hh diff --git a/src/game/shared/globals.cc b/game/shared/globals.cc index 8552214..8552214 100644 --- a/src/game/shared/globals.cc +++ b/game/shared/globals.cc diff --git a/src/game/shared/globals.hh b/game/shared/globals.hh index bfaf6cb..bfaf6cb 100644 --- a/src/game/shared/globals.hh +++ b/game/shared/globals.hh diff --git a/src/game/shared/pch.hh b/game/shared/pch.hh index 8607dd3..d3c09d7 100644 --- a/src/game/shared/pch.hh +++ b/game/shared/pch.hh @@ -2,7 +2,7 @@ #define SHARED_PCH_HH 1 #pragma once -#include <core/pch.hh> +#include <core/pch.hh> // inherit dependent includes from core.lib #include <csignal> diff --git a/src/game/shared/protocol.cc b/game/shared/protocol.cc index 0e57fe4..3af891d 100644 --- a/src/game/shared/protocol.cc +++ b/game/shared/protocol.cc @@ -2,19 +2,20 @@ #include "shared/protocol.hh" -#include "core/buffer.hh" -#include "core/floathacks.hh" +#include "core/io/buffer.hh" +#include "core/math/floathacks.hh" + +#include "shared/entity/head.hh" +#include "shared/entity/player.hh" +#include "shared/entity/transform.hh" +#include "shared/entity/velocity.hh" +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" -#include "shared/chunk.hh" -#include "shared/dimension.hh" #include "shared/globals.hh" -#include "shared/head.hh" -#include "shared/player.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" -static ReadBuffer read_buffer; -static WriteBuffer write_buffer; +static io::ReadBuffer read_buffer; +static io::WriteBuffer write_buffer; ENetPacket* protocol::encode(const protocol::StatusRequest& packet, enet_uint32 flags) { @@ -421,9 +422,9 @@ ENetPacket* protocol::utils::make_chat_message(const char* message, enet_uint32 return protocol::encode(packet, flags); } -ENetPacket* protocol::utils::make_chunk_voxels(Dimension* dimension, entt::entity entity, enet_uint32 flags) +ENetPacket* protocol::utils::make_chunk_voxels(world::Dimension* dimension, entt::entity entity, enet_uint32 flags) { - if(auto component = dimension->chunks.try_get<ChunkComponent>(entity)) { + if(auto component = dimension->chunks.try_get<world::ChunkComponent>(entity)) { protocol::ChunkVoxels packet; packet.chunk = component->cpos; packet.voxels = component->chunk->get_voxels(); @@ -433,9 +434,9 @@ ENetPacket* protocol::utils::make_chunk_voxels(Dimension* dimension, entt::entit return nullptr; } -ENetPacket* protocol::utils::make_entity_head(Dimension* dimension, entt::entity entity, enet_uint32 flags) +ENetPacket* protocol::utils::make_entity_head(world::Dimension* dimension, entt::entity entity, enet_uint32 flags) { - if(auto component = dimension->entities.try_get<HeadComponent>(entity)) { + if(auto component = dimension->entities.try_get<entity::Head>(entity)) { protocol::EntityHead packet; packet.entity = entity; packet.angles = component->angles; @@ -445,9 +446,9 @@ ENetPacket* protocol::utils::make_entity_head(Dimension* dimension, entt::entity return nullptr; } -ENetPacket* protocol::utils::make_entity_transform(Dimension* dimension, entt::entity entity, enet_uint32 flags) +ENetPacket* protocol::utils::make_entity_transform(world::Dimension* dimension, entt::entity entity, enet_uint32 flags) { - if(auto component = dimension->entities.try_get<TransformComponent>(entity)) { + if(auto component = dimension->entities.try_get<entity::Transform>(entity)) { protocol::EntityTransform packet; packet.entity = entity; packet.chunk = component->chunk; @@ -459,9 +460,9 @@ ENetPacket* protocol::utils::make_entity_transform(Dimension* dimension, entt::e return nullptr; } -ENetPacket* protocol::utils::make_entity_velocity(Dimension* dimension, entt::entity entity, enet_uint32 flags) +ENetPacket* protocol::utils::make_entity_velocity(world::Dimension* dimension, entt::entity entity, enet_uint32 flags) { - if(auto component = dimension->entities.try_get<VelocityComponent>(entity)) { + if(auto component = dimension->entities.try_get<entity::Velocity>(entity)) { protocol::EntityVelocity packet; packet.entity = entity; packet.value = component->value; @@ -471,9 +472,9 @@ ENetPacket* protocol::utils::make_entity_velocity(Dimension* dimension, entt::en return nullptr; } -ENetPacket* protocol::utils::make_entity_player(Dimension* dimension, entt::entity entity, enet_uint32 flags) +ENetPacket* protocol::utils::make_entity_player(world::Dimension* dimension, entt::entity entity, enet_uint32 flags) { - if(dimension->entities.any_of<PlayerComponent>(entity)) { + if(dimension->entities.any_of<entity::Player>(entity)) { protocol::EntityPlayer packet; packet.entity = entity; return protocol::encode(packet, flags); @@ -482,7 +483,7 @@ ENetPacket* protocol::utils::make_entity_player(Dimension* dimension, entt::enti return nullptr; } -ENetPacket* protocol::utils::make_dimension_info(const Dimension* dimension) +ENetPacket* protocol::utils::make_dimension_info(const world::Dimension* dimension) { protocol::DimensionInfo packet; packet.name = dimension->get_name(); diff --git a/src/game/shared/protocol.hh b/game/shared/protocol.hh index fa9f1e1..9141797 100644 --- a/src/game/shared/protocol.hh +++ b/game/shared/protocol.hh @@ -2,9 +2,12 @@ #define SHARED_PROTOCOL_HH 1 #pragma once -#include "shared/chunk.hh" +#include "shared/world/chunk.hh" +namespace world +{ class Dimension; +} // namespace world namespace protocol { @@ -93,16 +96,16 @@ ENetPacket* make_chat_message(const char* message, enet_uint32 flags = ENET_PACK namespace protocol::utils { -ENetPacket* make_chunk_voxels(Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); +ENetPacket* make_chunk_voxels(world::Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); } // namespace protocol::utils namespace protocol::utils { -ENetPacket* make_entity_head(Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); -ENetPacket* make_entity_transform(Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); -ENetPacket* make_entity_velocity(Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); -ENetPacket* make_entity_player(Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); -ENetPacket* make_dimension_info(const Dimension* dimension); +ENetPacket* make_entity_head(world::Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); +ENetPacket* make_entity_transform(world::Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); +ENetPacket* make_entity_velocity(world::Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); +ENetPacket* make_entity_player(world::Dimension* dimension, entt::entity entity, enet_uint32 flags = ENET_PACKET_FLAG_RELIABLE); +ENetPacket* make_dimension_info(const world::Dimension* dimension); } // namespace protocol::utils struct protocol::StatusRequest final : public protocol::Base<0x0000> { @@ -136,7 +139,7 @@ struct protocol::Disconnect final : public protocol::Base<0x0004> { struct protocol::ChunkVoxels final : public protocol::Base<0x0005> { chunk_pos chunk; - VoxelStorage voxels; + world::VoxelStorage voxels; }; struct protocol::EntityTransform final : public protocol::Base<0x0006> { diff --git a/src/game/shared/splash.cc b/game/shared/splash.cc index 0cd5f50..0cd5f50 100644 --- a/src/game/shared/splash.cc +++ b/game/shared/splash.cc diff --git a/src/game/shared/splash.hh b/game/shared/splash.hh index f494a5b..f494a5b 100644 --- a/src/game/shared/splash.hh +++ b/game/shared/splash.hh diff --git a/src/game/shared/threading.cc b/game/shared/threading.cc index e479506..82bcad7 100644 --- a/src/game/shared/threading.cc +++ b/game/shared/threading.cc @@ -2,8 +2,8 @@ #include "shared/threading.hh" -#include "core/cmdline.hh" -#include "core/constexpr.hh" +#include "core/io/cmdline.hh" +#include "core/math/constexpr.hh" constexpr static const char* DEFAULT_POOL_SIZE_ARG = "4"; @@ -34,7 +34,7 @@ void Task::set_status(task_status status) void threading::init(void) { - auto argument = cmdline::get("threads", DEFAULT_POOL_SIZE_ARG); + auto argument = io::cmdline::get("threads", DEFAULT_POOL_SIZE_ARG); auto num_concurrent_threads = std::thread::hardware_concurrency(); unsigned int thread_pool_size; @@ -44,9 +44,9 @@ void threading::init(void) thread_pool_size = num_concurrent_threads; } else { if(num_concurrent_threads) { - thread_pool_size = vx::clamp<unsigned int>(std::strtoul(argument, nullptr, 10), 1U, num_concurrent_threads); + thread_pool_size = math::clamp<unsigned int>(std::strtoul(argument, nullptr, 10), 1U, num_concurrent_threads); } else { - thread_pool_size = vx::max<unsigned int>(std::strtoul(argument, nullptr, 10), 1U); + thread_pool_size = math::max<unsigned int>(std::strtoul(argument, nullptr, 10), 1U); } } diff --git a/src/game/shared/threading.hh b/game/shared/threading.hh index 02903d5..02903d5 100644 --- a/src/game/shared/threading.hh +++ b/game/shared/threading.hh diff --git a/src/game/shared/types.hh b/game/shared/types.hh index 161ea46..161ea46 100644 --- a/src/game/shared/types.hh +++ b/game/shared/types.hh diff --git a/game/shared/world/CMakeLists.txt b/game/shared/world/CMakeLists.txt new file mode 100644 index 0000000..6b9c281 --- /dev/null +++ b/game/shared/world/CMakeLists.txt @@ -0,0 +1,17 @@ +target_sources(shared PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/chunk.cc" + "${CMAKE_CURRENT_LIST_DIR}/chunk.hh" + "${CMAKE_CURRENT_LIST_DIR}/chunk_aabb.cc" + "${CMAKE_CURRENT_LIST_DIR}/chunk_aabb.hh" + "${CMAKE_CURRENT_LIST_DIR}/dimension.cc" + "${CMAKE_CURRENT_LIST_DIR}/dimension.hh" + "${CMAKE_CURRENT_LIST_DIR}/feature.cc" + "${CMAKE_CURRENT_LIST_DIR}/feature.hh" + "${CMAKE_CURRENT_LIST_DIR}/item_registry.cc" + "${CMAKE_CURRENT_LIST_DIR}/item_registry.hh" + "${CMAKE_CURRENT_LIST_DIR}/ray_dda.cc" + "${CMAKE_CURRENT_LIST_DIR}/ray_dda.hh" + "${CMAKE_CURRENT_LIST_DIR}/voxel_registry.cc" + "${CMAKE_CURRENT_LIST_DIR}/voxel_registry.hh" + "${CMAKE_CURRENT_LIST_DIR}/voxel_storage.cc" + "${CMAKE_CURRENT_LIST_DIR}/voxel_storage.hh") diff --git a/game/shared/world/chunk.cc b/game/shared/world/chunk.cc new file mode 100644 index 0000000..24a8d06 --- /dev/null +++ b/game/shared/world/chunk.cc @@ -0,0 +1,69 @@ +#include "shared/pch.hh" + +#include "shared/world/chunk.hh" + +#include "shared/coord.hh" + +world::Chunk::Chunk(entt::entity entity, Dimension* dimension) +{ + m_entity = entity; + m_dimension = dimension; + m_voxels.fill(NULL_VOXEL_ID); + m_biome = BIOME_VOID; +} + +voxel_id world::Chunk::get_voxel(const local_pos& lpos) const +{ + return get_voxel(coord::to_index(lpos)); +} + +voxel_id world::Chunk::get_voxel(const std::size_t index) const +{ + if(index >= CHUNK_VOLUME) { + return NULL_VOXEL_ID; + } else { + return m_voxels[index]; + } +} + +void world::Chunk::set_voxel(voxel_id voxel, const local_pos& lpos) +{ + set_voxel(voxel, coord::to_index(lpos)); +} + +void world::Chunk::set_voxel(voxel_id voxel, const std::size_t index) +{ + if(index < CHUNK_VOLUME) { + m_voxels[index] = voxel; + } +} + +const world::VoxelStorage& world::Chunk::get_voxels(void) const +{ + return m_voxels; +} + +void world::Chunk::set_voxels(const VoxelStorage& voxels) +{ + m_voxels = voxels; +} + +unsigned int world::Chunk::get_biome(void) const +{ + return m_biome; +} + +void world::Chunk::set_biome(unsigned int biome) +{ + m_biome = biome; +} + +entt::entity world::Chunk::get_entity(void) const +{ + return m_entity; +} + +world::Dimension* world::Chunk::get_dimension(void) const +{ + return m_dimension; +} diff --git a/src/game/shared/chunk.hh b/game/shared/world/chunk.hh index 2603537..5eacf44 100644 --- a/src/game/shared/chunk.hh +++ b/game/shared/world/chunk.hh @@ -3,12 +3,17 @@ #pragma once #include "shared/types.hh" -#include "shared/voxel_storage.hh" +#include "shared/world/voxel_storage.hh" constexpr static unsigned int BIOME_VOID = 0U; +namespace world +{ class Dimension; +} // namespace world +namespace world +{ class Chunk final { public: explicit Chunk(entt::entity entity, Dimension* dimension); @@ -35,5 +40,6 @@ private: VoxelStorage m_voxels; unsigned int m_biome; }; +} // namespace world #endif // SHARED_CHUNK_HH diff --git a/src/game/shared/chunk_aabb.cc b/game/shared/world/chunk_aabb.cc index 5f23ea9..c0e540e 100644 --- a/src/game/shared/chunk_aabb.cc +++ b/game/shared/world/chunk_aabb.cc @@ -1,20 +1,20 @@ #include "shared/pch.hh" -#include "shared/chunk_aabb.hh" +#include "shared/world/chunk_aabb.hh" -void ChunkAABB::set_bounds(const chunk_pos& min, const chunk_pos& max) +void world::ChunkAABB::set_bounds(const chunk_pos& min, const chunk_pos& max) { this->min = min; this->max = max; } -void ChunkAABB::set_offset(const chunk_pos& base, const chunk_pos& size) +void world::ChunkAABB::set_offset(const chunk_pos& base, const chunk_pos& size) { this->min = base; this->max = base + size; } -bool ChunkAABB::contains(const chunk_pos& point) const +bool world::ChunkAABB::contains(const chunk_pos& point) const { auto result = true; result = result && (point.x >= min.x) && (point.x <= max.x); @@ -23,7 +23,7 @@ bool ChunkAABB::contains(const chunk_pos& point) const return result; } -bool ChunkAABB::intersect(const ChunkAABB& other_box) const +bool world::ChunkAABB::intersect(const ChunkAABB& other_box) const { auto result = true; result = result && (min.x < other_box.max.x) && (max.x > other_box.min.x); @@ -32,21 +32,21 @@ bool ChunkAABB::intersect(const ChunkAABB& other_box) const return result; } -ChunkAABB ChunkAABB::combine_with(const ChunkAABB& other_box) const +world::ChunkAABB world::ChunkAABB::combine_with(const ChunkAABB& other_box) const { ChunkAABB result; result.set_bounds(min, other_box.max); return result; } -ChunkAABB ChunkAABB::multiply_with(const ChunkAABB& other_box) const +world::ChunkAABB world::ChunkAABB::multiply_with(const ChunkAABB& other_box) const { ChunkAABB result; result.set_bounds(other_box.min, max); return result; } -ChunkAABB ChunkAABB::push(const chunk_pos& vector) const +world::ChunkAABB world::ChunkAABB::push(const chunk_pos& vector) const { ChunkAABB result; result.set_bounds(min + vector, max + vector); diff --git a/src/game/shared/chunk_aabb.hh b/game/shared/world/chunk_aabb.hh index fa63ffe..bd4a0c5 100644 --- a/src/game/shared/chunk_aabb.hh +++ b/game/shared/world/chunk_aabb.hh @@ -4,6 +4,8 @@ #include "shared/types.hh" +namespace world +{ class ChunkAABB final { public: ChunkAABB(void) = default; @@ -26,5 +28,6 @@ public: chunk_pos min; chunk_pos max; }; +} // namespace world #endif // SHARED_CHUNK_AABB diff --git a/src/game/shared/dimension.cc b/game/shared/world/dimension.cc index a919dc4..b5cedad 100644 --- a/src/game/shared/dimension.cc +++ b/game/shared/world/dimension.cc @@ -1,18 +1,19 @@ #include "shared/pch.hh" -#include "shared/dimension.hh" +#include "shared/world/dimension.hh" + +#include "shared/world/chunk.hh" -#include "shared/chunk.hh" #include "shared/coord.hh" #include "shared/globals.hh" -Dimension::Dimension(const char* name, float gravity) +world::Dimension::Dimension(const char* name, float gravity) { m_name = name; m_gravity = gravity; } -Dimension::~Dimension(void) +world::Dimension::~Dimension(void) { for(const auto it : m_chunkmap) delete it.second; @@ -20,17 +21,17 @@ Dimension::~Dimension(void) chunks.clear(); } -const char* Dimension::get_name(void) const +const char* world::Dimension::get_name(void) const { return m_name.c_str(); } -float Dimension::get_gravity(void) const +float world::Dimension::get_gravity(void) const { return m_gravity; } -Chunk* Dimension::create_chunk(const chunk_pos& cpos) +world::Chunk* world::Dimension::create_chunk(const chunk_pos& cpos) { auto it = m_chunkmap.find(cpos); @@ -56,7 +57,7 @@ Chunk* Dimension::create_chunk(const chunk_pos& cpos) return m_chunkmap.insert_or_assign(cpos, std::move(chunk)).first->second; } -Chunk* Dimension::find_chunk(entt::entity entity) const +world::Chunk* world::Dimension::find_chunk(entt::entity entity) const { if(chunks.valid(entity)) { return chunks.get<ChunkComponent>(entity).chunk; @@ -65,7 +66,7 @@ Chunk* Dimension::find_chunk(entt::entity entity) const } } -Chunk* Dimension::find_chunk(const chunk_pos& cpos) const +world::Chunk* world::Dimension::find_chunk(const chunk_pos& cpos) const { auto it = m_chunkmap.find(cpos); @@ -76,7 +77,7 @@ Chunk* Dimension::find_chunk(const chunk_pos& cpos) const } } -void Dimension::remove_chunk(entt::entity entity) +void world::Dimension::remove_chunk(entt::entity entity) { if(chunks.valid(entity)) { auto& component = chunks.get<ChunkComponent>(entity); @@ -85,7 +86,7 @@ void Dimension::remove_chunk(entt::entity entity) } } -void Dimension::remove_chunk(const chunk_pos& cpos) +void world::Dimension::remove_chunk(const chunk_pos& cpos) { auto it = m_chunkmap.find(cpos); @@ -95,7 +96,7 @@ void Dimension::remove_chunk(const chunk_pos& cpos) } } -void Dimension::remove_chunk(Chunk* chunk) +void world::Dimension::remove_chunk(Chunk* chunk) { if(chunk) { const auto& component = chunks.get<ChunkComponent>(chunk->get_entity()); @@ -104,7 +105,7 @@ void Dimension::remove_chunk(Chunk* chunk) } } -voxel_id Dimension::get_voxel(const voxel_pos& vpos) const +voxel_id world::Dimension::get_voxel(const voxel_pos& vpos) const { auto cpos = coord::to_chunk(vpos); auto lpos = coord::to_local(vpos); @@ -116,7 +117,7 @@ voxel_id Dimension::get_voxel(const voxel_pos& vpos) const } } -voxel_id Dimension::get_voxel(const chunk_pos& cpos, const local_pos& lpos) const +voxel_id world::Dimension::get_voxel(const chunk_pos& cpos, const local_pos& lpos) const { // This allows accessing get_voxel with negative // local coordinates that usually would result in an @@ -124,7 +125,7 @@ voxel_id Dimension::get_voxel(const chunk_pos& cpos, const local_pos& lpos) cons return get_voxel(coord::to_voxel(cpos, lpos)); } -bool Dimension::set_voxel(voxel_id voxel, const voxel_pos& vpos) +bool world::Dimension::set_voxel(voxel_id voxel, const voxel_pos& vpos) { auto cpos = coord::to_chunk(vpos); auto lpos = coord::to_local(vpos); @@ -147,7 +148,7 @@ bool Dimension::set_voxel(voxel_id voxel, const voxel_pos& vpos) return false; } -bool Dimension::set_voxel(voxel_id voxel, const chunk_pos& cpos, const local_pos& lpos) +bool world::Dimension::set_voxel(voxel_id voxel, const chunk_pos& cpos, const local_pos& lpos) { // This allows accessing set_voxel with negative // local coordinates that usually would result in an @@ -155,15 +156,15 @@ bool Dimension::set_voxel(voxel_id voxel, const chunk_pos& cpos, const local_pos return set_voxel(voxel, coord::to_voxel(cpos, lpos)); } -void Dimension::init(Config& config) +void world::Dimension::init(io::ConfigMap& config) { } -void Dimension::init_late(std::uint64_t global_seed) +void world::Dimension::init_late(std::uint64_t global_seed) { } -bool Dimension::generate(const chunk_pos& cpos, VoxelStorage& voxels) +bool world::Dimension::generate(const chunk_pos& cpos, VoxelStorage& voxels) { return false; } diff --git a/src/game/shared/dimension.hh b/game/shared/world/dimension.hh index 398a4ce..5b06895 100644 --- a/src/game/shared/dimension.hh +++ b/game/shared/world/dimension.hh @@ -5,13 +5,25 @@ #include "shared/const.hh" #include "shared/types.hh" +namespace io +{ +class ConfigMap; +} // namespace io + +namespace world +{ class Chunk; -class Config; class VoxelStorage; +} // namespace world +namespace world +{ using dimension_entropy_map = std::array<std::uint64_t, CHUNK_AREA>; using dimension_height_map = std::array<voxel_pos::value_type, CHUNK_AREA>; +} // namespace world +namespace world +{ class Dimension { public: explicit Dimension(const char* name, float gravity); @@ -37,7 +49,7 @@ public: bool set_voxel(voxel_id voxel, const chunk_pos& cpos, const local_pos& lpos); public: - virtual void init(Config& config); + virtual void init(io::ConfigMap& config); virtual void init_late(std::uint64_t global_seed); virtual bool generate(const chunk_pos& cpos, VoxelStorage& voxels); @@ -50,12 +62,18 @@ private: emhash8::HashMap<chunk_pos, Chunk*> m_chunkmap; float m_gravity; }; +} // namespace world +namespace world +{ struct ChunkComponent final { chunk_pos cpos; Chunk* chunk; }; +} // namespace world +namespace world +{ struct ChunkCreateEvent final { Dimension* dimension; chunk_pos cpos; @@ -81,5 +99,6 @@ struct VoxelSetEvent final { voxel_id voxel; Chunk* chunk; }; +} // namespace world #endif // SHARED_DIMENSION_HH diff --git a/src/game/shared/feature.cc b/game/shared/world/feature.cc index eb6cceb..cbdc183 100644 --- a/src/game/shared/feature.cc +++ b/game/shared/world/feature.cc @@ -1,13 +1,14 @@ #include "shared/pch.hh" -#include "shared/feature.hh" +#include "shared/world/feature.hh" + +#include "shared/world/chunk.hh" +#include "shared/world/dimension.hh" +#include "shared/world/voxel_storage.hh" -#include "shared/chunk.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/voxel_storage.hh" -void Feature::place(const voxel_pos& vpos, Dimension* dimension) const +void world::Feature::place(const voxel_pos& vpos, Dimension* dimension) const { for(const auto [rpos, voxel, overwrite] : (*this)) { auto it_vpos = vpos + rpos; @@ -29,7 +30,7 @@ void Feature::place(const voxel_pos& vpos, Dimension* dimension) const } } -void Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const +void world::Feature::place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const { for(const auto [rpos, voxel, overwrite] : (*this)) { auto it_vpos = vpos + rpos; diff --git a/src/game/shared/feature.hh b/game/shared/world/feature.hh index 2a7b27b..e8465e3 100644 --- a/src/game/shared/feature.hh +++ b/game/shared/world/feature.hh @@ -4,9 +4,14 @@ #include "shared/types.hh" +namespace world +{ class Dimension; class VoxelStorage; +} // namespace world +namespace world +{ class Feature final : public std::vector<std::tuple<voxel_pos, voxel_id, bool>> { public: Feature(void) = default; @@ -16,5 +21,6 @@ public: void place(const voxel_pos& vpos, Dimension* dimension) const; void place(const voxel_pos& vpos, const chunk_pos& cpos, VoxelStorage& voxels) const; }; +} // namespace world #endif // SHARED_FEATURE_HH diff --git a/game/shared/world/item_registry.cc b/game/shared/world/item_registry.cc new file mode 100644 index 0000000..3ec3b88 --- /dev/null +++ b/game/shared/world/item_registry.cc @@ -0,0 +1,103 @@ +#include "shared/pch.hh" + +#include "shared/world/item_registry.hh" + +#include "core/math/crc64.hh" + +#include "shared/world/voxel_registry.hh" + +std::unordered_map<std::string, world::ItemInfoBuilder> world::item_registry::builders = {}; +std::unordered_map<std::string, item_id> world::item_registry::names = {}; +std::vector<std::shared_ptr<world::ItemInfo>> world::item_registry::items = {}; + +world::ItemInfoBuilder::ItemInfoBuilder(const char* name) +{ + prototype.name = name; + prototype.texture = std::string(); + prototype.place_voxel = NULL_VOXEL_ID; + prototype.cached_texture = nullptr; +} + +world::ItemInfoBuilder& world::ItemInfoBuilder::set_texture(const char* texture) +{ + prototype.texture = texture; + prototype.cached_texture = nullptr; + return *this; +} + +world::ItemInfoBuilder& world::ItemInfoBuilder::set_place_voxel(voxel_id place_voxel) +{ + prototype.place_voxel = place_voxel; + return *this; +} + +item_id world::ItemInfoBuilder::build(void) const +{ + const auto it = world::item_registry::names.find(prototype.name); + + if(it != world::item_registry::names.cend()) { + spdlog::warn("item_registry: cannot build {}: name already present", prototype.name); + return it->second; + } + + auto new_info = std::make_shared<ItemInfo>(); + new_info->name = prototype.name; + new_info->texture = prototype.texture; + new_info->place_voxel = prototype.place_voxel; + new_info->cached_texture = nullptr; + + world::item_registry::items.push_back(new_info); + world::item_registry::names.insert_or_assign(prototype.name, static_cast<item_id>(world::item_registry::items.size())); + + return static_cast<item_id>(world::item_registry::items.size()); +} + +world::ItemInfoBuilder& world::item_registry::construct(const char* name) +{ + const auto it = world::item_registry::builders.find(name); + + if(it != world::item_registry::builders.cend()) { + return it->second; + } else { + return world::item_registry::builders.emplace(name, ItemInfoBuilder(name)).first->second; + } +} + +world::ItemInfo* world::item_registry::find(const char* name) +{ + const auto it = world::item_registry::names.find(name); + + if(it != world::item_registry::names.cend()) { + return world::item_registry::find(it->second); + } else { + return nullptr; + } +} + +world::ItemInfo* world::item_registry::find(const item_id item) +{ + if((item != NULL_ITEM_ID) && (item <= world::item_registry::items.size())) { + return world::item_registry::items[item - 1].get(); + } else { + return nullptr; + } +} + +void world::item_registry::purge(void) +{ + world::item_registry::builders.clear(); + world::item_registry::names.clear(); + world::item_registry::items.clear(); +} + +std::uint64_t world::item_registry::calcualte_checksum(void) +{ + std::uint64_t result = 0; + + for(const auto& info : world::item_registry::items) { + result = math::crc64(info->name, result); + result += static_cast<std::uint64_t>(info->place_voxel); + } + + return result; +} diff --git a/src/game/shared/item_registry.hh b/game/shared/world/item_registry.hh index 8847e97..7cf3bd8 100644 --- a/src/game/shared/item_registry.hh +++ b/game/shared/world/item_registry.hh @@ -2,7 +2,7 @@ #define SHARED_ITEM_REGISTRY_HH 1 #pragma once -#include "core/resource.hh" +#include "core/resource/resource.hh" #include "shared/types.hh" @@ -11,6 +11,8 @@ // anywhere else in the shared and server code struct TextureGUI; +namespace world +{ struct ItemInfo final { std::string name; std::string texture; @@ -18,7 +20,10 @@ struct ItemInfo final { resource_ptr<TextureGUI> cached_texture; // Client-side only }; +} // namespace world +namespace world +{ class ItemInfoBuilder final { public: explicit ItemInfoBuilder(const char* name); @@ -34,29 +39,30 @@ public: private: ItemInfo prototype; }; +} // namespace world -namespace item_registry +namespace world::item_registry { extern std::unordered_map<std::string, ItemInfoBuilder> builders; extern std::unordered_map<std::string, item_id> names; extern std::vector<std::shared_ptr<ItemInfo>> items; -} // namespace item_registry +} // namespace world::item_registry -namespace item_registry +namespace world::item_registry { ItemInfoBuilder& construct(const char* name); ItemInfo* find(const char* name); ItemInfo* find(const item_id item); -} // namespace item_registry +} // namespace world::item_registry -namespace item_registry +namespace world::item_registry { void purge(void); -} // namespace item_registry +} // namespace world::item_registry -namespace item_registry +namespace world::item_registry { std::uint64_t calcualte_checksum(void); -} // namespace item_registry +} // namespace world::item_registry #endif // SHARED_ITEM_REGISTRY_HH diff --git a/src/game/shared/ray_dda.cc b/game/shared/world/ray_dda.cc index 75d4386..b12fa48 100644 --- a/src/game/shared/ray_dda.cc +++ b/game/shared/world/ray_dda.cc @@ -1,30 +1,34 @@ #include "shared/pch.hh" -#include "shared/ray_dda.hh" +#include "shared/world/ray_dda.hh" + +#include "shared/world/dimension.hh" #include "shared/coord.hh" -#include "shared/dimension.hh" -RayDDA::RayDDA(const Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) +world::RayDDA::RayDDA( + const world::Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) { reset(dimension, start_chunk, start_fpos, direction); } -RayDDA::RayDDA(const Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) +world::RayDDA::RayDDA( + const world::Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) { reset(dimension, start_chunk, start_fpos, direction); } -void RayDDA::reset(const Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) +void world::RayDDA::reset( + const world::Dimension* dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) { this->dimension = dimension; this->start_chunk = start_chunk; this->start_fpos = start_fpos; this->direction = direction; - this->delta_dist.x = direction.x ? vx::abs(1.0f / direction.x) : std::numeric_limits<float>::max(); - this->delta_dist.y = direction.y ? vx::abs(1.0f / direction.y) : std::numeric_limits<float>::max(); - this->delta_dist.z = direction.z ? vx::abs(1.0f / direction.z) : std::numeric_limits<float>::max(); + this->delta_dist.x = direction.x ? math::abs(1.0f / direction.x) : std::numeric_limits<float>::max(); + this->delta_dist.y = direction.y ? math::abs(1.0f / direction.y) : std::numeric_limits<float>::max(); + this->delta_dist.z = direction.z ? math::abs(1.0f / direction.z) : std::numeric_limits<float>::max(); this->distance = 0.0f; this->vpos = coord::to_voxel(start_chunk, start_fpos); @@ -58,12 +62,13 @@ void RayDDA::reset(const Dimension* dimension, const chunk_pos& start_chunk, con } } -void RayDDA::reset(const Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) +void world::RayDDA::reset( + const world::Dimension& dimension, const chunk_pos& start_chunk, const glm::fvec3& start_fpos, const glm::fvec3& direction) { reset(&dimension, start_chunk, start_fpos, direction); } -voxel_id RayDDA::step(void) +voxel_id world::RayDDA::step(void) { if(side_dist.x < side_dist.z) { if(side_dist.x < side_dist.y) { diff --git a/src/game/shared/ray_dda.hh b/game/shared/world/ray_dda.hh index 5cc0005..927c74d 100644 --- a/src/game/shared/ray_dda.hh +++ b/game/shared/world/ray_dda.hh @@ -4,8 +4,13 @@ #include "shared/types.hh" +namespace world +{ class Dimension; +} // namespace world +namespace world +{ class RayDDA final { public: RayDDA(void) = default; @@ -31,5 +36,6 @@ public: voxel_pos vnormal; voxel_pos vpos; }; +} // namespace world #endif // SHARED_RAY_DDA diff --git a/src/game/shared/voxel_registry.cc b/game/shared/world/voxel_registry.cc index cb3c724..2a82445 100644 --- a/src/game/shared/voxel_registry.cc +++ b/game/shared/world/voxel_registry.cc @@ -1,14 +1,14 @@ #include "shared/pch.hh" -#include "shared/voxel_registry.hh" +#include "shared/world/voxel_registry.hh" -#include "core/crc64.hh" +#include "core/math/crc64.hh" -std::unordered_map<std::string, VoxelInfoBuilder> voxel_registry::builders = {}; -std::unordered_map<std::string, voxel_id> voxel_registry::names = {}; -std::vector<std::shared_ptr<VoxelInfo>> voxel_registry::voxels = {}; +std::unordered_map<std::string, world::VoxelInfoBuilder> world::voxel_registry::builders = {}; +std::unordered_map<std::string, voxel_id> world::voxel_registry::names = {}; +std::vector<std::shared_ptr<world::VoxelInfo>> world::voxel_registry::voxels = {}; -VoxelInfoBuilder::VoxelInfoBuilder(const char* name, voxel_type type, bool animated, bool blending) +world::VoxelInfoBuilder::VoxelInfoBuilder(const char* name, voxel_type type, bool animated, bool blending) { prototype.name = name; prototype.type = type; @@ -45,37 +45,37 @@ VoxelInfoBuilder::VoxelInfoBuilder(const char* name, voxel_type type, bool anima prototype.item_pick = NULL_ITEM_ID; } -VoxelInfoBuilder& VoxelInfoBuilder::add_texture_default(const char* texture) +world::VoxelInfoBuilder& world::VoxelInfoBuilder::add_texture_default(const char* texture) { default_texture.paths.push_back(texture); return *this; } -VoxelInfoBuilder& VoxelInfoBuilder::add_texture(voxel_face face, const char* texture) +world::VoxelInfoBuilder& world::VoxelInfoBuilder::add_texture(voxel_face face, const char* texture) { const auto index = static_cast<std::size_t>(face); prototype.textures[index].paths.push_back(texture); return *this; } -VoxelInfoBuilder& VoxelInfoBuilder::set_touch(voxel_touch type, const glm::fvec3& values) +world::VoxelInfoBuilder& world::VoxelInfoBuilder::set_touch(voxel_touch type, const glm::fvec3& values) { prototype.touch_type = type; prototype.touch_values = values; return *this; } -VoxelInfoBuilder& VoxelInfoBuilder::set_surface(voxel_surface surface) +world::VoxelInfoBuilder& world::VoxelInfoBuilder::set_surface(voxel_surface surface) { prototype.surface = surface; return *this; } -voxel_id VoxelInfoBuilder::build(void) const +voxel_id world::VoxelInfoBuilder::build(void) const { - const auto it = voxel_registry::names.find(prototype.name); + const auto it = world::voxel_registry::names.find(prototype.name); - if(it != voxel_registry::names.cend()) { + if(it != world::voxel_registry::names.cend()) { spdlog::warn("voxel_registry: cannot build {}: name already present", prototype.name); return it->second; } @@ -96,7 +96,7 @@ voxel_id VoxelInfoBuilder::build(void) const std::terminate(); } - if((voxel_registry::voxels.size() + state_count) >= MAX_VOXEL_ID) { + if((world::voxel_registry::voxels.size() + state_count) >= MAX_VOXEL_ID) { spdlog::critical("voxel_registry: voxel registry overflow"); std::terminate(); } @@ -130,59 +130,59 @@ voxel_id VoxelInfoBuilder::build(void) const new_info->item_pick = prototype.item_pick; // Base voxel identifier offset - new_info->base_voxel = voxel_registry::voxels.size() + 1; + new_info->base_voxel = world::voxel_registry::voxels.size() + 1; for(std::size_t i = 0; i < state_count; ++i) - voxel_registry::voxels.push_back(new_info); - voxel_registry::names.insert_or_assign(new_info->name, new_info->base_voxel); + world::voxel_registry::voxels.push_back(new_info); + world::voxel_registry::names.insert_or_assign(new_info->name, new_info->base_voxel); return new_info->base_voxel; } -VoxelInfoBuilder& voxel_registry::construct(const char* name, voxel_type type, bool animated, bool blending) +world::VoxelInfoBuilder& world::voxel_registry::construct(const char* name, voxel_type type, bool animated, bool blending) { - const auto it = voxel_registry::builders.find(name); + const auto it = world::voxel_registry::builders.find(name); - if(it != voxel_registry::builders.cend()) { + if(it != world::voxel_registry::builders.cend()) { return it->second; } else { - return voxel_registry::builders.emplace(name, VoxelInfoBuilder(name, type, animated, blending)).first->second; + return world::voxel_registry::builders.emplace(name, VoxelInfoBuilder(name, type, animated, blending)).first->second; } } -VoxelInfo* voxel_registry::find(const char* name) +world::VoxelInfo* world::voxel_registry::find(const char* name) { - const auto it = voxel_registry::names.find(name); + const auto it = world::voxel_registry::names.find(name); - if(it != voxel_registry::names.cend()) { - return voxel_registry::find(it->second); + if(it != world::voxel_registry::names.cend()) { + return world::voxel_registry::find(it->second); } else { return nullptr; } } -VoxelInfo* voxel_registry::find(const voxel_id voxel) +world::VoxelInfo* world::voxel_registry::find(const voxel_id voxel) { - if((voxel != NULL_VOXEL_ID) && (voxel <= voxel_registry::voxels.size())) { - return voxel_registry::voxels[voxel - 1].get(); + if((voxel != NULL_VOXEL_ID) && (voxel <= world::voxel_registry::voxels.size())) { + return world::voxel_registry::voxels[voxel - 1].get(); } else { return nullptr; } } -void voxel_registry::purge(void) +void world::voxel_registry::purge(void) { - voxel_registry::builders.clear(); - voxel_registry::names.clear(); - voxel_registry::voxels.clear(); + world::voxel_registry::builders.clear(); + world::voxel_registry::names.clear(); + world::voxel_registry::voxels.clear(); } -std::uint64_t voxel_registry::calcualte_checksum(void) +std::uint64_t world::voxel_registry::calcualte_checksum(void) { std::uint64_t result = 0; - for(const std::shared_ptr<VoxelInfo>& info : voxel_registry::voxels) { - result = crc64::get(info->name, result); + for(const std::shared_ptr<VoxelInfo>& info : world::voxel_registry::voxels) { + result = math::crc64(info->name, result); result += static_cast<std::uint64_t>(info->type); result += static_cast<std::uint64_t>(info->base_voxel); result += info->blending ? 256 : 1; diff --git a/src/game/shared/voxel_registry.hh b/game/shared/world/voxel_registry.hh index e0b2100..653c56e 100644 --- a/src/game/shared/voxel_registry.hh +++ b/game/shared/world/voxel_registry.hh @@ -4,6 +4,8 @@ #include "shared/types.hh" +namespace world +{ enum class voxel_face : unsigned short { CUBE_NORTH = 0x0000, CUBE_SOUTH = 0x0001, @@ -64,7 +66,10 @@ constexpr static voxel_vis VIS_EAST = 1 << static_cast<unsigned int>(voxel_facin constexpr static voxel_vis VIS_WEST = 1 << static_cast<unsigned int>(voxel_facing::WEST); constexpr static voxel_vis VIS_UP = 1 << static_cast<unsigned int>(voxel_facing::UP); constexpr static voxel_vis VIS_DOWN = 1 << static_cast<unsigned int>(voxel_facing::DOWN); +} // namespace world +namespace world +{ struct VoxelTexture final { std::vector<std::string> paths; std::size_t cached_offset; // client-side only @@ -97,7 +102,10 @@ struct VoxelInfo final { // and by default set to NULL_ITEM_ID item_id item_pick; }; +} // namespace world +namespace world +{ class VoxelInfoBuilder final { public: explicit VoxelInfoBuilder(const char* name, voxel_type type, bool animated, bool blending); @@ -116,29 +124,30 @@ private: VoxelTexture default_texture; VoxelInfo prototype; }; +} // namespace world -namespace voxel_registry +namespace world::voxel_registry { extern std::unordered_map<std::string, VoxelInfoBuilder> builders; extern std::unordered_map<std::string, voxel_id> names; extern std::vector<std::shared_ptr<VoxelInfo>> voxels; -} // namespace voxel_registry +} // namespace world::voxel_registry -namespace voxel_registry +namespace world::voxel_registry { VoxelInfoBuilder& construct(const char* name, voxel_type type, bool animated, bool blending); VoxelInfo* find(const char* name); VoxelInfo* find(const voxel_id voxel); -} // namespace voxel_registry +} // namespace world::voxel_registry -namespace voxel_registry +namespace world::voxel_registry { void purge(void); -} // namespace voxel_registry +} // namespace world::voxel_registry -namespace voxel_registry +namespace world::voxel_registry { std::uint64_t calcualte_checksum(void); -} // namespace voxel_registry +} // namespace world::voxel_registry #endif // SHARED_VOXEL_REGISTRY_HH diff --git a/src/game/shared/voxel_storage.cc b/game/shared/world/voxel_storage.cc index f2c4d42..e28c34f 100644 --- a/src/game/shared/voxel_storage.cc +++ b/game/shared/world/voxel_storage.cc @@ -1,10 +1,10 @@ #include "shared/pch.hh" -#include "shared/voxel_storage.hh" +#include "shared/world/voxel_storage.hh" -#include "core/buffer.hh" +#include "core/io/buffer.hh" -void VoxelStorage::serialize(WriteBuffer& buffer) const +void world::VoxelStorage::serialize(io::WriteBuffer& buffer) const { auto bound = mz_compressBound(sizeof(VoxelStorage)); auto zdata = std::vector<unsigned char>(bound); @@ -23,19 +23,21 @@ void VoxelStorage::serialize(WriteBuffer& buffer) const buffer.write_UI64(bound); // Write all the compressed data into the buffer - for(std::size_t i = 0; i < bound; buffer.write_UI8(zdata[i++])) - ; + for(std::size_t i = 0; i < bound; buffer.write_UI8(zdata[i++])) { + // empty + } } -void VoxelStorage::deserialize(ReadBuffer& buffer) +void world::VoxelStorage::deserialize(io::ReadBuffer& buffer) { auto size = static_cast<mz_ulong>(sizeof(VoxelStorage)); auto bound = static_cast<mz_ulong>(buffer.read_UI64()); auto zdata = std::vector<unsigned char>(bound); // Read all the compressed data from the buffer - for(std::size_t i = 0; i < bound; zdata[i++] = buffer.read_UI8()) - ; + for(std::size_t i = 0; i < bound; zdata[i++] = buffer.read_UI8()) { + // empty + } mz_uncompress(reinterpret_cast<unsigned char*>(data()), &size, zdata.data(), bound); diff --git a/src/game/shared/voxel_storage.hh b/game/shared/world/voxel_storage.hh index 702bdac..fdb6e33 100644 --- a/src/game/shared/voxel_storage.hh +++ b/game/shared/world/voxel_storage.hh @@ -5,14 +5,20 @@ #include "shared/const.hh" #include "shared/types.hh" +namespace io +{ class ReadBuffer; class WriteBuffer; +} // namespace io +namespace world +{ class VoxelStorage final : public std::array<voxel_id, CHUNK_VOLUME> { public: using std::array<voxel_id, CHUNK_VOLUME>::array; - void serialize(WriteBuffer& buffer) const; - void deserialize(ReadBuffer& buffer); + void serialize(io::WriteBuffer& buffer) const; + void deserialize(io::ReadBuffer& buffer); }; +} // namespace world #endif // SHARED_VOXEL_STORAGE_HH diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index ac6b9d0..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(core) -add_subdirectory(game) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt deleted file mode 100644 index 92f9cb8..0000000 --- a/src/core/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -add_library(core STATIC - "${CMAKE_CURRENT_LIST_DIR}/aabb.hh" - "${CMAKE_CURRENT_LIST_DIR}/aabb.cc" - "${CMAKE_CURRENT_LIST_DIR}/binfile.hh" - "${CMAKE_CURRENT_LIST_DIR}/binfile.cc" - "${CMAKE_CURRENT_LIST_DIR}/buffer.hh" - "${CMAKE_CURRENT_LIST_DIR}/buffer.cc" - "${CMAKE_CURRENT_LIST_DIR}/cmdline.hh" - "${CMAKE_CURRENT_LIST_DIR}/cmdline.cc" - "${CMAKE_CURRENT_LIST_DIR}/concepts.hh" - "${CMAKE_CURRENT_LIST_DIR}/config.cc" - "${CMAKE_CURRENT_LIST_DIR}/config.hh" - "${CMAKE_CURRENT_LIST_DIR}/constexpr.hh" - "${CMAKE_CURRENT_LIST_DIR}/crc64.cc" - "${CMAKE_CURRENT_LIST_DIR}/crc64.hh" - "${CMAKE_CURRENT_LIST_DIR}/epoch.cc" - "${CMAKE_CURRENT_LIST_DIR}/epoch.hh" - "${CMAKE_CURRENT_LIST_DIR}/floathacks.hh" - "${CMAKE_CURRENT_LIST_DIR}/image.cc" - "${CMAKE_CURRENT_LIST_DIR}/image.hh" - "${CMAKE_CURRENT_LIST_DIR}/pch.hh" - "${CMAKE_CURRENT_LIST_DIR}/resource.hh" - "${CMAKE_CURRENT_LIST_DIR}/strtools.cc" - "${CMAKE_CURRENT_LIST_DIR}/strtools.hh" - "${CMAKE_CURRENT_LIST_DIR}/version.cc" - "${CMAKE_CURRENT_LIST_DIR}/version.hh") -target_compile_features(core PUBLIC cxx_std_20) -target_include_directories(core PUBLIC "${DEPS_INCLUDE_DIR}") -target_include_directories(core PUBLIC "${PROJECT_SOURCE_DIR}/src") -target_precompile_headers(core PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") -target_link_libraries(core PUBLIC enet emhash glm physfs spdlog stb) - -if(WIN32) - target_compile_definitions(core PUBLIC _CRT_SECURE_NO_WARNINGS) - target_compile_definitions(core PUBLIC _USE_MATH_DEFINES) - target_compile_definitions(core PUBLIC NOMINMAX) -endif() - -if(MSVC) - target_compile_options(core PUBLIC /utf-8) -endif() - -configure_file("${CMAKE_CURRENT_LIST_DIR}/version.cc.in" "${CMAKE_CURRENT_LIST_DIR}/version.cc") diff --git a/src/core/binfile.hh b/src/core/binfile.hh deleted file mode 100644 index 56a3ddc..0000000 --- a/src/core/binfile.hh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CORE_BINFILE_HH -#define CORE_BINFILE_HH 1 -#pragma once - -struct BinFile final { - std::byte* buffer; - std::size_t size; -}; - -#endif // CORE_BINFILE_HH diff --git a/src/core/config.hh b/src/core/config.hh deleted file mode 100644 index e63b560..0000000 --- a/src/core/config.hh +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef CORE_CONFIG_HH -#define CORE_CONFIG_HH 1 -#pragma once - -#include "core/concepts.hh" - -class IConfigValue { -public: - virtual ~IConfigValue(void) = default; - virtual void set(const char* value) = 0; - virtual const char* get(void) const = 0; -}; - -class ConfigBoolean final : public IConfigValue { -public: - explicit ConfigBoolean(bool default_value = false); - virtual ~ConfigBoolean(void) = default; - - virtual void set(const char* value) override; - virtual const char* get(void) const override; - - bool get_value(void) const; - void set_value(bool value); - -private: - bool m_value; - std::string m_string; - -public: - static const char* to_string(bool value); - static bool from_string(const char* value); -}; - -template<vx::Arithmetic T> -class ConfigNumber : public IConfigValue { -public: - explicit ConfigNumber(T default_value = T(0)); - explicit ConfigNumber(T default_value, T min_value, T max_value); - virtual ~ConfigNumber(void) = default; - - virtual void set(const char* value) override; - virtual const char* get(void) const override; - - T get_value(void) const; - void set_value(T value); - - T get_min_value(void) const; - T get_max_value(void) const; - void set_limits(T min_value, T max_value); - -private: - T m_value; - T m_min_value; - T m_max_value; - std::string m_string; -}; - -class ConfigInt final : public ConfigNumber<int> { -public: - using ConfigNumber<int>::ConfigNumber; -}; - -class ConfigFloat final : public ConfigNumber<float> { -public: - using ConfigNumber<float>::ConfigNumber; -}; - -class ConfigUnsigned final : public ConfigNumber<unsigned int> { -public: - using ConfigNumber<unsigned int>::ConfigNumber; -}; - -class ConfigUnsigned64 final : public ConfigNumber<std::uint64_t> { -public: - using ConfigNumber<std::uint64_t>::ConfigNumber; -}; - -class ConfigSizeType final : public ConfigNumber<std::size_t> { -public: - using ConfigNumber<std::size_t>::ConfigNumber; -}; - -class ConfigString final : public IConfigValue { -public: - explicit ConfigString(const char* default_value); - virtual ~ConfigString(void) = default; - - virtual void set(const char* value) override; - virtual const char* get(void) const override; - -private: - std::string m_value; -}; - -class Config final { -public: - Config(void) = default; - virtual ~Config(void) = default; - - void load_cmdline(void); - bool load_file(const char* path); - bool save_file(const char* path) const; - - bool set_value(const char* name, const char* value); - const char* get_value(const char* name) const; - - void add_value(const char* name, IConfigValue& vref); - - const IConfigValue* find(const char* name) const; - -private: - std::unordered_map<std::string, IConfigValue*> m_values; -}; - -template<vx::Arithmetic T> -inline ConfigNumber<T>::ConfigNumber(T default_value) -{ - m_value = default_value; - m_min_value = std::numeric_limits<T>::min(); - m_max_value = std::numeric_limits<T>::max(); - m_string = std::to_string(default_value); -} - -template<vx::Arithmetic T> -inline ConfigNumber<T>::ConfigNumber(T default_value, T min_value, T max_value) -{ - m_value = default_value; - m_min_value = min_value; - m_max_value = max_value; - m_string = std::to_string(default_value); -} - -template<vx::Arithmetic T> -inline void ConfigNumber<T>::set(const char* value) -{ - std::istringstream(value) >> m_value; - m_value = std::clamp(m_value, m_min_value, m_max_value); - m_string = std::to_string(m_value); -} - -template<vx::Arithmetic T> -inline const char* ConfigNumber<T>::get(void) const -{ - return m_string.c_str(); -} - -template<vx::Arithmetic T> -inline T ConfigNumber<T>::get_value(void) const -{ - return m_value; -} - -template<vx::Arithmetic T> -inline void ConfigNumber<T>::set_value(T value) -{ - m_value = std::clamp(value, m_min_value, m_max_value); - m_string = std::to_string(m_value); -} - -template<vx::Arithmetic T> -inline T ConfigNumber<T>::get_min_value(void) const -{ - return m_min_value; -} - -template<vx::Arithmetic T> -inline T ConfigNumber<T>::get_max_value(void) const -{ - return m_max_value; -} - -template<vx::Arithmetic T> -inline void ConfigNumber<T>::set_limits(T min_value, T max_value) -{ - m_min_value = min_value; - m_max_value = max_value; - m_value = std::clamp(m_value, m_min_value, m_max_value); - m_string = std::to_string(m_value); -} - -#endif // CORE_CONFIG_HH diff --git a/src/core/crc64.hh b/src/core/crc64.hh deleted file mode 100644 index 642afea..0000000 --- a/src/core/crc64.hh +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CORE_CRC64_HH -#define CORE_CRC64_HH 1 -#pragma once - -namespace crc64 -{ -std::uint64_t get(const void* buffer, std::size_t size, std::uint64_t combine = UINT64_C(0)); -std::uint64_t get(const std::vector<std::byte>& buffer, std::uint64_t combine = UINT64_C(0)); -std::uint64_t get(const std::string& buffer, std::uint64_t combine = UINT64_C(0)); -} // namespace crc64 - -#endif // CORE_CRC64_HH diff --git a/src/core/epoch.hh b/src/core/epoch.hh deleted file mode 100644 index b17aadf..0000000 --- a/src/core/epoch.hh +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CORE_EPOCH_HH -#define CORE_EPOCH_HH 1 -#pragma once - -namespace epoch -{ -std::uint64_t seconds(void); -std::uint64_t milliseconds(void); -std::uint64_t microseconds(void); -} // namespace epoch - -namespace epoch -{ -std::int64_t signed_seconds(void); -std::int64_t signed_milliseconds(void); -std::int64_t signed_microseconds(void); -} // namespace epoch - -#endif // CORE_EPOCH_HH diff --git a/src/core/vectors.hh b/src/core/vectors.hh deleted file mode 100644 index 11ba2a1..0000000 --- a/src/core/vectors.hh +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef CORE_VECTORS_HH -#define CORE_VECTORS_HH 1 -#pragma once - -#include "core/concepts.hh" - -// core/vectors.hh - because NO ONE would POSSIBLY -// need integer-based distance calculations in a -// game about voxels. That would be INSANE! :D - -namespace vx -{ -template<vx::Arithmetic T> -constexpr static inline const T length2(const glm::vec<2, T>& vector); -template<vx::Arithmetic T> -constexpr static inline const T length2(const glm::vec<3, T>& vector); -template<vx::Arithmetic T> -constexpr static inline const T distance2(const glm::vec<2, T>& vector_a, const glm::vec<2, T>& vector_b); -template<vx::Arithmetic T> -constexpr static inline const T distance2(const glm::vec<3, T>& vector_a, const glm::vec<3, T>& vector_b); -} // namespace vx - -template<vx::Arithmetic T> -constexpr static inline const T vx::length2(const glm::vec<2, T>& vector) -{ - return (vector.x * vector.x) + (vector.y * vector.y); -} - -template<vx::Arithmetic T> -constexpr static inline const T vx::length2(const glm::vec<3, T>& vector) -{ - return (vector.x * vector.x) + (vector.y * vector.y) + (vector.z * vector.z); -} - -template<vx::Arithmetic T> -constexpr static inline const T vx::distance2(const glm::vec<2, T>& vector_a, const glm::vec<2, T>& vector_b) -{ - return vx::length2(vector_a - vector_b); -} - -template<vx::Arithmetic T> -constexpr static inline const T vx::distance2(const glm::vec<3, T>& vector_a, const glm::vec<3, T>& vector_b) -{ - return vx::length2(vector_a - vector_b); -} - -#endif // CORE_VECTORS_HH diff --git a/src/game/client/CMakeLists.txt b/src/game/client/CMakeLists.txt deleted file mode 100644 index ac40148..0000000 --- a/src/game/client/CMakeLists.txt +++ /dev/null @@ -1,126 +0,0 @@ -add_executable(vclient - "${CMAKE_CURRENT_LIST_DIR}/background.cc" - "${CMAKE_CURRENT_LIST_DIR}/background.hh" - "${CMAKE_CURRENT_LIST_DIR}/bother.cc" - "${CMAKE_CURRENT_LIST_DIR}/bother.hh" - "${CMAKE_CURRENT_LIST_DIR}/camera.cc" - "${CMAKE_CURRENT_LIST_DIR}/camera.hh" - "${CMAKE_CURRENT_LIST_DIR}/chat.cc" - "${CMAKE_CURRENT_LIST_DIR}/chat.hh" - "${CMAKE_CURRENT_LIST_DIR}/chunk_mesher.cc" - "${CMAKE_CURRENT_LIST_DIR}/chunk_mesher.hh" - "${CMAKE_CURRENT_LIST_DIR}/chunk_quad.hh" - "${CMAKE_CURRENT_LIST_DIR}/chunk_renderer.cc" - "${CMAKE_CURRENT_LIST_DIR}/chunk_renderer.hh" - "${CMAKE_CURRENT_LIST_DIR}/chunk_vbo.hh" - "${CMAKE_CURRENT_LIST_DIR}/chunk_visibility.cc" - "${CMAKE_CURRENT_LIST_DIR}/chunk_visibility.hh" - "${CMAKE_CURRENT_LIST_DIR}/const.hh" - "${CMAKE_CURRENT_LIST_DIR}/crosshair.cc" - "${CMAKE_CURRENT_LIST_DIR}/crosshair.hh" - "${CMAKE_CURRENT_LIST_DIR}/direct_connection.cc" - "${CMAKE_CURRENT_LIST_DIR}/direct_connection.hh" - "${CMAKE_CURRENT_LIST_DIR}/experiments.cc" - "${CMAKE_CURRENT_LIST_DIR}/experiments.hh" - "${CMAKE_CURRENT_LIST_DIR}/factory.cc" - "${CMAKE_CURRENT_LIST_DIR}/factory.hh" - "${CMAKE_CURRENT_LIST_DIR}/game.cc" - "${CMAKE_CURRENT_LIST_DIR}/game.hh" - "${CMAKE_CURRENT_LIST_DIR}/gamepad_axis.cc" - "${CMAKE_CURRENT_LIST_DIR}/gamepad_axis.hh" - "${CMAKE_CURRENT_LIST_DIR}/gamepad_button.cc" - "${CMAKE_CURRENT_LIST_DIR}/gamepad_button.hh" - "${CMAKE_CURRENT_LIST_DIR}/gamepad.cc" - "${CMAKE_CURRENT_LIST_DIR}/gamepad.hh" - "${CMAKE_CURRENT_LIST_DIR}/glfw.hh" - "${CMAKE_CURRENT_LIST_DIR}/globals.cc" - "${CMAKE_CURRENT_LIST_DIR}/globals.hh" - "${CMAKE_CURRENT_LIST_DIR}/gui_screen.hh" - "${CMAKE_CURRENT_LIST_DIR}/hotbar.cc" - "${CMAKE_CURRENT_LIST_DIR}/hotbar.hh" - "${CMAKE_CURRENT_LIST_DIR}/imdraw_ext.cc" - "${CMAKE_CURRENT_LIST_DIR}/imdraw_ext.hh" - "${CMAKE_CURRENT_LIST_DIR}/interpolation.cc" - "${CMAKE_CURRENT_LIST_DIR}/interpolation.hh" - "${CMAKE_CURRENT_LIST_DIR}/keybind.cc" - "${CMAKE_CURRENT_LIST_DIR}/keybind.hh" - "${CMAKE_CURRENT_LIST_DIR}/language.cc" - "${CMAKE_CURRENT_LIST_DIR}/language.hh" - "${CMAKE_CURRENT_LIST_DIR}/listener.cc" - "${CMAKE_CURRENT_LIST_DIR}/listener.hh" - "${CMAKE_CURRENT_LIST_DIR}/main_menu.cc" - "${CMAKE_CURRENT_LIST_DIR}/main_menu.hh" - "${CMAKE_CURRENT_LIST_DIR}/main.cc" - "${CMAKE_CURRENT_LIST_DIR}/message_box.cc" - "${CMAKE_CURRENT_LIST_DIR}/message_box.hh" - "${CMAKE_CURRENT_LIST_DIR}/metrics.cc" - "${CMAKE_CURRENT_LIST_DIR}/metrics.hh" - "${CMAKE_CURRENT_LIST_DIR}/outline.cc" - "${CMAKE_CURRENT_LIST_DIR}/outline.hh" - "${CMAKE_CURRENT_LIST_DIR}/pch.hh" - "${CMAKE_CURRENT_LIST_DIR}/play_menu.cc" - "${CMAKE_CURRENT_LIST_DIR}/play_menu.hh" - "${CMAKE_CURRENT_LIST_DIR}/player_look.cc" - "${CMAKE_CURRENT_LIST_DIR}/player_look.hh" - "${CMAKE_CURRENT_LIST_DIR}/player_move.cc" - "${CMAKE_CURRENT_LIST_DIR}/player_move.hh" - "${CMAKE_CURRENT_LIST_DIR}/player_target.cc" - "${CMAKE_CURRENT_LIST_DIR}/player_target.hh" - "${CMAKE_CURRENT_LIST_DIR}/program.cc" - "${CMAKE_CURRENT_LIST_DIR}/program.hh" - "${CMAKE_CURRENT_LIST_DIR}/progress_bar.cc" - "${CMAKE_CURRENT_LIST_DIR}/progress_bar.hh" - "${CMAKE_CURRENT_LIST_DIR}/receive.cc" - "${CMAKE_CURRENT_LIST_DIR}/receive.hh" - "${CMAKE_CURRENT_LIST_DIR}/scoreboard.cc" - "${CMAKE_CURRENT_LIST_DIR}/scoreboard.hh" - "${CMAKE_CURRENT_LIST_DIR}/screenshot.cc" - "${CMAKE_CURRENT_LIST_DIR}/screenshot.hh" - "${CMAKE_CURRENT_LIST_DIR}/session.cc" - "${CMAKE_CURRENT_LIST_DIR}/session.hh" - "${CMAKE_CURRENT_LIST_DIR}/settings.cc" - "${CMAKE_CURRENT_LIST_DIR}/settings.hh" - "${CMAKE_CURRENT_LIST_DIR}/skybox.cc" - "${CMAKE_CURRENT_LIST_DIR}/skybox.hh" - "${CMAKE_CURRENT_LIST_DIR}/sound_effect.cc" - "${CMAKE_CURRENT_LIST_DIR}/sound_effect.hh" - "${CMAKE_CURRENT_LIST_DIR}/sound_emitter.cc" - "${CMAKE_CURRENT_LIST_DIR}/sound_emitter.hh" - "${CMAKE_CURRENT_LIST_DIR}/sound.cc" - "${CMAKE_CURRENT_LIST_DIR}/sound.hh" - "${CMAKE_CURRENT_LIST_DIR}/splash.cc" - "${CMAKE_CURRENT_LIST_DIR}/splash.hh" - "${CMAKE_CURRENT_LIST_DIR}/status_lines.cc" - "${CMAKE_CURRENT_LIST_DIR}/status_lines.hh" - "${CMAKE_CURRENT_LIST_DIR}/texture_gui.cc" - "${CMAKE_CURRENT_LIST_DIR}/texture_gui.hh" - "${CMAKE_CURRENT_LIST_DIR}/toggles.cc" - "${CMAKE_CURRENT_LIST_DIR}/toggles.hh" - "${CMAKE_CURRENT_LIST_DIR}/voxel_anims.cc" - "${CMAKE_CURRENT_LIST_DIR}/voxel_anims.hh" - "${CMAKE_CURRENT_LIST_DIR}/voxel_atlas.cc" - "${CMAKE_CURRENT_LIST_DIR}/voxel_atlas.hh" - "${CMAKE_CURRENT_LIST_DIR}/voxel_sounds.cc" - "${CMAKE_CURRENT_LIST_DIR}/voxel_sounds.hh" - "${CMAKE_CURRENT_LIST_DIR}/window_title.cc" - "${CMAKE_CURRENT_LIST_DIR}/window_title.hh") -target_compile_features(vclient PUBLIC cxx_std_20) -target_compile_definitions(vclient PUBLIC GLFW_INCLUDE_NONE) -target_include_directories(vclient PUBLIC "${DEPS_INCLUDE_DIR}") -target_include_directories(vclient PRIVATE "${PROJECT_SOURCE_DIR}/src") -target_include_directories(vclient PRIVATE "${PROJECT_SOURCE_DIR}/src/game") -target_precompile_headers(vclient PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") -target_link_libraries(vclient PUBLIC shared dr_libs glad glfw3 imgui imgui_glfw imgui_opengl3 salad) - -if(WIN32 AND MSVC) - # GLFW defines APIENTRY and ENet includes - # Windows API headers which also define APIENTRY - target_compile_options(vclient PRIVATE /wd4005) -endif() - -if(WIN32) - enable_language(RC) - target_sources(vclient PRIVATE "${CMAKE_CURRENT_LIST_DIR}/vclient.rc") -endif() - -install(TARGETS vclient RUNTIME DESTINATION ".") diff --git a/src/game/client/camera.cc b/src/game/client/camera.cc deleted file mode 100644 index 724ae66..0000000 --- a/src/game/client/camera.cc +++ /dev/null @@ -1,107 +0,0 @@ -#include "client/pch.hh" - -#include "client/camera.hh" - -#include "core/angles.hh" -#include "core/config.hh" - -#include "shared/dimension.hh" -#include "shared/head.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" - -#include "client/const.hh" -#include "client/globals.hh" -#include "client/player_move.hh" -#include "client/session.hh" -#include "client/settings.hh" -#include "client/toggles.hh" - -ConfigFloat camera::roll_angle(2.0f, 0.0f, 4.0f); -ConfigFloat camera::vertical_fov(90.0f, 45.0f, 110.0f); -ConfigUnsigned camera::view_distance(16U, 4U, 32U); - -glm::fvec3 camera::angles; -glm::fvec3 camera::direction; -glm::fmat4x4 camera::matrix; -chunk_pos camera::position_chunk; -glm::fvec3 camera::position_local; - -static void reset_camera(void) -{ - camera::angles = glm::fvec3(0.0f, 0.0f, 0.0f); - camera::direction = DIR_FORWARD<float>; - camera::matrix = glm::identity<glm::fmat4x4>(); - camera::position_chunk = chunk_pos(0, 0, 0); - camera::position_local = glm::fvec3(0.0f, 0.0f, 0.0f); -} - -// Gracefully contributed by PQCraft himself in 2024 -// making PlatinumSrc and Voxelius kind of related to each other -static glm::fmat4x4 platinumsrc_viewmatrix(const glm::fvec3& position, const glm::fvec3& angles) -{ - glm::fvec3 forward, up; - cxangles::vectors(angles, &forward, nullptr, &up); - - auto result = glm::identity<glm::fmat4x4>(); - result[0][0] = forward.y * up.z - forward.z * up.y; - result[1][0] = forward.z * up.x - forward.x * up.z; - result[2][0] = forward.x * up.y - forward.y * up.x; - result[3][0] = -result[0][0] * position.x - result[1][0] * position.y - result[2][0] * position.z; - result[0][1] = up.x; - result[1][1] = up.y; - result[2][1] = up.z; - result[3][1] = -up.x * position.x - up.y * position.y - up.z * position.z; - result[0][2] = -forward.x; - result[1][2] = -forward.y; - result[2][2] = -forward.z; - result[3][2] = forward.x * position.x + forward.y * position.y + forward.z * position.z; - return result; -} - -void camera::init(void) -{ - globals::client_config.add_value("camera.roll_angle", camera::roll_angle); - globals::client_config.add_value("camera.vertical_fov", camera::vertical_fov); - globals::client_config.add_value("camera.view_distance", camera::view_distance); - - settings::add_slider(1, camera::vertical_fov, settings_location::GENERAL, "camera.vertical_fov", true, "%.0f"); - settings::add_slider(0, camera::view_distance, settings_location::VIDEO, "camera.view_distance", false); - settings::add_slider(10, camera::roll_angle, settings_location::VIDEO, "camera.roll_angle", true, "%.01f"); - - reset_camera(); -} - -void camera::update(void) -{ - if(!session::is_ingame()) { - reset_camera(); - return; - } - - const auto& head = globals::dimension->entities.get<HeadComponentIntr>(globals::player); - const auto& transform = globals::dimension->entities.get<TransformComponentIntr>(globals::player); - const auto& velocity = globals::dimension->entities.get<VelocityComponent>(globals::player); - - camera::angles = transform.angles + head.angles; - camera::position_chunk = transform.chunk; - camera::position_local = transform.local + head.offset; - - glm::fvec3 right_vector, up_vector; - cxangles::vectors(camera::angles, &camera::direction, &right_vector, &up_vector); - - auto client_angles = camera::angles; - - if(!toggles::get(TOGGLE_PM_FLIGHT)) { - // Apply the quake-like view rolling - client_angles[2] = vx::radians(-camera::roll_angle.get_value() * glm::dot(velocity.value / PMOVE_MAX_SPEED_GROUND, right_vector)); - } - - const auto z_near = 0.01f; - const auto z_far = 1.25f * static_cast<float>(CHUNK_SIZE * camera::view_distance.get_value()); - - auto proj = glm::perspective(vx::radians(camera::vertical_fov.get_value()), globals::aspect, z_near, z_far); - auto view = platinumsrc_viewmatrix(camera::position_local, client_angles); - - camera::matrix = proj * view; -} diff --git a/src/game/client/camera.hh b/src/game/client/camera.hh deleted file mode 100644 index 7cdce3b..0000000 --- a/src/game/client/camera.hh +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CLIENT_CAMERA_HH -#define CLIENT_CAMERA_HH 1 -#pragma once - -#include "shared/types.hh" - -class ConfigFloat; -class ConfigUnsigned; - -namespace camera -{ -extern ConfigFloat roll_angle; -extern ConfigFloat vertical_fov; -extern ConfigUnsigned view_distance; -} // namespace camera - -namespace camera -{ -extern glm::fvec3 angles; -extern glm::fvec3 direction; -extern glm::fmat4x4 matrix; -extern chunk_pos position_chunk; -extern glm::fvec3 position_local; -} // namespace camera - -namespace camera -{ -void init(void); -void update(void); -} // namespace camera - -#endif // CLIENT_CAMERA_HH diff --git a/src/game/client/experiments.cc b/src/game/client/experiments.cc deleted file mode 100644 index 4751208..0000000 --- a/src/game/client/experiments.cc +++ /dev/null @@ -1,77 +0,0 @@ -#include "client/pch.hh" - -#include "client/experiments.hh" - -#include "shared/dimension.hh" -#include "shared/game_items.hh" -#include "shared/game_voxels.hh" -#include "shared/item_registry.hh" - -#include "client/chat.hh" -#include "client/glfw.hh" -#include "client/globals.hh" -#include "client/hotbar.hh" -#include "client/player_target.hh" -#include "client/session.hh" -#include "client/status_lines.hh" - -static void on_glfw_mouse_button(const GlfwMouseButtonEvent& event) -{ - if(!globals::gui_screen && session::is_ingame()) { - if((event.action == GLFW_PRESS) && (player_target::voxel != NULL_VOXEL_ID)) { - if(event.button == GLFW_MOUSE_BUTTON_LEFT) { - experiments::attack(); - return; - } - - if(event.button == GLFW_MOUSE_BUTTON_RIGHT) { - experiments::interact(); - return; - } - } - } -} - -void experiments::init(void) -{ - globals::dispatcher.sink<GlfwMouseButtonEvent>().connect<&on_glfw_mouse_button>(); -} - -void experiments::init_late(void) -{ - hotbar::slots[0] = game_items::cobblestone; - hotbar::slots[1] = game_items::stone; - hotbar::slots[2] = game_items::dirt; - hotbar::slots[3] = game_items::grass; - hotbar::slots[4] = game_items::oak_leaves; - hotbar::slots[5] = game_items::oak_planks; - hotbar::slots[6] = game_items::oak_log; - hotbar::slots[7] = game_items::glass; - hotbar::slots[8] = game_items::slime; -} - -void experiments::shutdown(void) -{ -} - -void experiments::update(void) -{ -} - -void experiments::update_late(void) -{ -} - -void experiments::attack(void) -{ - globals::dimension->set_voxel(NULL_VOXEL_ID, player_target::coord); -} - -void experiments::interact(void) -{ - if(auto info = item_registry::find(hotbar::slots[hotbar::active_slot])) { - if(info->place_voxel != NULL_VOXEL_ID) { - globals::dimension->set_voxel(info->place_voxel, player_target::coord + player_target::normal); - } - } -} diff --git a/src/game/client/factory.cc b/src/game/client/factory.cc deleted file mode 100644 index 4c1c24e..0000000 --- a/src/game/client/factory.cc +++ /dev/null @@ -1,28 +0,0 @@ -#include "client/pch.hh" - -#include "client/factory.hh" - -#include "shared/dimension.hh" -#include "shared/factory.hh" -#include "shared/head.hh" -#include "shared/transform.hh" - -#include "client/globals.hh" -#include "client/sound_emitter.hh" - -void client_factory::create_player(Dimension* dimension, entt::entity entity) -{ - shared_factory::create_player(dimension, entity); - - const auto& head = dimension->entities.get<HeadComponent>(entity); - dimension->entities.emplace_or_replace<HeadComponentIntr>(entity, head); - dimension->entities.emplace_or_replace<HeadComponentPrev>(entity, head); - - const auto& transform = dimension->entities.get<TransformComponent>(entity); - dimension->entities.emplace_or_replace<TransformComponentIntr>(entity, transform); - dimension->entities.emplace_or_replace<TransformComponentPrev>(entity, transform); - - if(globals::sound_ctx) { - dimension->entities.emplace_or_replace<SoundEmitterComponent>(entity); - } -} diff --git a/src/game/client/factory.hh b/src/game/client/factory.hh deleted file mode 100644 index 29a3757..0000000 --- a/src/game/client/factory.hh +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CLIENT_FACTORY_HH -#define CLIENT_FACTORY_HH 1 -#pragma once - -class Dimension; - -namespace client_factory -{ -void create_player(Dimension* dimension, entt::entity entity); -} // namespace client_factory - -#endif // CLIENT_FACTORY_HH diff --git a/src/game/client/interpolation.cc b/src/game/client/interpolation.cc deleted file mode 100644 index 27b6dfd..0000000 --- a/src/game/client/interpolation.cc +++ /dev/null @@ -1,61 +0,0 @@ -#include "client/pch.hh" - -#include "client/interpolation.hh" - -#include "core/constexpr.hh" - -#include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/head.hh" -#include "shared/transform.hh" - -#include "client/globals.hh" - -static void transform_interpolate(float alpha) -{ - auto group = globals::dimension->entities.group<TransformComponentIntr>(entt::get<TransformComponent, TransformComponentPrev>); - - for(auto [entity, interp, current, previous] : group.each()) { - interp.angles[0] = vx::lerp(previous.angles[0], current.angles[0], alpha); - interp.angles[1] = vx::lerp(previous.angles[1], current.angles[1], alpha); - interp.angles[2] = vx::lerp(previous.angles[2], current.angles[2], alpha); - - // Figure out previous chunk-local floating-point coordinates transformed - // to the current WorldCoord's chunk domain coordinates; we're interpolating - // against these instead of using previous.position.local to prevent jittering - auto previous_shift = coord::to_relative(current.chunk, current.local, previous.chunk, previous.local); - auto previous_local = current.local + previous_shift; - - interp.chunk.x = current.chunk.x; - interp.chunk.y = current.chunk.y; - interp.chunk.z = current.chunk.z; - - interp.local.x = vx::lerp(previous_local.x, current.local.x, alpha); - interp.local.y = vx::lerp(previous_local.y, current.local.y, alpha); - interp.local.z = vx::lerp(previous_local.z, current.local.z, alpha); - } -} - -static void head_interpolate(float alpha) -{ - auto group = globals::dimension->entities.group<HeadComponentIntr>(entt::get<HeadComponent, HeadComponentPrev>); - - for(auto [entity, interp, current, previous] : group.each()) { - interp.angles[0] = vx::lerp(previous.angles[0], current.angles[0], alpha); - interp.angles[1] = vx::lerp(previous.angles[1], current.angles[1], alpha); - interp.angles[2] = vx::lerp(previous.angles[2], current.angles[2], alpha); - - interp.offset.x = vx::lerp(previous.offset.x, current.offset.x, alpha); - interp.offset.y = vx::lerp(previous.offset.y, current.offset.y, alpha); - interp.offset.z = vx::lerp(previous.offset.z, current.offset.z, alpha); - } -} - -void interpolation::update(void) -{ - if(globals::dimension) { - auto alpha = static_cast<float>(globals::fixed_accumulator) / static_cast<float>(globals::fixed_frametime_us); - transform_interpolate(alpha); - head_interpolate(alpha); - } -}
\ No newline at end of file diff --git a/src/game/client/listener.cc b/src/game/client/listener.cc deleted file mode 100644 index 6b691eb..0000000 --- a/src/game/client/listener.cc +++ /dev/null @@ -1,38 +0,0 @@ -#include "client/pch.hh" - -#include "client/listener.hh" - -#include "core/config.hh" -#include "core/constexpr.hh" - -#include "shared/dimension.hh" -#include "shared/velocity.hh" - -#include "client/camera.hh" -#include "client/const.hh" -#include "client/globals.hh" -#include "client/session.hh" -#include "client/sound.hh" - -void listener::update(void) -{ - if(session::is_ingame()) { - const auto& velocity = globals::dimension->entities.get<VelocityComponent>(globals::player).value; - const auto& position = camera::position_local; - - alListener3f(AL_POSITION, position.x, position.y, position.z); - alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z); - - float orientation[6]; - orientation[0] = camera::direction.x; - orientation[1] = camera::direction.y; - orientation[2] = camera::direction.z; - orientation[3] = DIR_UP<float>.x; - orientation[4] = DIR_UP<float>.y; - orientation[5] = DIR_UP<float>.z; - - alListenerfv(AL_ORIENTATION, orientation); - } - - alListenerf(AL_GAIN, vx::clamp(sound::volume_master.get_value() * 0.01f, 0.0f, 1.0f)); -} diff --git a/src/game/client/player_target.cc b/src/game/client/player_target.cc deleted file mode 100644 index 9c60c00..0000000 --- a/src/game/client/player_target.cc +++ /dev/null @@ -1,66 +0,0 @@ -#include "client/pch.hh" - -#include "client/player_target.hh" - -#include "shared/coord.hh" -#include "shared/dimension.hh" -#include "shared/ray_dda.hh" - -#include "client/camera.hh" -#include "client/game.hh" -#include "client/globals.hh" -#include "client/outline.hh" -#include "client/session.hh" - -constexpr static float MAX_REACH = 16.0f; - -voxel_id player_target::voxel; -voxel_pos player_target::coord; -voxel_pos player_target::normal; -const VoxelInfo* player_target::info; - -void player_target::init(void) -{ - player_target::voxel = NULL_VOXEL_ID; - player_target::coord = voxel_pos(); - player_target::normal = voxel_pos(); - player_target::info = nullptr; -} - -void player_target::update(void) -{ - if(session::is_ingame()) { - RayDDA ray(globals::dimension, camera::position_chunk, camera::position_local, camera::direction); - - do { - player_target::voxel = ray.step(); - - if(player_target::voxel != NULL_VOXEL_ID) { - player_target::coord = ray.vpos; - player_target::normal = ray.vnormal; - player_target::info = voxel_registry::find(player_target::voxel); - break; - } - - player_target::coord = voxel_pos(); - player_target::normal = voxel_pos(); - player_target::info = nullptr; - } while(ray.distance < MAX_REACH); - } else { - player_target::voxel = NULL_VOXEL_ID; - player_target::coord = voxel_pos(); - player_target::normal = voxel_pos(); - player_target::info = nullptr; - } -} - -void player_target::render(void) -{ - if((player_target::voxel != NULL_VOXEL_ID) && !client_game::hide_hud) { - auto cpos = coord::to_chunk(player_target::coord); - auto fpos = coord::to_local(player_target::coord); - - outline::prepare(); - outline::cube(cpos, glm::fvec3(fpos), glm::fvec3(1.0f), 2.0f, glm::fvec4(0.0f, 0.0f, 0.0f, 1.0f)); - } -} diff --git a/src/game/client/settings.hh b/src/game/client/settings.hh deleted file mode 100644 index 180f384..0000000 --- a/src/game/client/settings.hh +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef CLIENT_SETTINGS_HH -#define CLIENT_SETTINGS_HH 1 -#pragma once - -class ConfigBoolean; -class ConfigString; - -class ConfigInt; -class ConfigFloat; -class ConfigUnsigned; - -class ConfigKeyBind; -class ConfigGamepadAxis; -class ConfigGamepadButton; - -enum class settings_location : unsigned int { - GENERAL = 0x0000U, - KEYBOARD_MOVEMENT = 0x0001U, - KEYBOARD_GAMEPLAY = 0x0002U, - KEYBOARD_MISC = 0x0003U, - GAMEPAD = 0x0004U, - GAMEPAD_MOVEMENT = 0x0005U, - GAMEPAD_GAMEPLAY = 0x0006U, - GAMEPAD_MISC = 0x0007U, - MOUSE = 0x0008U, - VIDEO = 0x0009U, - VIDEO_GUI = 0x000AU, - SOUND = 0x000BU, - SOUND_LEVELS = 0x000CU, - COUNT = 0x000DU, -}; - -namespace settings -{ -void init(void); -void init_late(void); -void shutdown(void); -void layout(void); -} // namespace settings - -namespace settings -{ -void add_checkbox(int priority, ConfigBoolean& value, settings_location location, const char* name, bool tooltip); -} // namespace settings - -namespace settings -{ -void add_input(int priority, ConfigInt& value, settings_location location, const char* name, bool tooltip); -void add_input(int priority, ConfigFloat& value, settings_location location, const char* name, bool tooltip, const char* format = "%.3f"); -void add_input(int priority, ConfigUnsigned& value, settings_location location, const char* name, bool tooltip); -void add_input(int priority, ConfigString& value, settings_location location, const char* name, bool tooltip, bool allow_whitespace); -} // namespace settings - -namespace settings -{ -void add_slider(int priority, ConfigInt& value, settings_location location, const char* name, bool tooltip); -void add_slider(int priority, ConfigFloat& value, settings_location location, const char* name, bool tooltip, const char* format = "%.3f"); -void add_slider(int priority, ConfigUnsigned& value, settings_location location, const char* name, bool tooltip); -} // namespace settings - -namespace settings -{ -void add_stepper(int priority, ConfigInt& value, settings_location location, const char* name, bool tooltip); -void add_stepper(int priority, ConfigUnsigned& value, settings_location location, const char* name, bool tooltip); -} // namespace settings - -namespace settings -{ -void add_keybind(int priority, ConfigKeyBind& value, settings_location location, const char* name); -} // namespace settings - -namespace settings -{ -void add_gamepad_axis(int priority, ConfigGamepadAxis& value, settings_location location, const char* name); -void add_gamepad_button(int priority, ConfigGamepadButton& value, settings_location location, const char* name); -} // namespace settings - -namespace settings -{ -void add_language_select(int priority, settings_location location, const char* name); -} // namespace settings - -#endif // CLIENT_SETTINGS_HH diff --git a/src/game/client/skybox.cc b/src/game/client/skybox.cc deleted file mode 100644 index 8578f0e..0000000 --- a/src/game/client/skybox.cc +++ /dev/null @@ -1,11 +0,0 @@ -#include "client/pch.hh" - -#include "client/skybox.hh" - -glm::fvec3 skybox::fog_color; - -void skybox::init(void) -{ - // https://convertingcolors.com/hex-color-B1F3FF.html - skybox::fog_color = glm::fvec3(0.690f, 0.950f, 1.000f); -} diff --git a/src/game/client/voxel_anims.cc b/src/game/client/voxel_anims.cc deleted file mode 100644 index 4e0f168..0000000 --- a/src/game/client/voxel_anims.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "client/pch.hh" - -#include "client/voxel_anims.hh" - -#include "core/config.hh" -#include "core/constexpr.hh" - -#include "client/globals.hh" - -static ConfigUnsigned base_framerate(16U, 1U, 16U); - -std::uint64_t voxel_anims::nextframe = 0U; -std::uint32_t voxel_anims::frame = 0U; - -void voxel_anims::init(void) -{ - globals::client_config.add_value("voxel_anims.base_framerate", base_framerate); - - voxel_anims::nextframe = 0U; - voxel_anims::frame = 0U; -} - -void voxel_anims::update(void) -{ - if(globals::curtime >= voxel_anims::nextframe) { - voxel_anims::nextframe = globals::curtime + static_cast<std::uint64_t>(1000000.0 / static_cast<float>(base_framerate.get_value())); - voxel_anims::frame += 1U; - } -} diff --git a/src/game/server/unloader.cc b/src/game/server/unloader.cc deleted file mode 100644 index f986a61..0000000 --- a/src/game/server/unloader.cc +++ /dev/null @@ -1,76 +0,0 @@ -#include "server/pch.hh" - -#include "server/unloader.hh" - -#include "core/config.hh" - -#include "shared/chunk.hh" -#include "shared/chunk_aabb.hh" -#include "shared/dimension.hh" -#include "shared/player.hh" -#include "shared/transform.hh" - -#include "server/game.hh" -#include "server/globals.hh" -#include "server/inhabited.hh" -#include "server/universe.hh" - -static void on_chunk_update(const ChunkUpdateEvent& event) -{ - event.dimension->chunks.emplace_or_replace<InhabitedComponent>(event.chunk->get_entity()); -} - -static void on_voxel_set(const VoxelSetEvent& event) -{ - event.dimension->chunks.emplace_or_replace<InhabitedComponent>(event.chunk->get_entity()); -} - -void unloader::init(void) -{ - globals::dispatcher.sink<ChunkUpdateEvent>().connect<&on_chunk_update>(); - globals::dispatcher.sink<VoxelSetEvent>().connect<&on_voxel_set>(); -} - -void unloader::init_late(void) -{ -} - -void unloader::fixed_update_late(Dimension* dimension) -{ - auto group = dimension->entities.group(entt::get<PlayerComponent, TransformComponent>); - auto boxes = std::vector<ChunkAABB>(); - - for(const auto [entity, transform] : group.each()) { - ChunkAABB aabb; - aabb.min = transform.chunk - static_cast<chunk_pos::value_type>(server_game::view_distance.get_value()); - aabb.max = transform.chunk + static_cast<chunk_pos::value_type>(server_game::view_distance.get_value()); - boxes.push_back(aabb); - } - - auto view = dimension->chunks.view<ChunkComponent>(); - auto chunk_in_view = false; - - for(const auto [entity, chunk] : view.each()) { - chunk_in_view = false; - - for(const auto& aabb : boxes) { - if(aabb.contains(chunk.cpos)) { - chunk_in_view = true; - break; - } - } - - if(chunk_in_view) { - // The chunk is within view box of at least - // a single player; we shouldn't unload it now - continue; - } - - if(dimension->chunks.any_of<InhabitedComponent>(entity)) { - // Only store inhabited chunks on disk - universe::save_chunk(dimension, chunk.cpos); - } - - dimension->remove_chunk(entity); - } -} diff --git a/src/game/shared/CMakeLists.txt b/src/game/shared/CMakeLists.txt deleted file mode 100644 index d72a6e8..0000000 --- a/src/game/shared/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -add_library(shared STATIC - "${CMAKE_CURRENT_LIST_DIR}/chunk_aabb.cc" - "${CMAKE_CURRENT_LIST_DIR}/chunk_aabb.hh" - "${CMAKE_CURRENT_LIST_DIR}/chunk.cc" - "${CMAKE_CURRENT_LIST_DIR}/chunk.hh" - "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" - "${CMAKE_CURRENT_LIST_DIR}/collision.cc" - "${CMAKE_CURRENT_LIST_DIR}/collision.hh" - "${CMAKE_CURRENT_LIST_DIR}/const.hh" - "${CMAKE_CURRENT_LIST_DIR}/coord.hh" - "${CMAKE_CURRENT_LIST_DIR}/dimension.cc" - "${CMAKE_CURRENT_LIST_DIR}/dimension.hh" - "${CMAKE_CURRENT_LIST_DIR}/factory.cc" - "${CMAKE_CURRENT_LIST_DIR}/factory.hh" - "${CMAKE_CURRENT_LIST_DIR}/feature.cc" - "${CMAKE_CURRENT_LIST_DIR}/feature.hh" - "${CMAKE_CURRENT_LIST_DIR}/game_items.cc" - "${CMAKE_CURRENT_LIST_DIR}/game_items.hh" - "${CMAKE_CURRENT_LIST_DIR}/game_voxels.cc" - "${CMAKE_CURRENT_LIST_DIR}/game_voxels.hh" - "${CMAKE_CURRENT_LIST_DIR}/game.cc" - "${CMAKE_CURRENT_LIST_DIR}/game.hh" - "${CMAKE_CURRENT_LIST_DIR}/globals.cc" - "${CMAKE_CURRENT_LIST_DIR}/globals.hh" - "${CMAKE_CURRENT_LIST_DIR}/gravity.cc" - "${CMAKE_CURRENT_LIST_DIR}/gravity.hh" - "${CMAKE_CURRENT_LIST_DIR}/grounded.hh" - "${CMAKE_CURRENT_LIST_DIR}/head.hh" - "${CMAKE_CURRENT_LIST_DIR}/item_registry.cc" - "${CMAKE_CURRENT_LIST_DIR}/item_registry.hh" - "${CMAKE_CURRENT_LIST_DIR}/pch.hh" - "${CMAKE_CURRENT_LIST_DIR}/player.hh" - "${CMAKE_CURRENT_LIST_DIR}/protocol.cc" - "${CMAKE_CURRENT_LIST_DIR}/protocol.hh" - "${CMAKE_CURRENT_LIST_DIR}/ray_dda.cc" - "${CMAKE_CURRENT_LIST_DIR}/ray_dda.hh" - "${CMAKE_CURRENT_LIST_DIR}/splash.cc" - "${CMAKE_CURRENT_LIST_DIR}/splash.hh" - "${CMAKE_CURRENT_LIST_DIR}/stasis.cc" - "${CMAKE_CURRENT_LIST_DIR}/stasis.hh" - "${CMAKE_CURRENT_LIST_DIR}/threading.cc" - "${CMAKE_CURRENT_LIST_DIR}/threading.hh" - "${CMAKE_CURRENT_LIST_DIR}/transform.cc" - "${CMAKE_CURRENT_LIST_DIR}/transform.hh" - "${CMAKE_CURRENT_LIST_DIR}/types.hh" - "${CMAKE_CURRENT_LIST_DIR}/velocity.cc" - "${CMAKE_CURRENT_LIST_DIR}/velocity.hh" - "${CMAKE_CURRENT_LIST_DIR}/voxel_registry.cc" - "${CMAKE_CURRENT_LIST_DIR}/voxel_registry.hh" - "${CMAKE_CURRENT_LIST_DIR}/voxel_storage.cc" - "${CMAKE_CURRENT_LIST_DIR}/voxel_storage.hh") -target_compile_features(shared PUBLIC cxx_std_20) -target_include_directories(shared PUBLIC "${DEPS_INCLUDE_DIR}") -target_include_directories(shared PRIVATE "${PROJECT_SOURCE_DIR}/src") -target_include_directories(shared PRIVATE "${PROJECT_SOURCE_DIR}/src/game") -target_precompile_headers(shared PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pch.hh") -target_link_libraries(shared PUBLIC core enet entt FNL miniz parson thread_pool) diff --git a/src/game/shared/chunk.cc b/src/game/shared/chunk.cc deleted file mode 100644 index da798fa..0000000 --- a/src/game/shared/chunk.cc +++ /dev/null @@ -1,69 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/chunk.hh" - -#include "shared/coord.hh" - -Chunk::Chunk(entt::entity entity, Dimension* dimension) -{ - m_entity = entity; - m_dimension = dimension; - m_voxels.fill(NULL_VOXEL_ID); - m_biome = BIOME_VOID; -} - -voxel_id Chunk::get_voxel(const local_pos& lpos) const -{ - return get_voxel(coord::to_index(lpos)); -} - -voxel_id Chunk::get_voxel(const std::size_t index) const -{ - if(index >= CHUNK_VOLUME) { - return NULL_VOXEL_ID; - } else { - return m_voxels[index]; - } -} - -void Chunk::set_voxel(voxel_id voxel, const local_pos& lpos) -{ - set_voxel(voxel, coord::to_index(lpos)); -} - -void Chunk::set_voxel(voxel_id voxel, const std::size_t index) -{ - if(index < CHUNK_VOLUME) { - m_voxels[index] = voxel; - } -} - -const VoxelStorage& Chunk::get_voxels(void) const -{ - return m_voxels; -} - -void Chunk::set_voxels(const VoxelStorage& voxels) -{ - m_voxels = voxels; -} - -unsigned int Chunk::get_biome(void) const -{ - return m_biome; -} - -void Chunk::set_biome(unsigned int biome) -{ - m_biome = biome; -} - -entt::entity Chunk::get_entity(void) const -{ - return m_entity; -} - -Dimension* Chunk::get_dimension(void) const -{ - return m_dimension; -} diff --git a/src/game/shared/collision.hh b/src/game/shared/collision.hh deleted file mode 100644 index 1d2b358..0000000 --- a/src/game/shared/collision.hh +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef SHARED_COLLISION_HH -#define SHARED_COLLISION_HH 1 -#pragma once - -#include "core/aabb.hh" - -class Dimension; - -struct CollisionComponent final { - AABB aabb; - -public: - // NOTE: CollisionComponent::fixed_update must be called - // before TransformComponent::fixed_update and VelocityComponent::fixed_update - // because both transform and velocity may be updated internally - static void fixed_update(Dimension* dimension); -}; - -#endif // SHARED_COLLISION_HH diff --git a/src/game/shared/factory.cc b/src/game/shared/factory.cc deleted file mode 100644 index ad65928..0000000 --- a/src/game/shared/factory.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/factory.hh" - -#include "shared/collision.hh" -#include "shared/dimension.hh" -#include "shared/globals.hh" -#include "shared/gravity.hh" -#include "shared/head.hh" -#include "shared/player.hh" -#include "shared/transform.hh" -#include "shared/velocity.hh" - -void shared_factory::create_player(Dimension* dimension, entt::entity entity) -{ - spdlog::debug("factory[{}]: assigning player components to {}", dimension->get_name(), static_cast<std::uint64_t>(entity)); - - auto& collision = dimension->entities.emplace_or_replace<CollisionComponent>(entity); - collision.aabb.min = glm::fvec3(-0.4f, -1.6f, -0.4f); - collision.aabb.max = glm::fvec3(+0.4f, +0.2f, +0.4f); - - auto& head = dimension->entities.emplace_or_replace<HeadComponent>(entity); - head.angles = glm::fvec3(0.0f, 0.0f, 0.0f); - head.offset = glm::fvec3(0.0f, 0.0f, 0.0f); - - dimension->entities.emplace_or_replace<PlayerComponent>(entity); - - auto& transform = dimension->entities.emplace_or_replace<TransformComponent>(entity); - transform.chunk = chunk_pos(0, 2, 0); - transform.local = glm::fvec3(0.0f, 0.0f, 0.0f); - transform.angles = glm::fvec3(0.0f, 0.0f, 0.0f); - - auto& velocity = dimension->entities.emplace_or_replace<VelocityComponent>(entity); - velocity.value = glm::fvec3(0.0f, 0.0f, 0.0f); -} diff --git a/src/game/shared/factory.hh b/src/game/shared/factory.hh deleted file mode 100644 index ff39a00..0000000 --- a/src/game/shared/factory.hh +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SHARED_FACTORY -#define SHARED_FACTORY 1 -#pragma once - -class Dimension; - -namespace shared_factory -{ -void create_player(Dimension* dimension, entt::entity entity); -} // namespace shared_factory - -#endif // SHARED_FACTORY diff --git a/src/game/shared/game_items.cc b/src/game/shared/game_items.cc deleted file mode 100644 index 95fa6fc..0000000 --- a/src/game/shared/game_items.cc +++ /dev/null @@ -1,61 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/game_items.hh" - -#include "shared/game_voxels.hh" -#include "shared/item_registry.hh" - -item_id game_items::stone = NULL_ITEM_ID; -item_id game_items::cobblestone = NULL_ITEM_ID; -item_id game_items::dirt = NULL_ITEM_ID; -item_id game_items::grass = NULL_ITEM_ID; -item_id game_items::oak_leaves = NULL_ITEM_ID; -item_id game_items::oak_planks = NULL_ITEM_ID; -item_id game_items::oak_log = NULL_ITEM_ID; -item_id game_items::glass = NULL_ITEM_ID; -item_id game_items::slime = NULL_ITEM_ID; -item_id game_items::mud = NULL_ITEM_ID; - -void game_items::populate(void) -{ - // Stone; a hardened slate rock - game_items::stone = - item_registry::construct("stone").set_texture("textures/item/stone.png").set_place_voxel(game_voxels::stone).build(); - - // Cobblestone; a bunch of small stones - game_items::cobblestone = - item_registry::construct("cobblestone") - .set_texture("textures/item/cobblestone.png") - .set_place_voxel(game_voxels::cobblestone) - .build(); - - // Dirt; it's very dirty - game_items::dirt = item_registry::construct("dirt").set_texture("textures/item/dirt.png").set_place_voxel(game_voxels::dirt).build(); - - // Grass; literally just grassy dirt - game_items::grass = - item_registry::construct("grass").set_texture("textures/item/grass.png").set_place_voxel(game_voxels::grass).build(); - - // Oak leaves; they're bushy! - game_items::oak_leaves = - item_registry::construct("oak_leaves").set_texture("textures/item/oak_leaves.png").set_place_voxel(game_voxels::oak_leaves).build(); - - // Oak planks; watch for splinters! - game_items::oak_planks = - item_registry::construct("oak_planks").set_texture("textures/item/oak_planks.png").set_place_voxel(game_voxels::oak_planks).build(); - - // Oak log; a big wad of wood - game_items::oak_log = - item_registry::construct("oak_log").set_texture("textures/item/oak_log.png").set_place_voxel(game_voxels::oak_log).build(); - - // Glass; used for windowing - game_items::glass = - item_registry::construct("glass").set_texture("textures/item/glass.png").set_place_voxel(game_voxels::glass).build(); - - // Slime; it's bouncy! - game_items::slime = - item_registry::construct("slime").set_texture("textures/item/slime.png").set_place_voxel(game_voxels::slime).build(); - - // Mud; you sink in it! - game_items::mud = item_registry::construct("mud").set_texture("textures/item/mud.png").build(); -} diff --git a/src/game/shared/gravity.cc b/src/game/shared/gravity.cc deleted file mode 100644 index 068f658..0000000 --- a/src/game/shared/gravity.cc +++ /dev/null @@ -1,18 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/gravity.hh" - -#include "shared/dimension.hh" -#include "shared/globals.hh" -#include "shared/stasis.hh" -#include "shared/velocity.hh" - -void GravityComponent::fixed_update(Dimension* dimension) -{ - auto fixed_acceleration = globals::fixed_frametime * dimension->get_gravity(); - auto group = dimension->entities.group<GravityComponent>(entt::get<VelocityComponent>, entt::exclude<StasisComponent>); - - for(auto [entity, velocity] : group.each()) { - velocity.value.y += fixed_acceleration; - } -} diff --git a/src/game/shared/gravity.hh b/src/game/shared/gravity.hh deleted file mode 100644 index 19387c1..0000000 --- a/src/game/shared/gravity.hh +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef SHARED_GRAVITY_HH -#define SHARED_GRAVITY_HH 1 -#pragma once - -class Dimension; - -struct GravityComponent final { -public: - static void fixed_update(Dimension* dimension); -}; - -#endif // SHARED_GRAVITY_HH diff --git a/src/game/shared/grounded.hh b/src/game/shared/grounded.hh deleted file mode 100644 index a30b1dc..0000000 --- a/src/game/shared/grounded.hh +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SHARED_GROUNDED -#define SHARED_GROUNDED 1 -#pragma once - -#include "shared/voxel_registry.hh" - -// Assigned to entities which are grounded -// according to the collision and gravity system -struct GroundedComponent final { - voxel_surface surface; -}; - -#endif // SHARED_GROUNDED diff --git a/src/game/shared/head.hh b/src/game/shared/head.hh deleted file mode 100644 index 41304ef..0000000 --- a/src/game/shared/head.hh +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SHARED_HEAD_HH -#define SHARED_HEAD_HH 1 -#pragma once - -struct HeadComponent { - glm::fvec3 angles; - glm::fvec3 offset; -}; - -// Client-side only - interpolated and previous head -struct HeadComponentIntr final : public HeadComponent {}; -struct HeadComponentPrev final : public HeadComponent {}; - -#endif // SHARED_HEAD_HH diff --git a/src/game/shared/item_registry.cc b/src/game/shared/item_registry.cc deleted file mode 100644 index 1263201..0000000 --- a/src/game/shared/item_registry.cc +++ /dev/null @@ -1,103 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/item_registry.hh" - -#include "core/crc64.hh" - -#include "shared/voxel_registry.hh" - -std::unordered_map<std::string, ItemInfoBuilder> item_registry::builders = {}; -std::unordered_map<std::string, item_id> item_registry::names = {}; -std::vector<std::shared_ptr<ItemInfo>> item_registry::items = {}; - -ItemInfoBuilder::ItemInfoBuilder(const char* name) -{ - prototype.name = name; - prototype.texture = std::string(); - prototype.place_voxel = NULL_VOXEL_ID; - prototype.cached_texture = nullptr; -} - -ItemInfoBuilder& ItemInfoBuilder::set_texture(const char* texture) -{ - prototype.texture = texture; - prototype.cached_texture = nullptr; - return *this; -} - -ItemInfoBuilder& ItemInfoBuilder::set_place_voxel(voxel_id place_voxel) -{ - prototype.place_voxel = place_voxel; - return *this; -} - -item_id ItemInfoBuilder::build(void) const -{ - const auto it = item_registry::names.find(prototype.name); - - if(it != item_registry::names.cend()) { - spdlog::warn("item_registry: cannot build {}: name already present", prototype.name); - return it->second; - } - - auto new_info = std::make_shared<ItemInfo>(); - new_info->name = prototype.name; - new_info->texture = prototype.texture; - new_info->place_voxel = prototype.place_voxel; - new_info->cached_texture = nullptr; - - item_registry::items.push_back(new_info); - item_registry::names.insert_or_assign(prototype.name, static_cast<item_id>(item_registry::items.size())); - - return static_cast<item_id>(item_registry::items.size()); -} - -ItemInfoBuilder& item_registry::construct(const char* name) -{ - const auto it = item_registry::builders.find(name); - - if(it != item_registry::builders.cend()) { - return it->second; - } else { - return item_registry::builders.emplace(name, ItemInfoBuilder(name)).first->second; - } -} - -ItemInfo* item_registry::find(const char* name) -{ - const auto it = item_registry::names.find(name); - - if(it != item_registry::names.cend()) { - return item_registry::find(it->second); - } else { - return nullptr; - } -} - -ItemInfo* item_registry::find(const item_id item) -{ - if((item != NULL_ITEM_ID) && (item <= item_registry::items.size())) { - return item_registry::items[item - 1].get(); - } else { - return nullptr; - } -} - -void item_registry::purge(void) -{ - item_registry::builders.clear(); - item_registry::names.clear(); - item_registry::items.clear(); -} - -std::uint64_t item_registry::calcualte_checksum(void) -{ - std::uint64_t result = 0; - - for(const auto& info : item_registry::items) { - result = crc64::get(info->name, result); - result += static_cast<std::uint64_t>(info->place_voxel); - } - - return result; -} diff --git a/src/game/shared/player.hh b/src/game/shared/player.hh deleted file mode 100644 index 4f862d3..0000000 --- a/src/game/shared/player.hh +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef SHARED_PLAYER_HH -#define SHARED_PLAYER_HH 1 -#pragma once - -struct PlayerComponent final {}; - -#endif // SHARED_PLAYER_HH diff --git a/src/game/shared/stasis.cc b/src/game/shared/stasis.cc deleted file mode 100644 index 462871d..0000000 --- a/src/game/shared/stasis.cc +++ /dev/null @@ -1,19 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/stasis.hh" - -#include "shared/dimension.hh" -#include "shared/transform.hh" - -void StasisComponent::fixed_update(Dimension* dimension) -{ - auto view = dimension->entities.view<TransformComponent>(); - - for(auto [entity, transform] : view.each()) { - if(dimension->find_chunk(transform.chunk)) { - dimension->entities.remove<StasisComponent>(entity); - } else { - dimension->entities.emplace_or_replace<StasisComponent>(entity); - } - } -} diff --git a/src/game/shared/stasis.hh b/src/game/shared/stasis.hh deleted file mode 100644 index 2907cfb..0000000 --- a/src/game/shared/stasis.hh +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SHARED_STASIS_HH -#define SHARED_STASIS_HH 1 -#pragma once - -class Dimension; - -// Attached to entities with transform values -// out of bounds in a specific dimension -struct StasisComponent final { -public: - static void fixed_update(Dimension* dimension); -}; - -#endif // SHARED_STASIS_HH diff --git a/src/game/shared/transform.hh b/src/game/shared/transform.hh deleted file mode 100644 index 1a1be0e..0000000 --- a/src/game/shared/transform.hh +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef SHARED_TRANSFORM_HH -#define SHARED_TRANSFORM_HH 1 -#pragma once - -#include "shared/types.hh" - -class Dimension; - -struct TransformComponent { - chunk_pos chunk; - glm::fvec3 local; - glm::fvec3 angles; - -public: - // Updates TransformComponent values so that - // the local translation field is always within - // local coodrinates; [floating-point precision] - static void fixed_update(Dimension* dimension); -}; - -// Client-side only - interpolated and previous transform -struct TransformComponentIntr final : public TransformComponent {}; -struct TransformComponentPrev final : public TransformComponent {}; - -#endif // SHARED_TRANSFORM_HH diff --git a/src/game/shared/velocity.cc b/src/game/shared/velocity.cc deleted file mode 100644 index 329dc91..0000000 --- a/src/game/shared/velocity.cc +++ /dev/null @@ -1,17 +0,0 @@ -#include "shared/pch.hh" - -#include "shared/velocity.hh" - -#include "shared/dimension.hh" -#include "shared/globals.hh" -#include "shared/stasis.hh" -#include "shared/transform.hh" - -void VelocityComponent::fixed_update(Dimension* dimension) -{ - auto group = dimension->entities.group<VelocityComponent>(entt::get<TransformComponent>, entt::exclude<StasisComponent>); - - for(auto [entity, velocity, transform] : group.each()) { - transform.local += velocity.value * globals::fixed_frametime; - } -} diff --git a/src/game/shared/velocity.hh b/src/game/shared/velocity.hh deleted file mode 100644 index 17da420..0000000 --- a/src/game/shared/velocity.hh +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SHARED_VELOCITY_HH -#define SHARED_VELOCITY_HH 1 -#pragma once - -class Dimension; - -struct VelocityComponent final { - glm::fvec3 value; - -public: - // Updates entities TransformComponent values - // according to velocities multiplied by fixed_frametime. - // NOTE: This system was previously called inertial - static void fixed_update(Dimension* dimension); -}; - -#endif // SHARED_VELOCITY_HH |
