summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--.gitattributes2
-rw-r--r--.vscode/c_cpp_properties.json9
-rw-r--r--CMakeLists.txt3
-rw-r--r--core/CMakeLists.txt22
-rw-r--r--core/config/CMakeLists.txt7
-rw-r--r--core/config/boolean.cc46
-rw-r--r--core/config/boolean.hh29
-rw-r--r--core/config/ivalue.hh15
-rw-r--r--core/config/number.hh129
-rw-r--r--core/config/string.cc18
-rw-r--r--core/config/string.hh22
-rw-r--r--core/io/CMakeLists.txt7
-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.hh33
-rw-r--r--core/math/CMakeLists.txt11
-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.hh12
-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.hh47
-rw-r--r--core/pch.hh (renamed from src/core/pch.hh)0
-rw-r--r--core/resource/CMakeLists.txt6
-rw-r--r--core/resource/binfile.cc (renamed from src/core/binfile.cc)4
-rw-r--r--core/resource/binfile.hh10
-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.txt5
-rw-r--r--core/utils/epoch.cc (renamed from src/core/epoch.cc)14
-rw-r--r--core/utils/epoch.hh19
-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.cc12
-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.h12561
-rw-r--r--deps/dr_libs/src/dr_impl.c3
-rw-r--r--game/CMakeLists.txt (renamed from src/game/CMakeLists.txt)8
-rw-r--r--game/client/CMakeLists.txt47
-rw-r--r--game/client/config/CMakeLists.txt7
-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.txt15
-rw-r--r--game/client/entity/camera.cc110
-rw-r--r--game/client/entity/camera.hh35
-rw-r--r--game/client/entity/factory.cc29
-rw-r--r--game/client/entity/factory.hh15
-rw-r--r--game/client/entity/interpolation.cc63
-rw-r--r--game/client/entity/interpolation.hh (renamed from src/game/client/interpolation.hh)4
-rw-r--r--game/client/entity/listener.cc39
-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.cc79
-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.txt38
-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.hh93
-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.txt4
-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.txt5
-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.txt3
-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)bin54361 -> 54361 bytes
-rw-r--r--game/client/vclient.rc (renamed from src/game/client/vclient.rc)0
-rw-r--r--game/client/world/CMakeLists.txt21
-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.cc68
-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.cc11
-rw-r--r--game/client/world/skybox.hh (renamed from src/game/client/skybox.hh)8
-rw-r--r--game/client/world/voxel_anims.cc31
-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)bin60201 -> 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.txt10
-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.cc77
-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.txt27
-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.txt16
-rw-r--r--game/shared/entity/collision.cc (renamed from src/game/shared/collision.cc)77
-rw-r--r--game/shared/entity/collision.hh25
-rw-r--r--game/shared/entity/factory.cc36
-rw-r--r--game/shared/entity/factory.hh15
-rw-r--r--game/shared/entity/gravity.cc19
-rw-r--r--game/shared/entity/gravity.hh18
-rw-r--r--game/shared/entity/grounded.hh16
-rw-r--r--game/shared/entity/head.hh20
-rw-r--r--game/shared/entity/player.hh10
-rw-r--r--game/shared/entity/stasis.cc19
-rw-r--r--game/shared/entity/stasis.hh20
-rw-r--r--game/shared/entity/transform.cc (renamed from src/game/shared/transform.cc)11
-rw-r--r--game/shared/entity/transform.hh34
-rw-r--r--game/shared/entity/velocity.cc18
-rw-r--r--game/shared/entity/velocity.hh23
-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.cc69
-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.txt17
-rw-r--r--game/shared/world/chunk.cc69
-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.cc103
-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.txt2
-rw-r--r--src/core/CMakeLists.txt43
-rw-r--r--src/core/binfile.hh10
-rw-r--r--src/core/config.hh181
-rw-r--r--src/core/crc64.hh12
-rw-r--r--src/core/epoch.hh19
-rw-r--r--src/core/vectors.hh47
-rw-r--r--src/game/client/CMakeLists.txt126
-rw-r--r--src/game/client/camera.cc107
-rw-r--r--src/game/client/camera.hh32
-rw-r--r--src/game/client/experiments.cc77
-rw-r--r--src/game/client/factory.cc28
-rw-r--r--src/game/client/factory.hh12
-rw-r--r--src/game/client/interpolation.cc61
-rw-r--r--src/game/client/listener.cc38
-rw-r--r--src/game/client/player_target.cc66
-rw-r--r--src/game/client/settings.hh83
-rw-r--r--src/game/client/skybox.cc11
-rw-r--r--src/game/client/voxel_anims.cc29
-rw-r--r--src/game/server/unloader.cc76
-rw-r--r--src/game/shared/CMakeLists.txt57
-rw-r--r--src/game/shared/chunk.cc69
-rw-r--r--src/game/shared/collision.hh19
-rw-r--r--src/game/shared/factory.cc35
-rw-r--r--src/game/shared/factory.hh12
-rw-r--r--src/game/shared/game_items.cc61
-rw-r--r--src/game/shared/gravity.cc18
-rw-r--r--src/game/shared/gravity.hh12
-rw-r--r--src/game/shared/grounded.hh13
-rw-r--r--src/game/shared/head.hh14
-rw-r--r--src/game/shared/item_registry.cc103
-rw-r--r--src/game/shared/player.hh7
-rw-r--r--src/game/shared/stasis.cc19
-rw-r--r--src/game/shared/stasis.hh14
-rw-r--r--src/game/shared/transform.hh25
-rw-r--r--src/game/shared/velocity.cc17
-rw-r--r--src/game/shared/velocity.hh17
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
index 5f3ccd2..5f3ccd2 100644
--- a/src/game/client/vclient.ico
+++ b/game/client/vclient.ico
Binary files differ
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
index 02ff006..02ff006 100644
--- a/src/game/server/vserver.ico
+++ b/game/server/vserver.ico
Binary files differ
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