]> git.bts.cx Git - benzene.git/commitdiff
Initial version
authorBen Sherratt <redacted>
Sun, 12 May 2024 12:01:16 +0000 (13:01 +0100)
committerBen Sherratt <redacted>
Sun, 12 May 2024 19:35:59 +0000 (20:35 +0100)
* Dump everything from the current common codebase into a repo!

150 files changed:
.gitmodules [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
src/bz/audio/ogg.c [new file with mode: 0644]
src/bz/audio/ogg.h [new file with mode: 0644]
src/bz/audio/playback.c [new file with mode: 0644]
src/bz/audio/playback.h [new file with mode: 0644]
src/bz/audio/playback_internal.h [new file with mode: 0644]
src/bz/audio/speech.cpp [new file with mode: 0644]
src/bz/audio/speech.h [new file with mode: 0644]
src/bz/audio/wav.c [new file with mode: 0644]
src/bz/audio/wav.h [new file with mode: 0644]
src/bz/collision/collision.c [new file with mode: 0644]
src/bz/collision/collision.h [new file with mode: 0644]
src/bz/collision/particle_collision.c [new file with mode: 0644]
src/bz/collision/particle_collision.h [new file with mode: 0644]
src/bz/common.pch [new file with mode: 0644]
src/bz/debug/assert.c [new file with mode: 0644]
src/bz/debug/assert.h [new file with mode: 0644]
src/bz/debug/log.c [new file with mode: 0644]
src/bz/debug/log.h [new file with mode: 0644]
src/bz/debug/log_internal.h [new file with mode: 0644]
src/bz/debug/perfgraph.c [new file with mode: 0644]
src/bz/debug/perfgraph.h [new file with mode: 0644]
src/bz/debug/perfgraph_internal.h [new file with mode: 0644]
src/bz/fx/agent_simulation.c [new file with mode: 0644]
src/bz/fx/agent_simulation.h [new file with mode: 0644]
src/bz/fx/agent_simulation_internal.h [new file with mode: 0644]
src/bz/fx/particle_simulation.c [new file with mode: 0644]
src/bz/fx/particle_simulation.h [new file with mode: 0644]
src/bz/fx/particle_simulation_internal.h [new file with mode: 0644]
src/bz/fx/particle_system.c [new file with mode: 0644]
src/bz/fx/particle_system.h [new file with mode: 0644]
src/bz/fx/particle_system_internal.h [new file with mode: 0644]
src/bz/game/actor.c [new file with mode: 0644]
src/bz/game/actor.h [new file with mode: 0644]
src/bz/game/actor_internal.h [new file with mode: 0644]
src/bz/game/scene.c [new file with mode: 0644]
src/bz/game/scene.h [new file with mode: 0644]
src/bz/game/scene_identifiers.cpp [new file with mode: 0644]
src/bz/game/scene_internal.h [new file with mode: 0644]
src/bz/game/tilemap.c [new file with mode: 0644]
src/bz/game/tilemap.h [new file with mode: 0644]
src/bz/game/tilemap_internal.h [new file with mode: 0644]
src/bz/gfx/aseprite.c [new file with mode: 0644]
src/bz/gfx/aseprite.h [new file with mode: 0644]
src/bz/gfx/aseprite_internal.h [new file with mode: 0644]
src/bz/gfx/draw_queue.c [new file with mode: 0644]
src/bz/gfx/draw_queue.h [new file with mode: 0644]
src/bz/gfx/drawing.c [new file with mode: 0644]
src/bz/gfx/drawing.h [new file with mode: 0644]
src/bz/gfx/font.c [new file with mode: 0644]
src/bz/gfx/font.h [new file with mode: 0644]
src/bz/gfx/font_internal.h [new file with mode: 0644]
src/bz/gfx/gfx.c [new file with mode: 0644]
src/bz/gfx/gfx.h [new file with mode: 0644]
src/bz/gfx/gfx_internal.h [new file with mode: 0644]
src/bz/gfx/particle_drawing.c [new file with mode: 0644]
src/bz/gfx/particle_drawing.h [new file with mode: 0644]
src/bz/gfx/tilemap.c [new file with mode: 0644]
src/bz/gfx/tilemap.h [new file with mode: 0644]
src/bz/gfx/tilemap_internal.h [new file with mode: 0644]
src/bz/impl.c [new file with mode: 0644]
src/bz/input/input.c [new file with mode: 0644]
src/bz/input/input.h [new file with mode: 0644]
src/bz/input/input_id.h [new file with mode: 0644]
src/bz/input/input_id_internal.h [new file with mode: 0644]
src/bz/input/input_internal.h [new file with mode: 0644]
src/bz/input/platform.h [new file with mode: 0644]
src/bz/math/math.c [new file with mode: 0644]
src/bz/math/math.h [new file with mode: 0644]
src/bz/math/matrix.c [new file with mode: 0644]
src/bz/math/matrix.h [new file with mode: 0644]
src/bz/math/random.c [new file with mode: 0644]
src/bz/math/random.h [new file with mode: 0644]
src/bz/math/vector.c [new file with mode: 0644]
src/bz/math/vector.h [new file with mode: 0644]
src/bz/memory/allocator.c [new file with mode: 0644]
src/bz/memory/allocator.h [new file with mode: 0644]
src/bz/memory/arena.c [new file with mode: 0644]
src/bz/memory/arena.h [new file with mode: 0644]
src/bz/memory/arena_internal.h [new file with mode: 0644]
src/bz/renderer/palette.c [new file with mode: 0644]
src/bz/renderer/palette.h [new file with mode: 0644]
src/bz/renderer/palette_internal.h [new file with mode: 0644]
src/bz/renderer/render_pass.h [new file with mode: 0644]
src/bz/renderer/render_pass_internal.h [new file with mode: 0644]
src/bz/renderer/renderer.c [new file with mode: 0644]
src/bz/renderer/renderer.h [new file with mode: 0644]
src/bz/renderer/renderer_internal.h [new file with mode: 0644]
src/bz/resources/resource.c [new file with mode: 0644]
src/bz/resources/resource.h [new file with mode: 0644]
src/bz/scripting/bindings.cpp [new file with mode: 0644]
src/bz/scripting/bindings.h [new file with mode: 0644]
src/bz/scripting/bindings_internal.h [new file with mode: 0644]
src/bz/scripting/environment.c [new file with mode: 0644]
src/bz/scripting/environment.h [new file with mode: 0644]
src/bz/scripting/environment_internal.h [new file with mode: 0644]
src/bz/scripting/script.c [new file with mode: 0644]
src/bz/scripting/script.h [new file with mode: 0644]
src/bz/scripting/script_internal.h [new file with mode: 0644]
src/bz/scripting/static_crc32.hpp [new file with mode: 0644]
src/bz/types/common.h [new file with mode: 0644]
src/bz/types/identifier.c [new file with mode: 0644]
src/bz/types/identifier.h [new file with mode: 0644]
src/bz/types/identifier_internal.h [new file with mode: 0644]
src/bz/types/point.h [new file with mode: 0644]
src/bz/types/rect.h [new file with mode: 0644]
src/bz/types/size.h [new file with mode: 0644]
src/bz/types/user_parameter.h [new file with mode: 0644]
src_platform/playdate/bz/debug/assert.c [new file with mode: 0644]
src_platform/playdate/bz/debug/log.c [new file with mode: 0644]
src_platform/playdate/bz/debug/perfgraph.c [new file with mode: 0644]
src_platform/playdate/bz/gfx/gfx_platform.c [new file with mode: 0644]
src_platform/playdate/bz/gfx/gfx_platform.h [new file with mode: 0644]
src_platform/playdate/bz/input/input.c [new file with mode: 0644]
src_platform/playdate/bz/memory/arena.c [new file with mode: 0644]
src_platform/playdate/bz/renderer/renderer.c [new file with mode: 0644]
src_platform/playdate/bz/resources/resource.c [new file with mode: 0644]
src_platform/playdate/playdate/entrypoint.c [new file with mode: 0644]
src_platform/playdate/playdate/entrypoint.h [new file with mode: 0644]
src_platform/sdl/bz/audio/playback.c [new file with mode: 0644]
src_platform/sdl/bz/debug/assert.c [new file with mode: 0644]
src_platform/sdl/bz/debug/log.c [new file with mode: 0644]
src_platform/sdl/bz/debug/perfgraph.c [new file with mode: 0644]
src_platform/sdl/bz/gfx/gfx_platform.c [new file with mode: 0644]
src_platform/sdl/bz/gfx/gfx_platform.h [new file with mode: 0644]
src_platform/sdl/bz/input/input.c [new file with mode: 0644]
src_platform/sdl/bz/memory/arena.c [new file with mode: 0644]
src_platform/sdl/bz/renderer/renderer.c [new file with mode: 0644]
src_platform/sdl/bz/resources/file_system_internal.h [new file with mode: 0644]
src_platform/sdl/bz/resources/resource.c [new file with mode: 0644]
src_platform/sdl/main.c [new file with mode: 0644]
third_party/parson [new submodule]
third_party/pcg-c-basic [new submodule]
third_party/physfs [new submodule]
third_party/sdl [new submodule]
third_party/snippets_org_crc/crc.c [new file with mode: 0644]
third_party/snippets_org_crc/crc.h [new file with mode: 0644]
third_party/soloud_speech/Elements.def [new file with mode: 0644]
third_party/soloud_speech/darray.cpp [new file with mode: 0644]
third_party/soloud_speech/darray.h [new file with mode: 0644]
third_party/soloud_speech/klatt.cpp [new file with mode: 0644]
third_party/soloud_speech/klatt.h [new file with mode: 0644]
third_party/soloud_speech/legal_readme.txt [new file with mode: 0644]
third_party/soloud_speech/resonator.cpp [new file with mode: 0644]
third_party/soloud_speech/resonator.h [new file with mode: 0644]
third_party/soloud_speech/tts.cpp [new file with mode: 0644]
third_party/soloud_speech/tts.h [new file with mode: 0644]
third_party/stb [new submodule]
third_party/sun [new submodule]

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..92f60db
--- /dev/null
@@ -0,0 +1,18 @@
+[submodule "third_party/sdl"]
+       path = third_party/sdl
+       url = https://github.com/libsdl-org/SDL.git
+[submodule "third_party/physfs"]
+       path = third_party/physfs
+       url = https://github.com/icculus/physfs.git
+[submodule "third_party/parson"]
+       path = third_party/parson
+       url = https://github.com/kgabis/parson.git
+[submodule "third_party/stb"]
+       path = third_party/stb
+       url = https://github.com/nothings/stb.git
+[submodule "third_party/pcg-c-basic"]
+       path = third_party/pcg-c-basic
+       url = https://github.com/imneme/pcg-c-basic.git
+[submodule "third_party/sun"]
+       path = third_party/sun
+       url = http://git.bts.cx/sun.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e20134d
--- /dev/null
@@ -0,0 +1,362 @@
+cmake_minimum_required(VERSION 3.12)
+
+if(PLAYDATE_DEVICE)
+       set(PROJECT_LANGUAGES C ASM CXX)
+elseif(APPLE)
+       set(PROJECT_LANGUAGES C OBJC CXX)
+else()
+       # Some error needs to go here, and more platforms...
+endif()
+
+set(CMAKE_CXX_STANDARD 11)
+
+project(benzene ${PROJECT_LANGUAGES})
+
+add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/sun/runtime EXCLUDE_FROM_ALL)
+
+if(PLAYDATE_DEVICE)
+       # Playdate is software renderer
+elseif(APPLE)
+       add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/sdl EXCLUDE_FROM_ALL)
+else()
+       # Some error needs to go here, and more platforms...
+endif()
+
+set(BENZENE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
+set(BENZENE_SOURCES
+       ${BENZENE_SRC_DIR}/bz/impl.c
+
+       ${BENZENE_SRC_DIR}/bz/audio/ogg.h
+       ${BENZENE_SRC_DIR}/bz/audio/ogg.c
+       ${BENZENE_SRC_DIR}/bz/audio/playback.h
+       ${BENZENE_SRC_DIR}/bz/audio/playback_internal.h
+       ${BENZENE_SRC_DIR}/bz/audio/playback.c
+       ${BENZENE_SRC_DIR}/bz/audio/speech.h
+       ${BENZENE_SRC_DIR}/bz/audio/speech.cpp
+       ${BENZENE_SRC_DIR}/bz/audio/wav.h
+       ${BENZENE_SRC_DIR}/bz/audio/wav.c
+
+       ${BENZENE_SRC_DIR}/bz/collision/collision.h
+       ${BENZENE_SRC_DIR}/bz/collision/collision.c
+       ${BENZENE_SRC_DIR}/bz/collision/particle_collision.h
+       ${BENZENE_SRC_DIR}/bz/collision/particle_collision.c
+
+       ${BENZENE_SRC_DIR}/bz/debug/assert.h
+       ${BENZENE_SRC_DIR}/bz/debug/assert.c
+       ${BENZENE_SRC_DIR}/bz/debug/log.h
+       ${BENZENE_SRC_DIR}/bz/debug/log.c
+       ${BENZENE_SRC_DIR}/bz/debug/perfgraph.h
+       ${BENZENE_SRC_DIR}/bz/debug/perfgraph.c
+
+       ${BENZENE_SRC_DIR}/bz/fx/agent_simulation.h
+       ${BENZENE_SRC_DIR}/bz/fx/agent_simulation_internal.h
+       ${BENZENE_SRC_DIR}/bz/fx/agent_simulation.c
+       ${BENZENE_SRC_DIR}/bz/fx/particle_simulation.h
+       ${BENZENE_SRC_DIR}/bz/fx/particle_simulation_internal.h
+       ${BENZENE_SRC_DIR}/bz/fx/particle_simulation.c
+       ${BENZENE_SRC_DIR}/bz/fx/particle_system.h
+       ${BENZENE_SRC_DIR}/bz/fx/particle_system_internal.h
+       ${BENZENE_SRC_DIR}/bz/fx/particle_system.c
+
+       ${BENZENE_SRC_DIR}/bz/game/actor.h
+       ${BENZENE_SRC_DIR}/bz/game/actor_internal.h
+       ${BENZENE_SRC_DIR}/bz/game/actor.c
+       ${BENZENE_SRC_DIR}/bz/game/scene.h
+       ${BENZENE_SRC_DIR}/bz/game/scene_internal.h
+       ${BENZENE_SRC_DIR}/bz/game/scene.c
+       ${BENZENE_SRC_DIR}/bz/game/scene_identifiers.cpp
+       ${BENZENE_SRC_DIR}/bz/game/tilemap.h
+       ${BENZENE_SRC_DIR}/bz/game/tilemap_internal.h
+       ${BENZENE_SRC_DIR}/bz/game/tilemap.c
+
+       ${BENZENE_SRC_DIR}/bz/gfx/aseprite.h
+       ${BENZENE_SRC_DIR}/bz/gfx/aseprite_internal.h
+       ${BENZENE_SRC_DIR}/bz/gfx/aseprite.c
+       ${BENZENE_SRC_DIR}/bz/gfx/draw_queue.h
+       ${BENZENE_SRC_DIR}/bz/gfx/draw_queue.c
+       ${BENZENE_SRC_DIR}/bz/gfx/drawing.h
+       ${BENZENE_SRC_DIR}/bz/gfx/drawing.c
+       ${BENZENE_SRC_DIR}/bz/gfx/font.h
+       ${BENZENE_SRC_DIR}/bz/gfx/font_internal.h
+       ${BENZENE_SRC_DIR}/bz/gfx/font.c
+       ${BENZENE_SRC_DIR}/bz/gfx/gfx.h
+       ${BENZENE_SRC_DIR}/bz/gfx/gfx_internal.h
+       ${BENZENE_SRC_DIR}/bz/gfx/gfx.c
+       ${BENZENE_SRC_DIR}/bz/gfx/particle_drawing.h
+       ${BENZENE_SRC_DIR}/bz/gfx/particle_drawing.c
+       ${BENZENE_SRC_DIR}/bz/gfx/tilemap.h
+       ${BENZENE_SRC_DIR}/bz/gfx/tilemap_internal.h
+       ${BENZENE_SRC_DIR}/bz/gfx/tilemap.c
+
+       ${BENZENE_SRC_DIR}/bz/input/input.h
+       ${BENZENE_SRC_DIR}/bz/input/input_internal.h
+       ${BENZENE_SRC_DIR}/bz/input/input.c
+       ${BENZENE_SRC_DIR}/bz/input/input_id.h
+       ${BENZENE_SRC_DIR}/bz/input/input_id_internal.h
+       ${BENZENE_SRC_DIR}/bz/input/platform.h
+
+       ${BENZENE_SRC_DIR}/bz/math/math.h
+       ${BENZENE_SRC_DIR}/bz/math/math.c
+       ${BENZENE_SRC_DIR}/bz/math/matrix.h
+       ${BENZENE_SRC_DIR}/bz/math/matrix.c
+       ${BENZENE_SRC_DIR}/bz/math/random.h
+       ${BENZENE_SRC_DIR}/bz/math/random.c
+       ${BENZENE_SRC_DIR}/bz/math/vector.h
+       ${BENZENE_SRC_DIR}/bz/math/vector.c
+
+       ${BENZENE_SRC_DIR}/bz/memory/allocator.h
+       ${BENZENE_SRC_DIR}/bz/memory/allocator.c
+       ${BENZENE_SRC_DIR}/bz/memory/arena.h
+       ${BENZENE_SRC_DIR}/bz/memory/arena_internal.h
+       ${BENZENE_SRC_DIR}/bz/memory/arena.c
+
+       ${BENZENE_SRC_DIR}/bz/renderer/palette.h
+       ${BENZENE_SRC_DIR}/bz/renderer/palette_internal.h
+       ${BENZENE_SRC_DIR}/bz/renderer/palette.c
+       ${BENZENE_SRC_DIR}/bz/renderer/render_pass.h
+       ${BENZENE_SRC_DIR}/bz/renderer/render_pass_internal.h
+       ${BENZENE_SRC_DIR}/bz/renderer/renderer.h
+       ${BENZENE_SRC_DIR}/bz/renderer/renderer_internal.h
+       ${BENZENE_SRC_DIR}/bz/renderer/renderer.c
+
+       ${BENZENE_SRC_DIR}/bz/resources/resource.h
+       ${BENZENE_SRC_DIR}/bz/resources/resource.c
+
+       ${BENZENE_SRC_DIR}/bz/scripting/bindings.h
+       ${BENZENE_SRC_DIR}/bz/scripting/bindings_internal.h
+       ${BENZENE_SRC_DIR}/bz/scripting/bindings.cpp
+       ${BENZENE_SRC_DIR}/bz/scripting/environment.h
+       ${BENZENE_SRC_DIR}/bz/scripting/environment_internal.h
+       ${BENZENE_SRC_DIR}/bz/scripting/environment.c
+       ${BENZENE_SRC_DIR}/bz/scripting/script.h
+       ${BENZENE_SRC_DIR}/bz/scripting/script_internal.h
+       ${BENZENE_SRC_DIR}/bz/scripting/script.c
+       ${BENZENE_SRC_DIR}/bz/scripting/static_crc32.hpp
+
+       ${BENZENE_SRC_DIR}/bz/types/common.h
+       ${BENZENE_SRC_DIR}/bz/types/identifier.h
+       ${BENZENE_SRC_DIR}/bz/types/identifier.c
+       ${BENZENE_SRC_DIR}/bz/types/point.h
+       ${BENZENE_SRC_DIR}/bz/types/rect.h
+       ${BENZENE_SRC_DIR}/bz/types/size.h
+       ${BENZENE_SRC_DIR}/bz/types/user_parameter.h
+)
+
+if(PLAYDATE_DEVICE)
+       set(PLATFORM_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src_platform/playdate)
+       set(BENZENE_PLATFORM_SOURCES
+               ${PLATFORM_SRC_DIR}/bz/debug/assert.c
+               ${PLATFORM_SRC_DIR}/bz/debug/log.c
+               ${PLATFORM_SRC_DIR}/bz/debug/perfgraph.c
+
+               ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.h
+               ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.c
+
+               ${PLATFORM_SRC_DIR}/bz/input/input.c
+
+               ${PLATFORM_SRC_DIR}/bz/memory/arena.c
+
+               ${PLATFORM_SRC_DIR}/bz/renderer/renderer.c
+
+               ${PLATFORM_SRC_DIR}/bz/resources/resource.c
+
+               ${PLATFORM_SRC_DIR}/playdate/entrypoint.h
+               ${PLATFORM_SRC_DIR}/playdate/entrypoint.c
+       )
+else()
+       set(PLATFORM_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src_platform/sdl)
+       set(BENZENE_PLATFORM_SOURCES
+               ${PLATFORM_SRC_DIR}/bz/audio/playback.c
+
+               ${PLATFORM_SRC_DIR}/bz/debug/assert.c
+               ${PLATFORM_SRC_DIR}/bz/debug/log.c
+               ${PLATFORM_SRC_DIR}/bz/debug/perfgraph.c
+
+               ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.h
+               ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.c
+
+               ${PLATFORM_SRC_DIR}/bz/input/input.c
+
+               ${PLATFORM_SRC_DIR}/bz/memory/arena.c
+
+               ${PLATFORM_SRC_DIR}/bz/renderer/renderer.c
+
+               ${PLATFORM_SRC_DIR}/bz/resources/file_system_internal.h
+               ${PLATFORM_SRC_DIR}/bz/resources/resource.c
+
+               ${PLATFORM_SRC_DIR}/main.c
+       )
+endif()
+
+if(NOT PLAYDATE_DEVICE)
+       set(PHYSFS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/physfs/)
+       set(PHYSFS_SOURCES
+               ${PHYSFS_SRC_DIR}/src/physfs.c
+               ${PHYSFS_SRC_DIR}/src/physfs.h
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_7z.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_dir.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_grp.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_hog.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_iso9660.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_mvl.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_qpak.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_slb.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_unpacked.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_vdf.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_wad.c
+               ${PHYSFS_SRC_DIR}/src/physfs_archiver_zip.c
+               ${PHYSFS_SRC_DIR}/src/physfs_byteorder.c
+               ${PHYSFS_SRC_DIR}/src/physfs_casefolding.h
+               ${PHYSFS_SRC_DIR}/src/physfs_internal.h
+               ${PHYSFS_SRC_DIR}/src/physfs_lzmasdk.h
+               ${PHYSFS_SRC_DIR}/src/physfs_miniz.h
+               ${PHYSFS_SRC_DIR}/src/physfs_platform_android.c
+               ${PHYSFS_SRC_DIR}/src/physfs_platform_os2.c
+               ${PHYSFS_SRC_DIR}/src/physfs_platform_posix.c
+               ${PHYSFS_SRC_DIR}/src/physfs_platform_qnx.c
+               ${PHYSFS_SRC_DIR}/src/physfs_platform_unix.c
+               ${PHYSFS_SRC_DIR}/src/physfs_platform_windows.c
+               ${PHYSFS_SRC_DIR}/src/physfs_platforms.h
+               ${PHYSFS_SRC_DIR}/src/physfs_unicode.c
+       )
+       if(APPLE)
+               list(APPEND PHYSFS_SOURCES ${PHYSFS_SRC_DIR}/src/physfs_platform_apple.m)
+       endif()
+endif()
+
+set(PARSON_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/parson/)
+set(PARSON_SOURCES
+       ${PARSON_SRC_DIR}/parson.h
+       ${PARSON_SRC_DIR}/parson.c
+)
+
+set(PCG_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/pcg-c-basic/)
+set(PCG_SOURCES
+       ${PCG_SRC_DIR}/pcg_basic.h
+       ${PCG_SRC_DIR}/pcg_basic.c
+)
+
+set(CRC_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/snippets_org_crc/)
+set(CRC_SOURCES
+       ${CRC_SRC_DIR}/crc.h
+       ${CRC_SRC_DIR}/crc.c
+)
+
+set(SOLOUD_SPEECH_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/soloud_speech/)
+set(SOLOUD_SPEECH_SOURCES
+       ${SOLOUD_SPEECH_SRC_DIR}/darray.h
+       ${SOLOUD_SPEECH_SRC_DIR}/darray.cpp
+       ${SOLOUD_SPEECH_SRC_DIR}/klatt.h
+       ${SOLOUD_SPEECH_SRC_DIR}/klatt.cpp
+       ${SOLOUD_SPEECH_SRC_DIR}/resonator.h
+       ${SOLOUD_SPEECH_SRC_DIR}/resonator.cpp
+       ${SOLOUD_SPEECH_SRC_DIR}/tts.h
+       ${SOLOUD_SPEECH_SRC_DIR}/tts.cpp
+)
+
+
+add_library(benzene STATIC ${BENZENE_SOURCES})
+set_property(TARGET benzene PROPERTY C_STANDARD 11)
+
+if(PLAYDATE_DEVICE)
+       # NB: This is copied out of the Playdate SDK. Check for correctness on update.
+
+       set(ENVSDK $ENV{PLAYDATE_SDK_PATH})
+
+       if (NOT ${ENVSDK} STREQUAL "")
+               # Convert path from Windows
+               file(TO_CMAKE_PATH ${ENVSDK} SDK)
+       else()
+               execute_process(
+                               COMMAND bash -c "egrep '^\\s*SDKRoot' $HOME/.Playdate/config"
+                               COMMAND head -n 1
+                               COMMAND cut -c9-
+                               OUTPUT_VARIABLE SDK
+                               OUTPUT_STRIP_TRAILING_WHITESPACE
+               )
+       endif()
+
+       if (NOT EXISTS ${SDK})
+               message(FATAL_ERROR "SDK Path not found; set ENV value PLAYDATE_SDK_PATH")
+               return()
+       endif()
+
+       ##
+
+       include_directories("${SDK}/C_API")
+       set(PDC "${SDK}/bin/pdc" -sdkpath "${SDK}")
+
+       add_compile_definitions(TARGET_EXTENSION=1)
+
+       set(HEAP_SIZE 8388208)
+       set(STACK_SIZE 61800)
+       set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -x assembler-with-cpp -D__HEAP_SIZE=${HEAP_SIZE} -D__STACK_SIZE=${STACK_SIZE}")
+
+       set(MCFLAGS -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -D__FPU_USED=1)
+
+       target_compile_definitions(benzene PUBLIC TARGET_PLAYDATE=1)
+       target_compile_options(benzene PUBLIC -Wall -Wno-unknown-pragmas -Wdouble-promotion)
+#      target_compile_options(benzene PUBLIC $<$<CONFIG:DEBUG>:-O2>)
+#      target_compile_options(benzene PUBLIC $<$<CONFIG:RELEASE>:-O2>)
+       target_compile_options(benzene PUBLIC -O2)
+
+       target_compile_options(benzene PUBLIC ${MCFLAGS})
+       target_compile_options(benzene PUBLIC -falign-functions=16 -fomit-frame-pointer)
+       target_compile_options(benzene PUBLIC -gdwarf-2)
+       target_compile_options(benzene PUBLIC -fverbose-asm)
+       target_compile_options(benzene PUBLIC -ffunction-sections -fdata-sections)
+       target_compile_options(benzene PUBLIC -mword-relocations -fno-common)
+
+       #target_compile_options(benzene PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>)
+
+       target_link_options(benzene PUBLIC ${MCFLAGS})
+
+       target_link_options(benzene PUBLIC -specs=nosys.specs)
+
+#      target_compile_options(benzene PUBLIC "-funroll-loops")
+
+       target_compile_options(benzene PUBLIC "-nostdlib")
+#      target_compile_options(benzene PUBLIC "-nostdinc")
+       target_compile_options(benzene PUBLIC "-nodefaultlibs")
+
+elseif(APPLE)
+
+endif()
+
+if(PLAYDATE_DEVICE)
+       target_sources(benzene PRIVATE ${BENZENE_PLATFORM_SOURCES} ${PARSON_SOURCES} ${PCG_SOURCES} ${CRC_SOURCES})
+else()
+       target_sources(benzene PRIVATE ${BENZENE_PLATFORM_SOURCES} ${PHYSFS_SOURCES} ${PARSON_SOURCES} ${PCG_SOURCES} ${CRC_SOURCES} ${SOLOUD_SPEECH_SOURCES})
+endif()
+
+target_precompile_headers(benzene PRIVATE ${BENZENE_SRC_DIR}/bz/common.pch)
+
+target_link_libraries(benzene PRIVATE sun)
+target_include_directories(benzene PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sun/runtime/src)
+
+if(PLAYDATE_DEVICE)
+
+else()
+       if(TARGET SDL2::SDL2main)
+               target_link_libraries(benzene PRIVATE SDL2::SDL2main)
+       endif()
+
+       # FIXME, Ideally we want to use the DLLs so that Steam etc. can patch
+       #target_link_libraries(benzene PRIVATE SDL2::SDL2)
+       target_link_libraries(benzene PRIVATE SDL2::SDL2-static)
+endif()
+
+if(APPLE)
+       target_link_libraries(benzene PRIVATE "-framework Foundation")
+       target_link_libraries(benzene PRIVATE "-framework IOKit")
+endif()
+
+target_include_directories(benzene PRIVATE ${GAME_SRC_DIR})
+target_include_directories(benzene PRIVATE ${BENZENE_SRC_DIR})
+target_include_directories(benzene PRIVATE ${PLATFORM_SRC_DIR})
+target_include_directories(benzene PRIVATE ${PARSON_SRC_DIR})
+target_include_directories(benzene PRIVATE ${PCG_SRC_DIR})
+target_include_directories(benzene PRIVATE ${CRC_SRC_DIR})
+target_include_directories(benzene PRIVATE ${SOLOUD_SPEECH_SRC_DIR})
+target_include_directories(benzene PRIVATE ${PHYSFS_SRC_DIR}/src)
+target_include_directories(benzene PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stb)
diff --git a/src/bz/audio/ogg.c b/src/bz/audio/ogg.c
new file mode 100644 (file)
index 0000000..bf00345
--- /dev/null
@@ -0,0 +1,109 @@
+#include <bz/audio/ogg.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+
+#include <stb_vorbis.c>
+
+struct BZAudioOGGStream {
+       BZAudioOGGStreamDetails details;
+       BZResourceID handle;
+       size_t dataBlockSize;
+//     uint8_t *dataBlock;
+//     uint32_t rate;
+//     uint8_t channels;
+       stb_vorbis *vorbis;
+};
+
+BZAudioOGGStream *bzAudioOpenOGGStream(BZAudioOGGStreamDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZAudioOGGStream *stream = bzMemoryAlloc(arena, sizeof(BZAudioOGGStream));
+
+       stream->handle = bzResourcesOpenResource("music", "assets/music/%s.ogg", identifier);
+       
+       size_t initialSize = 128;
+       int memoryConsumed, error;
+       for (;;) {
+               bzResourcesSeek(stream->handle, 0);
+
+               void *initialData = bzMemoryAllocTmp(arena, initialSize);// FIXME, realloc
+               bzResourcesReadBytes(stream->handle, initialData, initialSize);
+
+               stream->vorbis = stb_vorbis_open_pushdata(initialData, initialSize, &memoryConsumed, &error, NULL); // FIXME, temp memory
+               if (stream->vorbis != NULL) {
+                       break;
+               }
+               bzAssert(error == VORBIS_need_more_data);
+               initialSize <<= 1;
+               bzMemoryArenaResetTmp(arena);
+       }
+
+       bzResourcesSeek(stream->handle, memoryConsumed);
+
+       stb_vorbis_info info = stb_vorbis_get_info(stream->vorbis);
+
+       stream->details = (BZAudioOGGStreamDetails) {
+               .rate = info.sample_rate,
+               .channels = info.channels,
+               .maxSamples = info.max_frame_size,
+       };
+
+       if (detailsOut != NULL) {
+               *detailsOut = stream->details;
+       }
+
+       stream->dataBlockSize = info.temp_memory_required;
+
+       //*outputSizeOut = stream->dataBlockSize;
+
+//     stream->dataBlock = bzMemoryAlloc(arena, stream->dataBlockSize);
+
+//     stream->rate = info.sample_rate;
+//     stream->channels = info.channels;
+
+       //bzResourcesCloseResource(handle);
+
+       return stream;
+}
+//size_t *samplesOut, uint32_t *rateOut, uint8_t *channelsOut, 
+
+void bzAudioResetOGGData(BZAudioOGGStream *stream) {
+       bzResourcesSeek(stream->handle, 0);
+       stb_vorbis_flush_pushdata(stream->vorbis);
+}
+
+size_t bzAudioQueryOGGData(BZAudioOGGStream *stream, size_t maxDataSize, float *data) {
+       float **output;
+       size_t samples = 0;
+
+       void *dataBlock = alloca(stream->dataBlockSize); // FIXME
+
+       for (;;) {
+               size_t offset = bzResourcesTell(stream->handle);
+               bzResourcesReadBytes(stream->handle, dataBlock, stream->dataBlockSize);
+               size_t bytesUsed = stb_vorbis_decode_frame_pushdata(stream->vorbis, dataBlock, stream->dataBlockSize, NULL, &output, &samples);
+               if (bytesUsed == 0 && samples == 0) {
+                       break;
+               }
+               //bzAssert(bytesUsed != 0 || samples != 0);
+               bzResourcesSeek(stream->handle, offset + bytesUsed);
+               if (samples > 0) {
+                       break;
+               }
+       }
+
+       for (size_t i = 0, out = 0; i < samples; ++i) {
+               for (size_t j = 0; j < stream->details.channels; ++j, ++out) {
+                       data[out] = output[j][i];
+               }
+       }
+
+       return samples * stream->details.channels * sizeof(float);
+}
+
+void bzAudioCloseOGGStream(BZAudioOGGStream *stream) {
+       stb_vorbis_close(stream->vorbis);
+       bzResourcesCloseResource(stream->handle);
+}
diff --git a/src/bz/audio/ogg.h b/src/bz/audio/ogg.h
new file mode 100644 (file)
index 0000000..8f8a7e5
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef BZ_AUDIO_OGG_H
+#define BZ_AUDIO_OGG_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZAudioOGGStreamDetails {
+       uint32_t rate;
+       uint8_t channels;
+       uint32_t maxSamples;
+};
+typedef struct BZAudioOGGStreamDetails BZAudioOGGStreamDetails;
+
+typedef struct BZAudioOGGStream BZAudioOGGStream;
+
+extern BZAudioOGGStream *bzAudioOpenOGGStream(BZAudioOGGStreamDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...);
+extern void bzAudioCloseOGGStream(BZAudioOGGStream *stream);
+
+extern void bzAudioResetOGGData(BZAudioOGGStream *stream);
+extern size_t bzAudioQueryOGGData(BZAudioOGGStream *stream, size_t maxDataSize, float *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/audio/playback.c b/src/bz/audio/playback.c
new file mode 100644 (file)
index 0000000..dcaa5f8
--- /dev/null
@@ -0,0 +1,2 @@
+#include <bz/audio/playback_internal.h>
+
diff --git a/src/bz/audio/playback.h b/src/bz/audio/playback.h
new file mode 100644 (file)
index 0000000..42c55b7
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef BZ_AUDIO_PLAYBACK_H
+#define BZ_AUDIO_PLAYBACK_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZAudioPlaybackEngine BZAudioPlaybackEngine;
+typedef BZAudioPlaybackEngine * BZAudioPlaybackEngineID;
+
+extern BZAudioPlaybackEngineID bzAudioPlaybackInit(BZMemoryArenaID arena, const char *identifierFmt, ...);
+extern void bzAudioPlaybackTeardown(BZAudioPlaybackEngineID engine);
+
+extern void bzAudioPlaybackSetParameter(BZAudioPlaybackEngineID engine, BZIdentifierHash parameterIdentifier, float value);
+extern void bzAudioPlaybackPostEvent(BZAudioPlaybackEngineID engine, BZIdentifierHash eventIdentifier, ...);
+
+extern void bzAudioPlaybackUseSoundbank(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/audio/playback_internal.h b/src/bz/audio/playback_internal.h
new file mode 100644 (file)
index 0000000..3d32be3
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef BZ_AUDIO_PLAYBACK_INTERNAL_H
+#define BZ_AUDIO_PLAYBACK_INTERNAL_H
+
+#include <bz/audio/playback.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/audio/speech.cpp b/src/bz/audio/speech.cpp
new file mode 100644 (file)
index 0000000..ef8ece6
--- /dev/null
@@ -0,0 +1,109 @@
+#include <bz/audio/speech.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+#include <darray.h>
+#include <klatt.h>
+#include <tts.h>
+#include <new>
+#include <parson.h>
+#include <string.h> // strcmp
+
+struct BZAudioVoice {
+       darray element;
+       klatt synth;
+       size_t maxFrames;
+//     short *sample;
+};
+
+BZAudioVoice *bzAudioLoadSpeechVoice(BZAudioVoiceDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       int baseFrequency = 1330;
+       float baseSpeed = 10.0f;
+       float baseDeclination = 0.5f;
+       int aBaseWaveform = KW_SAW;
+
+       BZResourceID handle = bzResourcesOpenResource("voices", "assets/voices/%s.voice.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+
+       char *data = (char *)alloca(length); // FIXME, temporary memory
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *voiceJson = json_parse_string(data);
+       JSON_Object *voiceJsonObject = json_object(voiceJson);
+
+       if (json_object_has_value_of_type(voiceJsonObject, "frequency", JSONNumber)) {
+               baseFrequency = json_object_get_number(voiceJsonObject, "frequency");
+       }
+
+       if (json_object_has_value_of_type(voiceJsonObject, "speed", JSONNumber)) {
+               baseSpeed = json_object_get_number(voiceJsonObject, "speed");
+       }
+
+       if (json_object_has_value_of_type(voiceJsonObject, "declination", JSONNumber)) {
+               baseDeclination = json_object_get_number(voiceJsonObject, "declination");
+       }
+
+       if (json_object_has_value_of_type(voiceJsonObject, "waveform", JSONString)) {
+               const char *waveform = json_object_get_string(voiceJsonObject, "waveform");
+               if (strcmp(waveform, "saw") == 0) {
+                       aBaseWaveform = KW_SAW;
+               } else if (strcmp(waveform, "triangle") == 0) {
+                       aBaseWaveform = KW_TRIANGLE;
+               } else if (strcmp(waveform, "sin") == 0) {
+                       aBaseWaveform = KW_SIN;
+               } else if (strcmp(waveform, "square") == 0) {
+                       aBaseWaveform = KW_SQUARE;
+               } else if (strcmp(waveform, "pulse") == 0) {
+                       aBaseWaveform = KW_PULSE;
+               } else if (strcmp(waveform, "noise") == 0) {
+                       aBaseWaveform = KW_NOISE;
+               } else if (strcmp(waveform, "warble") == 0) {
+                       aBaseWaveform = KW_WARBLE;
+               }
+       }
+
+       json_value_free(voiceJson);
+
+       BZAudioVoice *voice = new(bzMemoryAlloc(arena, sizeof(BZAudioVoice))) BZAudioVoice();
+       voice->synth.init(baseFrequency, baseSpeed, baseDeclination, aBaseWaveform);
+       voice->maxFrames = 500;//4 * 1024;
+//     voice->sample = (short *)bzMemoryAlloc(arena, voice->synth.mNspFr * voice->maxFrames * sizeof(short));
+
+       detailsOut->channels = 1;
+       detailsOut->rate = 11025;
+       detailsOut->maxSamples = voice->synth.mNspFr * voice->maxFrames;
+
+//     *sizeOut = outputCount * sizeof(short);
+//     *samplingRateOut = 11025;
+//     *channelsOut = 1;
+//     *bitsPerSampleOut = 16;
+
+       return voice;
+}
+
+size_t bzAudioGenerateSpeech(BZAudioVoice *voice, short *dst, size_t dstSize, const char *speechFmt, ...) {
+       bzMakeIdentifier(speech, speechFmt);
+
+       voice->element.clear();
+
+       darray phone; // FIXME, darray should have a stable memory footprint
+       xlate_string(speech, &phone);
+       int frames = klatt::phone_to_elm(phone.getData(), phone.getSize(), &voice->element);
+       bzAssertMessage(frames <= voice->maxFrames, "Too many frames, %d", frames);
+
+       voice->synth.initsynth(voice->element.getSize(), (unsigned char *)voice->element.getData());
+       
+       size_t sampleSize = voice->synth.mNspFr * frames;
+       size_t outputCount = 0;
+       while (outputCount < sampleSize) {
+               outputCount += voice->synth.synth(voice->synth.mNspFr /* This seems to be ignored... */, &dst[outputCount]);
+               if (outputCount == sampleSize) { break; }
+       }
+       
+       return outputCount * sizeof(short);
+}
diff --git a/src/bz/audio/speech.h b/src/bz/audio/speech.h
new file mode 100644 (file)
index 0000000..1d7848f
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef BZ_AUDIO_SPEECH_H
+#define BZ_AUDIO_SPEECH_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZAudioVoiceDetails {
+       uint32_t rate;
+       uint8_t channels;
+       uint32_t maxSamples;
+};
+typedef struct BZAudioVoiceDetails BZAudioVoiceDetails;
+
+typedef struct BZAudioVoice BZAudioVoice;
+
+extern BZAudioVoice *bzAudioLoadSpeechVoice(BZAudioVoiceDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...);
+extern size_t bzAudioGenerateSpeech(BZAudioVoice *voice, short *dst, size_t dstSize, const char *speechFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/audio/wav.c b/src/bz/audio/wav.c
new file mode 100644 (file)
index 0000000..8942335
--- /dev/null
@@ -0,0 +1,107 @@
+#include <bz/audio/wav.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+
+#include <assert.h> // Static assertions
+
+// https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf
+
+#define PACKED_STRUCT __attribute__((__packed__))
+
+typedef uint16_t RIFF_WORD;
+typedef uint32_t RIFF_DWORD;
+typedef uint8_t RIFF_BYTE;
+
+typedef RIFF_DWORD RIFF_FOURCC; // Four-character code
+
+typedef RIFF_FOURCC RIFF_CKID;  // Four-character-code chunk identifier
+typedef RIFF_DWORD RIFF_CKSIZE; // 32-bit unsigned size value
+
+struct PACKED_STRUCT RIFF_CK {
+       RIFF_CKID ckID;
+       RIFF_CKSIZE ckSize;
+       //RIFF_BYTE ckData[];
+};
+typedef struct RIFF_CK RIFF_CK;
+static_assert(sizeof(RIFF_CK) == 8, "CK is wrong size");
+
+#define CHUNK_ID(a,b,c,d) (((a) << 0) | ((b) << 8) | ((c) << 16) | ((d) << 24))
+
+struct PACKED_STRUCT RIFF_WAVEFormatCK {
+       RIFF_WORD wFormatTag;        // Format category
+       RIFF_WORD wChannels;         // Number of channels
+       RIFF_DWORD dwSamplesPerSec;  // Sampling rate
+       RIFF_DWORD dwAvgBytesPerSec; // For buffer estimation
+       RIFF_WORD wBlockAlign;       // Data block size
+};
+typedef struct RIFF_WAVEFormatCK RIFF_WAVEFormatCK;
+
+struct PACKED_STRUCT RIFF_PCMFormatCK {
+       RIFF_WORD wBitsPerSample; // Sample size
+};
+typedef struct RIFF_PCMFormatCK RIFF_PCMFormatCK;
+
+enum {
+       RIFF_WAVE_FORMAT_PCM  = 0x0001, // Microsoft Pulse Code Modulation (PCM) format
+       RIFF_IBM_FORMAT_MULAW = 0x0101, // IBM mu-law format
+       RIFF_IBM_FORMAT_ALAW  = 0x0102, // IBM a-law format
+       RIFF_IBM_FORMAT_ADPCM = 0x0103  // IBM AVC Adaptive Differential Pulse Code Modulation format
+};
+
+void *bzAudioLoadWAV(size_t *sizeOut, BZMemoryArenaID arena, uint32_t *samplingRateOut, uint16_t *channelsOut, uint16_t *bitsPerSampleOut, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZResourceID handle = bzResourcesOpenResource("sounds", "assets/sounds/%s.wav", identifier);
+
+       void *wavData = NULL;
+
+       for (;;) {
+               RIFF_CK chunk;
+               size_t readLength = bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
+               if (readLength != sizeof(chunk)) {
+                       break;
+               }
+
+               size_t chunkDataStartPosition = bzResourcesTell(handle);
+
+               switch (chunk.ckID) {
+                       case CHUNK_ID('R','I','F','F'): {
+                               RIFF_CKID fileType;
+                               bzResourcesReadBytes(handle, &fileType, sizeof(fileType));
+                               bzAssertMessage(fileType == CHUNK_ID('W','A','V','E'), "Not a WAV file");
+                               break;
+                       }
+
+                       case CHUNK_ID('f','m','t',' '): {
+                               RIFF_WAVEFormatCK format;
+                               bzResourcesReadBytes(handle, &format, sizeof(format));
+                               bzAssertMessage(format.wFormatTag == RIFF_WAVE_FORMAT_PCM, "Not a PCM file");
+                               RIFF_PCMFormatCK pcmFormat;
+                               bzResourcesReadBytes(handle, &pcmFormat, sizeof(pcmFormat));
+
+                               *samplingRateOut = format.dwSamplesPerSec;
+                               *channelsOut = format.wChannels;
+                               *bitsPerSampleOut = pcmFormat.wBitsPerSample;
+                       }
+
+                       case CHUNK_ID('d','a','t','a'): {
+                               wavData = bzMemoryAlloc(arena, chunk.ckSize);
+                               *sizeOut = chunk.ckSize;
+                               bzResourcesReadBytes(handle, wavData, chunk.ckSize);
+                       }
+
+                       default:
+                               // Skip this chunk...
+                               bzResourcesSeek(handle, chunkDataStartPosition + chunk.ckSize);
+                               break;
+               }
+       }
+
+       bzResourcesCloseResource(handle);
+
+       bzLog("Loaded WAV '%s', %d, %d, %d", identifier, *samplingRateOut, *channelsOut, *bitsPerSampleOut);
+
+       return wavData;
+}
diff --git a/src/bz/audio/wav.h b/src/bz/audio/wav.h
new file mode 100644 (file)
index 0000000..f4da91c
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_AUDIO_WAV_H
+#define BZ_AUDIO_WAV_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void *bzAudioLoadWAV(size_t *sizeOut, BZMemoryArenaID arena, uint32_t *samplingRateOut, uint16_t *channelsOut, uint16_t *bitsPerSampleOut, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/collision/collision.c b/src/bz/collision/collision.c
new file mode 100644 (file)
index 0000000..20710dd
--- /dev/null
@@ -0,0 +1,277 @@
+#include <bz/collision/collision.h>
+
+#include <bz/debug/assert.h>
+#include <bz/math/math.h>
+#include <bz/memory/allocator.h>
+
+struct BZCollisionBody {
+       BZCollisionBodyType type;
+       BZCollisionShape shape;
+       BZIdentifierHash tagHash;
+       void *userParameter;
+};
+
+struct BZCollisionSpace {
+       size_t maxBodies;
+       size_t nextBody;
+       BZCollisionBody bodies[];
+};
+
+const BZCollisionShapeType BZCollisionShapeTypeMax = BZCollisionShapeTypeLine;
+
+enum CollisionTestResult {
+    CollisionTestResultNoCollision,
+    CollisionTestResultCollision,
+    CollisionTestResultCollisionInverted,
+};
+typedef enum CollisionTestResult CollisionTestResult;
+
+struct CollisionDetails {
+       BZVector position;
+       BZVector normal;
+};
+typedef struct CollisionDetails CollisionDetails;
+
+static bool collisionCircleCircleTest(CollisionDetails *detailsOut, float *penetrationOut, const BZCollisionShape *shape1, const BZCollisionShape *shape2) {
+       bool colliding = false;
+       
+       BZVector delta;
+       bzVectorSubtract(&delta, &shape2->circleOrigin, &shape1->circleOrigin);
+       float radius = shape1->circleRadius + shape2->circleRadius;
+       
+       float deltaMagnitude = bzVectorMagnitude(&delta);
+       colliding = (deltaMagnitude > 0.01f && deltaMagnitude <= radius);
+       if (colliding) {
+               bzVectorInverse(&delta, &delta);
+               bzVectorNormalized(&detailsOut->normal, &delta);
+               bzVectorScale(&detailsOut->position, &detailsOut->normal, radius);
+               bzVectorAdd(&detailsOut->position, &detailsOut->position, &shape2->circleOrigin);
+               *penetrationOut = (radius - deltaMagnitude);
+       }
+       
+       return colliding;
+}
+
+static bool collisionCircleCirclePerimeterTest(CollisionDetails *detailsOut, float *penetrationOut, const BZCollisionShape *shape1, const BZCollisionShape *shape2) {
+       bool colliding = false;
+       
+       BZVector delta;
+       bzVectorSubtract(&delta, &shape1->circleOrigin, &shape2->circleOrigin);
+       float innerRadius = shape2->circleRadius - shape1->circleRadius;
+
+       float deltaMagnitude = bzVectorMagnitude(&delta);
+       colliding = deltaMagnitude > innerRadius;// (deltaMagnitude > 0.01f && deltaMagnitude <= radius);
+       if (colliding) {
+               bzVectorNormalized(&detailsOut->normal, &delta);
+               bzVectorScale(&detailsOut->position, &detailsOut->normal, shape2->circleRadius);
+               bzVectorAdd(&detailsOut->position, &detailsOut->position, &shape2->circleOrigin);
+
+               bzVectorInverse(&detailsOut->normal, &detailsOut->normal);
+
+               *penetrationOut = shape1->circleRadius;
+       }
+       
+       return colliding;
+}
+
+static bool collisionLineLineTest(CollisionDetails *detailsOut, float *penetrationOut, const BZCollisionShape *shape1, const BZCollisionShape *shape2) {
+       bool colliding = false;
+       
+       BZVector delta;
+       bzVectorSubtract(&delta, &shape2->lineOrigin, &shape1->lineOrigin);
+       
+       float lineCross = bzVectorCross(&shape1->lineDirection, &shape2->lineDirection);
+       
+       float t = bzVectorCross(&delta, &shape2->lineDirection) / lineCross;
+       if (t >= 0.0f && t <= shape1->lineMagnitude) {
+               float u = bzVectorCross(&delta, &shape1->lineDirection) / lineCross;
+               if (u >= 0.0f && u <= shape2->lineMagnitude) {
+                       bzVectorScale(&delta, &shape1->lineDirection, t);
+                       bzVectorAdd(&detailsOut->position, &delta, &shape1->lineOrigin);
+                       colliding = true;
+               }
+       }
+       
+       return colliding;
+}
+
+static bool collisionCircleLineTest(CollisionDetails *detailsOut, float *penetrationOut, const BZCollisionShape *shape1, const BZCollisionShape *shape2) {
+       bool colliding = false;
+
+       BZVector delta;
+       bzVectorSubtract(&delta, &shape1->circleOrigin, &shape2->lineOrigin);
+       
+       float dot = bzVectorDot(&delta, &shape2->lineDirection);
+       if (dot > 0.01f && dot <= shape2->lineMagnitude) {
+               BZVector projection, test;
+               bzVectorScale(&projection, &shape2->lineDirection, dot);
+               bzVectorSubtract(&test, &delta, &projection);
+               float magnitude = bzVectorMagnitude(&test);
+               colliding = (magnitude > 0.01f && magnitude <= shape1->circleRadius);
+               if (colliding) {
+                       bzVectorAdd(&detailsOut->position, &projection, &shape2->lineOrigin);
+                       bzVectorNormalized(&detailsOut->normal, &test);
+                       *penetrationOut = bzAbs(magnitude - shape1->circleRadius);
+               }
+       }
+       
+       return colliding;
+}
+
+
+
+static bool collisionCircleTriangleTest(CollisionDetails *detailsOut, float *penetrationOut, const BZCollisionShape *shape1, const BZCollisionShape *shape2) {
+       // https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle
+
+       #define tsign(x1, y1, x2, y2, x3, y3) (((x1)-(x3))*((y2)-(y3))-((x2)-(x3))*((y1)-(y3)))
+
+       float x = shape1->circleOrigin.x;
+       float y = shape1->circleOrigin.y;
+
+       float x1 = shape2->trianglePoint1.x;
+       float y1 = shape2->trianglePoint1.y;
+       float x2 = shape2->trianglePoint2.x;
+       float y2 = shape2->trianglePoint2.y;
+       float x3 = shape2->trianglePoint3.x;
+       float y3 = shape2->trianglePoint3.y;
+
+       float a = tsign(x,y,x1,y1,x2,y2);
+       float b = tsign(x,y,x2,y2,x3,y3);
+       float c = tsign(x,y,x3,y3,x1,y1);
+
+       // FIXME, need to do radius.
+
+       return ! ((a<0 || b<0 || c<0) && (a>0 || b>0 || c>0));
+       
+       /*bool colliding = false;
+
+       BZVector delta;
+       bzVectorSubtract(&delta, &shape1->circleOrigin, &shape2->lineOrigin);
+       
+       float dot = bzVectorDot(&delta, &shape2->lineDirection);
+       if (dot > 0.01f && dot <= shape2->lineMagnitude) {
+               BZVector projection, test;
+               bzVectorScale(&projection, &shape2->lineDirection, dot);
+               bzVectorSubtract(&test, &delta, &projection);
+               float magnitude = bzVectorMagnitude(&test);
+               colliding = (magnitude > 0.01f && magnitude <= shape1->circleRadius);
+               if (colliding) {
+                       bzVectorAdd(&detailsOut->position, &projection, &shape2->lineOrigin);
+                       bzVectorNormalized(&detailsOut->normal, &test);
+                       *penetrationOut = bzAbs(magnitude - shape1->circleRadius);
+               }
+       }
+       
+       return colliding;*/
+       return false;
+}
+
+static CollisionTestResult collisionTest(CollisionDetails *detailsOut, float *penetrationOut, const BZCollisionShape *shape1, const BZCollisionShape *shape2) {
+       #define TEST_MASK(type1, type2) (((type1) << BZCollisionShapeTypeMax) | (type2))
+       
+       switch (TEST_MASK(shape1->type, shape2->type)) {
+               case TEST_MASK(BZCollisionShapeTypeCircle, BZCollisionShapeTypeCircle):
+                       return collisionCircleCircleTest(detailsOut, penetrationOut, shape1, shape2) ? CollisionTestResultCollision : CollisionTestResultNoCollision;
+                       
+               case TEST_MASK(BZCollisionShapeTypeCircle, BZCollisionShapeTypeCirclePerimeter):
+                       return collisionCircleCirclePerimeterTest(detailsOut, penetrationOut, shape1, shape2) ? CollisionTestResultCollision : CollisionTestResultNoCollision;
+
+               case TEST_MASK(BZCollisionShapeTypeCirclePerimeter, BZCollisionShapeTypeCircle):
+                       return collisionCircleCirclePerimeterTest(detailsOut, penetrationOut, shape2, shape1) ? CollisionTestResultCollisionInverted : CollisionTestResultNoCollision;
+
+               case TEST_MASK(BZCollisionShapeTypeLine, BZCollisionShapeTypeLine):
+                       return collisionLineLineTest(detailsOut, penetrationOut, shape1, shape2) ? CollisionTestResultCollision : CollisionTestResultNoCollision;
+                       
+               case TEST_MASK(BZCollisionShapeTypeCircle, BZCollisionShapeTypeLine):
+                       return collisionCircleLineTest(detailsOut, penetrationOut, shape1, shape2) ? CollisionTestResultCollision : CollisionTestResultNoCollision;
+                       
+               case TEST_MASK(BZCollisionShapeTypeLine, BZCollisionShapeTypeCircle):
+                       return collisionCircleLineTest(detailsOut, penetrationOut, shape2, shape1) ? CollisionTestResultCollisionInverted : CollisionTestResultNoCollision;
+
+               case TEST_MASK(BZCollisionShapeTypeCircle, BZCollisionShapeTypeTriangle):
+                       return collisionCircleTriangleTest(detailsOut, penetrationOut, shape1, shape2) ? CollisionTestResultCollision : CollisionTestResultNoCollision;
+
+               case TEST_MASK(BZCollisionShapeTypeTriangle, BZCollisionShapeTypeCircle):
+                       return collisionCircleTriangleTest(detailsOut, penetrationOut, shape2, shape1) ? CollisionTestResultCollisionInverted : CollisionTestResultNoCollision;
+
+                       
+               default:
+                       bzAssertMessage(false, "Invalid collision test");
+                       return 0;
+       }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+BZCollisionSpaceID bzCollisionMakeSpace(BZMemoryArenaID arena, size_t maxBodies, const char *identifierFmt, ...) {
+       BZCollisionSpaceID space = (BZCollisionSpace *)bzMemoryAlloc(arena, sizeof(BZCollisionSpace) + maxBodies * sizeof(BZCollisionBody));
+       space->maxBodies = maxBodies;
+       bzCollisionResetSpace(space);
+       return space;
+}
+
+void bzCollisionResetSpace(BZCollisionSpaceID space) {
+       space->nextBody = 0;
+}
+
+BZCollisionBodyID bzCollisionAddBody(BZCollisionSpaceID space, BZCollisionBodyType bodyType, const BZCollisionShape *shape, BZIdentifierHash tagHash, void *userParameter) {
+       bzAssert(space->nextBody < space->maxBodies);
+       BZCollisionBodyID body = &space->bodies[space->nextBody++];
+       body->type = bodyType;
+       body->shape = *shape;
+       body->tagHash = tagHash;
+       body->userParameter = userParameter;
+
+       return body;
+}
+
+bool bzCollisionResolve(BZVector *positionOut, void **userParameterOut, BZCollisionSpaceID space, const BZCollisionShape *shape, BZIdentifierHash tagHash) {
+       CollisionDetails details;
+       float penetration;
+       for (size_t i = 0; i < space->nextBody; ++i) {
+               BZCollisionBody *body = &space->bodies[i];
+               if (body->tagHash == tagHash) {
+                       CollisionTestResult testResult = collisionTest(&details, &penetration, shape, &body->shape);
+                       if (testResult != CollisionTestResultNoCollision) {
+                               if (positionOut != NULL) {
+                                       BZVector delta;
+                                       bzVectorScale(&delta, &details.normal, penetration);
+                                       bzVectorAdd(positionOut, &details.position, &delta);
+                               }
+                               if (userParameterOut != NULL) {
+                                       *userParameterOut = body->userParameter;
+                               }
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+
+
+
+#include <bz/gfx/gfx.h>
+void bzCollisionDrawDebug(BZCollisionSpaceID space) {
+       for (size_t i = 0; i < space->nextBody; ++i) {
+               switch (space->bodies[i].shape.type) {
+                       case BZCollisionShapeTypeCircle:
+                               bzCirc(space->bodies[i].shape.circleOrigin.x, space->bodies[i].shape.circleOrigin.y, space->bodies[i].shape.circleRadius, 2);
+                               break;
+
+                       default:
+                               break;
+               }
+       }
+}
diff --git a/src/bz/collision/collision.h b/src/bz/collision/collision.h
new file mode 100644 (file)
index 0000000..24418ff
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef BZ_COLLISION_COLLISION_H
+#define BZ_COLLISION_COLLISION_H
+
+#include <bz/math/vector.h>
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum BZCollisionBodyType {
+       BZCollisionBodyTypeSolid,
+};
+typedef enum BZCollisionBodyType BZCollisionBodyType;
+
+enum BZCollisionShapeType {
+       BZCollisionShapeTypeUnknown,
+       BZCollisionShapeTypeCircle,
+       BZCollisionShapeTypeCirclePerimeter,
+       BZCollisionShapeTypeLine,
+       BZCollisionShapeTypeTriangle,
+};
+typedef enum BZCollisionShapeType BZCollisionShapeType;
+
+struct BZCollisionShape {
+       BZCollisionShapeType type;
+       union {
+               struct {
+                       BZVector circleOrigin;
+                       float circleRadius;
+               };
+               struct {
+                       BZVector lineOrigin;
+                       BZVector lineDirection;
+                       float lineMagnitude;
+               };
+               struct {
+                       BZVector trianglePoint1;
+                       BZVector trianglePoint2;
+                       BZVector trianglePoint3;
+               };
+       };
+};
+typedef struct BZCollisionShape BZCollisionShape;
+
+//typedef uint32_t BZCollisionTag;
+typedef uint32_t BZCollisionTagMask;
+
+typedef struct BZCollisionSpace BZCollisionSpace;
+typedef BZCollisionSpace * BZCollisionSpaceID;
+
+typedef struct BZCollisionBody BZCollisionBody;
+typedef BZCollisionBody * BZCollisionBodyID;
+
+extern BZCollisionSpaceID bzCollisionMakeSpace(BZMemoryArenaID arena, size_t maxBodies, const char *identifierFmt, ...);
+
+
+
+extern void bzCollisionResetSpace(BZCollisionSpaceID space);
+
+extern BZCollisionBodyID bzCollisionAddBody(BZCollisionSpaceID space, BZCollisionBodyType bodyType, const BZCollisionShape *shape, BZIdentifierHash tagHash, void *userParameter);
+
+/////zones
+
+extern bool bzCollisionResolve(BZVector *positionOut, void **userParameterOut, BZCollisionSpaceID space, const BZCollisionShape *shape, BZIdentifierHash tagHash);
+
+extern void bzCollisionDrawDebug(BZCollisionSpaceID space);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/collision/particle_collision.c b/src/bz/collision/particle_collision.c
new file mode 100644 (file)
index 0000000..6392628
--- /dev/null
@@ -0,0 +1,20 @@
+#include <bz/collision/particle_collision.h>
+
+#include <bz/fx/particle_simulation_internal.h>
+
+void bzCollisionPopulateParticles(BZCollisionSpaceID space, BZParticleSimulationID simulation, BZIdentifierHash tagHash, float radiusMultiplier) {
+       BZCollisionShape s;
+
+       s.type = BZCollisionShapeTypeCircle;
+       
+       for (size_t i = 0; i < simulation->maxParticles; ++i) {
+               float particleStartTicks = simulation->spawnTime[i];
+               float lifetime = simulation->lifetime[i];
+               if (particleStartTicks + lifetime >= simulation->time) {
+                       s.circleOrigin.x = simulation->positionX[i];
+                       s.circleOrigin.y = simulation->positionY[i];
+                       s.circleRadius = simulation->size[i] * radiusMultiplier;
+                       bzCollisionAddBody(space, BZCollisionBodyTypeSolid, &s, tagHash, (void *)i);
+               }
+       }
+}
diff --git a/src/bz/collision/particle_collision.h b/src/bz/collision/particle_collision.h
new file mode 100644 (file)
index 0000000..5c9b7f9
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef BZ_COLLISION_PARTICLE_COLLISION_H
+#define BZ_COLLISION_PARTICLE_COLLISION_H
+
+#include <bz/collision/collision.h>
+#include <bz/fx/particle_simulation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzCollisionPopulateParticles(BZCollisionSpaceID space, BZParticleSimulationID simulation, BZIdentifierHash tagHash, float radiusMultiplier);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/common.pch b/src/bz/common.pch
new file mode 100644 (file)
index 0000000..c253c3f
--- /dev/null
@@ -0,0 +1,3 @@
+#include <bz/debug/assert.h>
+#include <bz/debug/log.h>
+#include <bz/debug/perfgraph.h>
\ No newline at end of file
diff --git a/src/bz/debug/assert.c b/src/bz/debug/assert.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/bz/debug/assert.h b/src/bz/debug/assert.h
new file mode 100644 (file)
index 0000000..72381df
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef BZ_DEBUG_ASSERT_H
+#define BZ_DEBUG_ASSERT_H
+
+#include <bz/debug/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define bzAssertMessage(test, fmt, ...) {if ((test) != true) { bzError(fmt, ##__VA_ARGS__); bzAssertExit(); } }
+#define bzAssert(test) bzAssertMessage(test, "failed assertion \""#test"\"")
+
+extern void bzAssertExit(void); // TODO: Consider moving this to generic failure handling
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/debug/log.c b/src/bz/debug/log.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/bz/debug/log.h b/src/bz/debug/log.h
new file mode 100644 (file)
index 0000000..34c655d
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_DEBUG_LOG_H
+#define BZ_DEBUG_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void _bzLog(const char *a1, unsigned long a2, const char *fmt, ...);
+extern void _bzError(const char *a1, unsigned long a2, const char *fmt, ...);
+
+#define bzLog(fmt, ...) _bzLog(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
+#define bzError(fmt, ...) _bzError(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/debug/log_internal.h b/src/bz/debug/log_internal.h
new file mode 100644 (file)
index 0000000..7ac38ca
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef BZ_DEBUG_LOG_INTERNAL_H
+#define BZ_DEBUG_LOG_INTERNAL_H
+
+#include <bz/debug/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzLogInit(const char *outputFilePath);
+extern void bzLogTeardown(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/debug/perfgraph.c b/src/bz/debug/perfgraph.c
new file mode 100644 (file)
index 0000000..e84ba36
--- /dev/null
@@ -0,0 +1,75 @@
+#include <bz/debug/perfgraph_internal.h>
+
+#include <bz/types/common.h>
+#include <bz/gfx/gfx.h>
+
+extern void resetSystemTimer(void);
+extern float getSystemTimer(void);
+
+struct BZPerfTimer {
+       const char *identifier;
+       float startTime;
+       float elapsedTime;
+};
+typedef struct BZPerfTimer BZPerfTimer;
+
+BZPerfTimer perfTimers[32];
+
+void bzPerfReset() {
+       resetSystemTimer();
+
+       for (size_t i = 0; i < 32; ++i) { // FIXME
+               perfTimers[i].identifier = NULL;
+               perfTimers[i].elapsedTime = 0.0f;
+       }
+}
+
+static BZPerfTimer *bzPerfTimerFind(const char *identifier) {
+       BZPerfTimer *timer = NULL;
+       
+       for (size_t i = 0; i < 32; ++i) {
+               if (perfTimers[i].identifier == NULL && timer == NULL) {
+                       timer = &perfTimers[i];
+               } else if (perfTimers[i].identifier == identifier) {
+                       timer = &perfTimers[i];
+                       break;
+               }
+       }
+
+       bzAssert(timer != NULL);
+       return timer;
+}
+
+void _bzPerfTimerStart(const char *identifier) {
+       BZPerfTimer *timer = bzPerfTimerFind(identifier);
+       timer->identifier = identifier;
+       timer->startTime = getSystemTimer();
+}
+
+void _bzPerfTimerStop(const char *identifier) {
+       float endTime = getSystemTimer();
+       BZPerfTimer *timer = bzPerfTimerFind(identifier);
+       timer->elapsedTime += endTime - timer->startTime;
+}
+
+void _bzPerfTimerOutput() {
+       for (size_t i = 0; i < 32; ++i) {
+               if (perfTimers[i].identifier != NULL) {
+                       bzLog("Timer '%s': %fs", perfTimers[i].identifier, perfTimers[i].elapsedTime);
+               }
+       }
+}
+
+void _bzPerfTimerDraw() {
+       int y = 10;
+       int width = 50;
+       for (size_t i = 0; i < 32; ++i) {
+               if (perfTimers[i].identifier != NULL) {
+                       bzRectFill(10, y, 10 + width, y+3, 5);
+                       float t = perfTimers[i].elapsedTime / (1 / 30.0f);
+                       bzRectFill(10, y, 10 + width * t, y + 3, 8);
+                       bzPrint(10 + width, y, 7, perfTimers[i].identifier);
+                       y += 5;
+               }
+       }
+}
diff --git a/src/bz/debug/perfgraph.h b/src/bz/debug/perfgraph.h
new file mode 100644 (file)
index 0000000..ae9870e
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef BZ_DEBUG_PERFGRAPH_H
+#define BZ_DEBUG_PERFGRAPH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void _bzPerfTimerStart(const char *identifier);
+extern void _bzPerfTimerStop(const char *identifier);
+extern void _bzPerfTimerOutput(void);
+extern void _bzPerfTimerDraw(void);
+
+#define _bzPerfTimerStartNull(id) {}
+#define _bzPerfTimerStopNull(id) {}
+#define _bzPerfTimerOutputNull() {}
+#define _bzPerfTimerDrawNull() {}
+
+#if 1
+       #define bzPerfTimerStart _bzPerfTimerStartNull
+       #define bzPerfTimerStop _bzPerfTimerStopNull
+       #define bzPerfTimerOutput _bzPerfTimerOutputNull
+       #define bzPerfTimerDraw _bzPerfTimerDrawNull
+#else
+       #define bzPerfTimerStart _bzPerfTimerStart
+       #define bzPerfTimerStop _bzPerfTimerStop
+       #define bzPerfTimerOutput _bzPerfTimerOutput
+       #define bzPerfTimerDraw _bzPerfTimerDraw
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/debug/perfgraph_internal.h b/src/bz/debug/perfgraph_internal.h
new file mode 100644 (file)
index 0000000..d81e937
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_DEBUG_PERFGRAPH_INTERNAL_H
+#define BZ_DEBUG_PERFGRAPH_INTERNAL_H
+
+#include <bz/debug/perfgraph.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzPerfReset(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/fx/agent_simulation.c b/src/bz/fx/agent_simulation.c
new file mode 100644 (file)
index 0000000..f550aab
--- /dev/null
@@ -0,0 +1,202 @@
+#include "agent_simulation_internal.h"
+
+#include <bz/math/math.h>
+#include <bz/math/random.h>
+#include <bz/memory/allocator.h>
+
+struct BZAgent {
+       size_t agentIdx;
+       BZAgentDetails details;
+       BZVector force;
+       BZVector velocity;
+};
+
+struct BZAgentSimulation {
+       size_t maxAgents;
+       size_t agentCount;
+       BZAgent *agents;
+};
+
+BZAgentSimulationID bzFXCreateAgentSimulation(BZMemoryArenaID arena, size_t maxAgents, const char *identifierFmt, ...) {
+       BZAgentSimulation *simulation = bzMemoryAlloc(arena, sizeof(BZAgentSimulation));
+
+       simulation->maxAgents = maxAgents;
+       simulation->agents = bzMemoryAlloc(arena, maxAgents * sizeof(BZAgent));
+
+       return simulation;
+}
+
+static void agentSeekForce(BZVector *out, BZAgent *agent, const BZVector *seekTarget) {
+       BZVector delta;
+       bzVectorSubtract(&delta, seekTarget, &agent->details.position);
+       if (bzVectorMagnitudeSquared(&delta) > 10.0f * 10.0f) { // FIXME
+               bzVectorNormalized(out, &delta);
+               bzVectorScale(out, out, agent->details.maxForce); // FIXME
+       } else {
+               bzVectorSet(out, 0, 0);
+       }
+}
+
+static void agentSeparationForce(BZVector *out, BZAgentSimulationID simulation, BZAgent *agent) {
+       bzVectorSet(out, 0, 0);
+       for (size_t i = 0; i < simulation->agentCount; ++i) {
+               BZVector delta;
+               bzVectorSubtract(&delta, &agent->details.position, &simulation->agents[i].details.position);
+               float squareDistance = bzVectorMagnitudeSquared(&delta);
+               float radius = 50.0f; // FIXME
+
+               if (squareDistance > 0 && squareDistance < radius * radius) {
+                       BZVector force;
+                       bzVectorScale(&force, &delta, (radius * radius) / squareDistance); // Square distance helps scale up/down avoidance
+                       bzVectorAdd(out, out, &force);
+               }
+       }
+}
+
+static void agentAlignmentForce(BZVector *out, BZAgentSimulationID simulation, BZAgent *agent) {
+       BZVector averageForward = bzVectorMake(0, 0);
+       size_t neighbourCount = 0;
+       for (size_t i = 0; i < simulation->agentCount; ++i) {
+               BZVector delta;
+               bzVectorSubtract(&delta, &agent->details.position, &simulation->agents[i].details.position);
+               float squareDistance = bzVectorMagnitudeSquared(&delta);
+               float radius = 150.0f; // FIXME
+
+               if (squareDistance > 0 && squareDistance < radius * radius) {
+                       BZVector forward;
+                       bzVectorMakeAngle(&forward, simulation->agents[i].details.angle);
+                       bzVectorAdd(&averageForward, &averageForward, &forward);
+                       neighbourCount += 1;
+               }
+       }
+
+       if (neighbourCount > 0) {
+               bzVectorNormalized(out, &averageForward); // FIXME, need to divide by number of agents? limit??
+       } else {
+               bzVectorSet(out, 0, 0);
+       }
+}
+
+static void agentCohesionForce(BZVector *out, BZAgentSimulationID simulation, BZAgent *agent) {
+       BZVector averagePosition = bzVectorMake(0, 0);
+       size_t neighbourCount = 0;
+       for (size_t i = 0; i < simulation->agentCount; ++i) {
+               BZVector delta;
+               bzVectorSubtract(&delta, &agent->details.position, &simulation->agents[i].details.position);
+               float squareDistance = bzVectorMagnitudeSquared(&delta);
+               float radius = 150.0f; // FIXME
+
+               if (squareDistance > 0 && squareDistance < radius * radius) {
+                       bzVectorAdd(&averagePosition, &averagePosition, &simulation->agents[i].details.position);
+                       neighbourCount += 1;
+               }
+       }
+
+       if (neighbourCount > 0) {
+               bzVectorScale(&averagePosition, &averagePosition, 1.0f / neighbourCount);
+               bzVectorSubtract(&averagePosition, &averagePosition, &agent->details.position);
+               bzVectorNormalized(out, &averagePosition);
+       } else {
+               bzVectorSet(out, 0, 0);
+       }
+}
+
+void bzFXUpdateAgentSimulation(BZAgentSimulationID simulation, float deltaTime, BZCollisionSpaceID collisionSpace, BZVector *v) {
+       for (size_t i = 0; i < simulation->agentCount; ++i) {
+               BZAgent *agent = &simulation->agents[i];
+               
+               BZVector seekForce;
+               agentSeekForce(&seekForce, agent, v);
+
+               BZVector separationForce;
+               agentSeparationForce(&separationForce, simulation, agent);
+
+               BZVector alignmentForce;
+               agentAlignmentForce(&alignmentForce, simulation, agent);
+
+               BZVector cohesionForce;
+               agentCohesionForce(&cohesionForce, simulation, agent);
+
+               bzVectorSet(&agent->force, 0, 0);
+               bzVectorScale(&seekForce, &seekForce, 40.0f);
+               bzVectorAdd(&agent->force, &agent->force, &seekForce);
+               bzVectorScale(&separationForce, &separationForce, 100.0f);
+               bzVectorAdd(&agent->force, &agent->force, &separationForce);
+               bzVectorScale(&alignmentForce, &alignmentForce, 10.0f);
+               bzVectorAdd(&agent->force, &agent->force, &alignmentForce);
+               bzVectorScale(&cohesionForce, &cohesionForce, 10.0f);
+               bzVectorAdd(&agent->force, &agent->force, &cohesionForce);
+
+//             bzVectorScale(&agent->force, &agent->force, 36.0f);
+
+               float forceMagnitude = bzVectorMagnitude(&agent->force);
+               forceMagnitude = bzMin(forceMagnitude, agent->details.maxForce);
+               if (forceMagnitude > 0.0f) {
+                       bzVectorNormalized(&agent->force, &agent->force);
+                       bzVectorScale(&agent->force, &agent->force, forceMagnitude);
+                       bzVectorScale(&agent->force, &agent->force, 1.0f / agent->details.mass);
+                       
+                       BZVector force;
+                       bzVectorScale(&force, &agent->force, deltaTime);
+                       bzVectorAdd(&agent->velocity, &agent->velocity, &force);
+//                     bzVectorCopy(&agent->velocity, &agent->force);
+               }
+
+               float velocityMagnitude = bzVectorMagnitude(&agent->velocity);
+               velocityMagnitude = bzMin(velocityMagnitude, agent->details.maxSpeed);
+               if (velocityMagnitude > 0.0f) {
+                       bzVectorNormalized(&agent->velocity, &agent->velocity);
+                       bzVectorScale(&agent->velocity, &agent->velocity, velocityMagnitude);
+               }
+
+               BZVector velocity;
+               bzVectorScale(&velocity, &agent->velocity, deltaTime);
+               bzVectorAdd(&agent->details.position, &agent->details.position, &velocity);
+
+//             bzCollisionResolve()
+       }
+}
+
+BZAgentID bzFXAgentSimulationAddAgent(BZAgentSimulationID simulation, const BZAgentDetails *details) {
+       bzAssertMessage(simulation->agentCount < simulation->maxAgents, "No available agents");
+
+       BZAgent *agent = &simulation->agents[simulation->agentCount++];
+       agent->agentIdx = 1;
+       agent->details = *details;
+       //bzVectorSet(&agent->force, 0, 0);
+       bzVectorSet(&agent->velocity, bzRandomFloatRange(-10, 10), bzRandomFloatRange(-10, 10));
+       
+       return agent;
+}
+
+void bzFXAgentSimulationRemoveAgent(BZAgentSimulationID simulation, BZAgentID agent) {
+       agent->agentIdx = 0; // FIXME
+}
+
+void bzFXAgentSimulationGetAgentDetails(BZAgentDetails *detailsOut, BZAgentSimulationID simulation, BZAgentID agent) {
+       *detailsOut = agent->details;
+}
+
+
+
+#include <bz/gfx/gfx.h>
+void bzFXAgentSimulationDrawDebug(BZAgentSimulationID simulation) {
+       for (size_t i = 0; i < simulation->agentCount; ++i) {
+               BZAgent *agent = &simulation->agents[i];
+               bzCirc(agent->details.position.x, agent->details.position.y, 2, 9);
+
+               BZVector forward;
+               bzVectorMakeAngle(&forward, agent->details.angle);
+               //bzLine(agent->details.position.x, agent->details.position.y, agent->details.position.x + forward.x * 40, agent->details.position.y + forward.y * 40, 7);
+
+               bzLine(
+                       agent->details.position.x, agent->details.position.y,
+                       agent->details.position.x + agent->velocity.x, agent->details.position.y + agent->velocity.y,
+                       7);
+
+               bzLine(
+                       agent->details.position.x, agent->details.position.y,
+                       agent->details.position.x + agent->force.x, agent->details.position.y + agent->force.y,
+                       8);
+       }
+}
diff --git a/src/bz/fx/agent_simulation.h b/src/bz/fx/agent_simulation.h
new file mode 100644 (file)
index 0000000..8172165
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef BZ_FX_AGENT_SIMULATION_H
+#define BZ_FX_AGENT_SIMULATION_H
+
+#include <bz/collision/collision.h>
+#include <bz/math/vector.h>
+#include <bz/memory/arena.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZAgentSimulation BZAgentSimulation;
+typedef BZAgentSimulation * BZAgentSimulationID;
+
+typedef struct BZAgent BZAgent;
+typedef BZAgent * BZAgentID;
+
+struct BZAgentDetails {
+       float mass;
+       BZVector position;
+       float angle;
+       float maxForce;
+       float maxSpeed;
+};
+typedef struct BZAgentDetails BZAgentDetails;
+
+#define bzAgentDefaultMass 1.0f
+#define bzAgentDefaultMaxForce 50.0f
+#define bzAgentDefaultMaxSpeed 100.0f
+
+extern BZAgentSimulationID bzFXCreateAgentSimulation(BZMemoryArenaID arena, size_t maxAgents, const char *identifierFmt, ...);
+
+extern void bzFXUpdateAgentSimulation(BZAgentSimulationID simulation, float deltaTime, BZCollisionSpaceID collisionSpace, BZVector *v);
+
+extern BZAgentID bzFXAgentSimulationAddAgent(BZAgentSimulationID simulation, const BZAgentDetails *details);
+extern void bzFXAgentSimulationRemoveAgent(BZAgentSimulationID simulation, BZAgentID agent);
+
+extern void bzFXAgentSimulationGetAgentDetails(BZAgentDetails *detailsOut, BZAgentSimulationID simulation, BZAgentID agent);
+
+extern void bzFXAgentSimulationDrawDebug(BZAgentSimulationID simulation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/fx/agent_simulation_internal.h b/src/bz/fx/agent_simulation_internal.h
new file mode 100644 (file)
index 0000000..bb9e193
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_FX_AGENT_SIMULATION_INTERNAL_H
+#define BZ_FX_AGENT_SIMULATION_INTERNAL_H
+
+#include <bz/fx/agent_simulation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/fx/particle_simulation.c b/src/bz/fx/particle_simulation.c
new file mode 100644 (file)
index 0000000..477e982
--- /dev/null
@@ -0,0 +1,71 @@
+#include <bz/fx/particle_simulation_internal.h>
+
+#include <bz/fx/particle_system_internal.h>
+#include <bz/math/math.h>
+#include <bz/math/random.h>
+#include <bz/memory/allocator.h>
+#include <bz/types/identifier.h>
+
+BZParticleSimulationID bzFXCreateParticleSimulation(BZMemoryArenaID arena, size_t maxParticles, BZParticleSystemID system, const char *identifierFmt, ...) {
+       BZParticleSimulationID simulation = bzMemoryAlloc(arena, sizeof(BZParticleSimulation));
+       simulation->system = system;
+
+       simulation->maxParticles = maxParticles;//10 * 1024;
+       simulation->nextParticle = 0;
+
+       simulation->time = 0;
+
+       simulation->spawnTime = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->lifetime = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->positionX = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->positionY = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->size = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->baseAngle = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->angle = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->color = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->alpha = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float));
+       simulation->userData = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(void *));
+       
+       return simulation;
+}
+
+void bzFXUpdateParticleSystem(BZParticleSimulationID simulation, float deltaTime) {
+       simulation->time += deltaTime;//1.0f / 30.0f;
+       bzFXUpdateParticleSystemParticles(simulation->system,
+               simulation->maxParticles,
+               simulation->time,
+               simulation->spawnTime,
+               simulation->lifetime,
+               simulation->positionX,
+               simulation->positionY,
+               simulation->size,
+               simulation->baseAngle,
+               simulation->angle,
+               simulation->color,
+               simulation->alpha,
+               simulation->userData);
+}
+
+void bzFXSpawnParticles(BZParticleSimulationID simulation, BZIdentifierHash eventIdentifier, const BZVector *position, const float angle) {
+       BZParticleSystemID particleSystem = simulation->system;
+       simulation->nextParticle += bzFXSpawnParticleSystemParticles(particleSystem,
+               eventIdentifier,
+               position, angle,
+               simulation->maxParticles, simulation->nextParticle,
+               simulation->time,
+               simulation->spawnTime,
+               simulation->lifetime,
+               simulation->positionX,
+               simulation->positionY,
+               simulation->size,
+               simulation->baseAngle,
+               simulation->angle,
+               simulation->color,
+               simulation->alpha,
+               simulation->userData);
+       simulation->nextParticle %= simulation->maxParticles;
+}
+
+void bzFXDespawnParticle(BZParticleSimulationID simulation, size_t idx) {
+       simulation->lifetime[idx] = 0.0f;
+}
diff --git a/src/bz/fx/particle_simulation.h b/src/bz/fx/particle_simulation.h
new file mode 100644 (file)
index 0000000..676a7a4
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef BZ_FX_PARTICLE_SIMULATION_H
+#define BZ_FX_PARTICLE_SIMULATION_H
+
+#include <bz/fx/particle_system.h>
+#include <bz/math/matrix.h>
+#include <bz/memory/arena.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZParticleSimulation BZParticleSimulation;
+typedef BZParticleSimulation * BZParticleSimulationID;
+
+extern BZParticleSimulationID bzFXCreateParticleSimulation(BZMemoryArenaID arena, size_t maxParticles, BZParticleSystemID system, const char *identifierFmt, ...);
+
+extern void bzFXUpdateParticleSystem(BZParticleSimulationID simulation, float deltaTime);
+
+extern void bzFXSpawnParticles(BZParticleSimulationID simulation, BZIdentifierHash eventIdentifier, const BZVector *position, const float angle);
+extern void bzFXDespawnParticle(BZParticleSimulationID simulation, size_t idx); // Do not use this
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/fx/particle_simulation_internal.h b/src/bz/fx/particle_simulation_internal.h
new file mode 100644 (file)
index 0000000..97bce7f
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef BZ_FX_PARTICLE_SIMULATION_INTERNAL_H
+#define BZ_FX_PARTICLE_SIMULATION_INTERNAL_H
+
+#include <bz/fx/particle_simulation.h>
+
+#include <bz/math/vector.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZParticleSimulation {
+       BZParticleSystemID system;
+
+       size_t maxParticles;
+       size_t nextParticle;
+       float time;
+
+       float *spawnTime;
+       float *lifetime;
+       float *positionX;
+       float *positionY;
+       float *size;
+       float *baseAngle;
+       float *angle;
+       float *color;
+       float *alpha;
+       void **userData;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/fx/particle_system.c b/src/bz/fx/particle_system.c
new file mode 100644 (file)
index 0000000..389e114
--- /dev/null
@@ -0,0 +1,402 @@
+#include <bz/fx/particle_system_internal.h>
+
+#include <bz/math/math.h>
+#include <bz/math/random.h>
+#include <bz/math/vector.h>
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier_internal.h>
+#include <parson.h>
+
+enum BZParticleSystemValueType {
+       BZParticleSystemValueTypeConstant,
+       BZParticleSystemValueTypeRandomChoice,
+       BZParticleSystemValueTypeRandomRange,
+};
+typedef enum BZParticleSystemValueType BZParticleSystemValueType;
+
+struct BZParticleSystemFloatValue {
+       BZParticleSystemValueType valueType;
+       union {
+               float constantValue;
+               struct {
+                       size_t choiceValueCount;
+                       float *choiceValues;
+               };
+               struct {
+                       float rangeMinValue;
+                       float rangeMaxValue;
+               };
+       };
+};
+typedef struct BZParticleSystemFloatValue BZParticleSystemFloatValue;
+
+struct BZParticleSystemFloatTableValue {
+       float time;
+       BZParticleSystemFloatValue value;
+};
+typedef struct BZParticleSystemFloatTableValue BZParticleSystemFloatTableValue;
+
+struct BZParticleSystemFloatTable {
+       size_t valueCount;
+       BZParticleSystemFloatTableValue *values;
+       
+};
+typedef struct BZParticleSystemFloatTable BZParticleSystemFloatTable;
+
+struct BZParticleSubsystem {
+       size_t maxParticles;
+       BZIdentifierHash identifier;
+
+       float *lifetimeValueTable;
+       float *angleValueTable;
+       float *velocityXValueTable;
+       float *velocityYValueTable;
+       float *sizeValueTable;
+       float *colorValueTable;
+       float *alphaValueTable;
+};
+typedef struct BZParticleSubsystem BZParticleSubsystem;
+
+struct BZParticleEventEmission {
+       BZParticleSystemFloatValue countValue;
+       BZParticleSubsystem *particleSubsystem;
+};
+typedef struct BZParticleEventEmission BZParticleEventEmission;
+
+struct BZParticleEvent {
+       BZIdentifierHash identifier;
+       size_t emissionCount;
+       BZParticleEventEmission *emissions;
+};
+typedef struct BZParticleEvent BZParticleEvent;
+
+struct BZParticleSystem {
+       size_t subsystemCount;
+       BZParticleSubsystem *subsystems;
+       size_t eventCount;
+       BZParticleEvent *events;
+};
+
+static void particleJSONReadFloatValue(BZMemoryArenaID arena, BZParticleSystemFloatValue *valueOut, JSON_Value *value, float defaultValue) {
+       JSON_Value_Type valueType = json_value_get_type(value);
+
+       switch (valueType) {
+               case JSONNumber:
+                       valueOut->valueType = BZParticleSystemValueTypeConstant;
+                       valueOut->constantValue = json_value_get_number(value);
+                       break;
+
+               case JSONArray: {
+                       JSON_Array *values = json_value_get_array(value);
+                       valueOut->valueType = BZParticleSystemValueTypeRandomChoice;
+                       valueOut->choiceValueCount = json_array_get_count(values);
+                       valueOut->choiceValues = (float *)bzMemoryAlloc(arena, valueOut->choiceValueCount * sizeof(float));
+                       for (size_t i = 0; i < valueOut->choiceValueCount; ++i) {
+                               valueOut->choiceValues[i] = json_array_get_number(values, i);
+                       }
+                       break;
+               }
+
+               case JSONObject: {
+                       JSON_Object *rangeObject = json_value_get_object(value);
+                       valueOut->valueType = BZParticleSystemValueTypeRandomRange;
+                       valueOut->rangeMinValue = json_object_get_number(rangeObject, "min");
+                       valueOut->rangeMaxValue = json_object_get_number(rangeObject, "max");
+                       break;
+               }
+
+               default:
+                       valueOut->valueType = BZParticleSystemValueTypeConstant;
+                       valueOut->constantValue = defaultValue;
+                       break;
+       }
+}
+
+static void particleJSONReadFloatTable(BZMemoryArenaID arena, BZParticleSystemFloatTable *tableOut, JSON_Value *value, float defaultValue) {
+       bool isTable = false;
+
+       JSON_Array *floatTable = json_value_get_array(value);
+       if (floatTable != NULL) {
+               JSON_Object *test =  json_array_get_object(floatTable, 0);
+               if (test != NULL && json_object_has_value_of_type(test, "time", JSONNumber)) {
+                       isTable = true;
+
+                       BZParticleSystemFloatValue previousValue;
+
+                       tableOut->valueCount = json_array_get_count(floatTable);
+                       tableOut->values = (BZParticleSystemFloatTableValue *)bzMemoryAlloc(arena, tableOut->valueCount * sizeof(BZParticleSystemFloatTableValue));
+
+                       for (size_t i = 0; i < tableOut->valueCount; ++i) {
+                               JSON_Object *entry = json_array_get_object(floatTable, i);
+                               tableOut->values[i].time = json_object_get_number(entry, "time");
+
+                               JSON_Value *valueValue = json_object_get_value(entry, "value");
+                               if (valueValue != NULL) {
+                                       particleJSONReadFloatValue(arena, &previousValue, valueValue, defaultValue);
+                               }
+                               tableOut->values[i].value = previousValue;
+                       }
+               }
+       }
+       
+       if (isTable == false) {
+               tableOut->valueCount = 1;
+               tableOut->values = (BZParticleSystemFloatTableValue *)bzMemoryAlloc(arena, tableOut->valueCount * sizeof(BZParticleSystemFloatTableValue));
+               tableOut->values[0].time = 1.0f;
+               particleJSONReadFloatValue(arena, &tableOut->values[0].value, value, defaultValue);
+       }
+}
+
+static void resolveParticleSystemFloatValue(float *valueOut, const BZParticleSystemFloatValue *value) {
+       switch (value->valueType) {
+               case BZParticleSystemValueTypeConstant:
+                       *valueOut = value->constantValue;
+                       break;
+
+               case BZParticleSystemValueTypeRandomChoice:
+                       *valueOut = *((float *)bzRandomArrayValue(value->choiceValueCount, sizeof(float), value->choiceValues));
+                       break;
+
+               case BZParticleSystemValueTypeRandomRange:
+                       *valueOut = bzRandomFloatRange(value->rangeMinValue, value->rangeMaxValue);
+                       break;
+       }
+}
+
+static void resolveParticleSystemFloatTableValue(float valuesOut[], size_t valuesCount, const BZParticleSystemFloatTable *table) {
+       float toT = table->values[0].time;
+       float toValue;
+       resolveParticleSystemFloatValue(&toValue, &table->values[0].value);
+       
+       float fromValue = toValue;
+       float fromT = 0.0f;
+
+       size_t idx = 0;
+       for (size_t i = 0; i <= valuesCount; ++i) {
+               float t = (float)i / (float)valuesCount;
+               
+               if (t >= toT) {
+                       if (idx < table->valueCount) {
+                               idx += 1;
+                               fromT = toT;
+                               fromValue = toValue;
+                               toT = table->values[idx].time;
+                               resolveParticleSystemFloatValue(&toValue, &table->values[idx].value);
+                       } else {
+                               fromT = toT;
+                               fromValue = toValue;
+                               toT = 1.0f;
+                       }
+               }
+
+               float tt = bzUnlerp(fromT, toT, t);
+               valuesOut[i] = bzLerp(fromValue, toValue, tt);
+       }
+}
+
+#define ParticleVariants 128
+#define ParticleSlices 64
+
+BZParticleSystemID bzFXLoadParticleSystem(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZParticleSystemID particleSystem = bzMemoryAlloc(arena, sizeof(BZParticleSystem));
+
+       BZResourceID handle = bzResourcesOpenResource("particles", "assets/particles/%s.system.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+       char *data = bzMemoryAllocTmp(arena, length);
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *particleSystemJson = json_parse_string(data);
+       JSON_Object *particleSystemJsonObject = json_object(particleSystemJson);
+
+       JSON_Array *subsystemsArray = json_object_get_array(particleSystemJsonObject, "subsystems");
+       bzAssertMessage(subsystemsArray != NULL, "Invalid particle JSON");
+
+       particleSystem->subsystemCount = json_array_get_count(subsystemsArray);
+       particleSystem->subsystems = bzMemoryAlloc(arena, particleSystem->subsystemCount * sizeof(BZParticleSubsystem));
+
+       for (size_t i = 0; i < particleSystem->subsystemCount; ++i) {
+               BZParticleSubsystem *subsystem = &particleSystem->subsystems[i];
+
+               subsystem->maxParticles = 1024; // FIXME, load
+
+               subsystem->lifetimeValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float));
+               subsystem->angleValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float) * ParticleSlices);
+               subsystem->velocityXValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float) * ParticleSlices);
+               subsystem->velocityYValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float) * ParticleSlices);
+               subsystem->sizeValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float) * ParticleSlices);
+               subsystem->colorValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float) * ParticleSlices);
+               subsystem->alphaValueTable = bzMemoryAlloc(arena, ParticleVariants * sizeof(float) * ParticleSlices);
+       }
+
+       for (size_t i = 0; i < particleSystem->subsystemCount; ++i) {
+               BZParticleSubsystem *subsystem = &particleSystem->subsystems[i];
+
+               JSON_Object *subsystemObject = json_array_get_object(subsystemsArray, i);
+
+               const char *identifier = json_object_get_string(subsystemObject, "identifier");
+               subsystem->identifier = bzIdentifierHashFromIdentifier(identifier);
+
+               bzMemoryArenaPushWatermark(arena); // for temp stuff
+
+               BZParticleSystemFloatValue lifetimeValue;
+               particleJSONReadFloatValue(arena, &lifetimeValue, json_object_get_value(subsystemObject, "lifetime"), 1.0f);
+
+               BZParticleSystemFloatTable angleTable;
+               particleJSONReadFloatTable(arena, &angleTable, json_object_get_value(subsystemObject, "angle"), 0.0f);
+               
+               BZParticleSystemFloatTable velocityXTable;
+               particleJSONReadFloatTable(arena, &velocityXTable, json_object_get_value(subsystemObject, "velocity-x"), 1.0f);
+
+               BZParticleSystemFloatTable velocityYTable;
+               particleJSONReadFloatTable(arena, &velocityYTable, json_object_get_value(subsystemObject, "velocity-y"), 1.0f);
+
+               BZParticleSystemFloatTable sizeTable;
+               particleJSONReadFloatTable(arena, &sizeTable, json_object_get_value(subsystemObject, "size"), 1.0f);
+
+               BZParticleSystemFloatTable colorTable;
+               particleJSONReadFloatTable(arena, &colorTable, json_object_get_value(subsystemObject, "color"), 7.0f);
+
+               BZParticleSystemFloatTable alphaTable;
+               particleJSONReadFloatTable(arena, &alphaTable, json_object_get_value(subsystemObject, "alpha"), 0.0f);
+
+               for (size_t i = 0; i < ParticleVariants; ++i) {
+                       resolveParticleSystemFloatValue(&subsystem->lifetimeValueTable[i], &lifetimeValue);
+
+                       size_t idx = i * ParticleSlices;
+
+                       resolveParticleSystemFloatTableValue(&subsystem->angleValueTable[idx], ParticleSlices, &angleTable);
+                       resolveParticleSystemFloatTableValue(&subsystem->velocityXValueTable[idx], ParticleSlices, &velocityXTable);
+                       resolveParticleSystemFloatTableValue(&subsystem->velocityYValueTable[idx], ParticleSlices, &velocityYTable);
+                       resolveParticleSystemFloatTableValue(&subsystem->sizeValueTable[idx], ParticleSlices, &sizeTable);
+                       resolveParticleSystemFloatTableValue(&subsystem->colorValueTable[idx], ParticleSlices, &colorTable);
+                       resolveParticleSystemFloatTableValue(&subsystem->alphaValueTable[idx], ParticleSlices, &alphaTable);
+               }
+
+               bzMemoryArenaPopWatermark(arena);
+       }
+
+       JSON_Array *eventsArray = json_object_get_array(particleSystemJsonObject, "events");
+       bzAssertMessage(eventsArray != NULL, "Invalid particle JSON");
+
+       particleSystem->eventCount = json_array_get_count(eventsArray);
+       particleSystem->events = bzMemoryAlloc(arena, particleSystem->eventCount * sizeof(BZParticleEvent));
+
+       for (size_t i = 0; i < particleSystem->eventCount; ++i) {
+               BZParticleEvent *event = &particleSystem->events[i];
+
+               JSON_Object *eventObject = json_array_get_object(eventsArray, i);
+
+               const char *identifier = json_object_get_string(eventObject, "identifier");
+               event->identifier = bzIdentifierHashFromIdentifier(identifier);
+
+               JSON_Array *emissionsArray = json_object_get_array(eventObject, "emissions");
+               event->emissionCount = json_array_get_count(emissionsArray);
+               event->emissions = bzMemoryAlloc(arena, event->emissionCount * sizeof(BZParticleEventEmission));
+
+               for (size_t j = 0; j < event->emissionCount; ++j) {
+                       BZParticleEventEmission *emission = &event->emissions[j];
+
+                       JSON_Object *emissionObject = json_array_get_object(emissionsArray, j);
+
+                       BZParticleSystemFloatValue lifetimeValue;
+                       particleJSONReadFloatValue(arena, &emission->countValue, json_object_get_value(emissionObject, "count"), 1.0f);
+
+                       const char *subsystemIdentifier = json_object_get_string(emissionObject, "subsystem");
+                       BZIdentifierHash subsystemIdentifierHash = bzIdentifierHashFromIdentifier(subsystemIdentifier);
+
+                       for (size_t k = 0; k < particleSystem->subsystemCount; ++k) {
+                               BZParticleSubsystem *subsystem = &particleSystem->subsystems[k];
+                               if (subsystem->identifier == subsystemIdentifierHash) {
+                                       emission->particleSubsystem = subsystem;
+                                       break;
+                               } 
+                       }
+
+                       bzAssertMessage(emission->particleSubsystem != NULL, "could not find particle subsystem '%s'", subsystemIdentifier);
+               }
+       }
+
+       json_value_free(particleSystemJson);
+       bzMemoryArenaResetTmp(arena);
+
+       return particleSystem;
+}
+
+size_t bzFXSpawnParticleSystemParticles(BZParticleSystemID particleSystem, BZIdentifierHash eventIdentifier, const BZVector *spawnPosition, const float spawnAngle, size_t maxCount, size_t currentIdx, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]) {
+       size_t spawnedCount = 0;
+       size_t idx = currentIdx;
+
+       float localX = spawnPosition->x;
+       float localY = spawnPosition->y;
+       float localAngle = spawnAngle;
+
+       for (size_t i = 0; i < particleSystem->eventCount; ++i) {
+               BZParticleEvent *event = &particleSystem->events[i];
+               if (event->identifier == eventIdentifier) {
+                       for (size_t j = 0; j < event->emissionCount; ++j) {
+                               BZParticleEventEmission *emission = &event->emissions[j];
+                               BZParticleSubsystem *particleSubsystem = emission->particleSubsystem;
+
+                               float count;
+                               resolveParticleSystemFloatValue(&count, &emission->countValue);
+
+                               for (size_t k = 0; k < count; ++k) {
+                                       spawnTime[idx] = time;
+                                       lifetime[idx] = particleSubsystem->lifetimeValueTable[idx % ParticleVariants];
+                                       positionX[idx] = localX;
+                                       positionY[idx] = localY;
+                                       baseAngle[idx] = localAngle;
+                                       angle[idx] = localAngle;
+                                       userData[idx] = particleSubsystem;
+                                       idx += 1;
+                                       idx %= maxCount;
+                                       spawnedCount += 1;
+                               }
+                       }
+               }
+       }
+
+       return spawnedCount;
+}
+
+#include <bz/math/matrix.h>
+void bzFXUpdateParticleSystemParticles(BZParticleSystemID particleSystem, size_t count, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]) {
+       BZMatrix local;
+
+       for (size_t i = 0; i < count; ++i) {
+               float particleTicks = time - spawnTime[i];
+               float l = lifetime[i];
+               if (particleTicks <= l) {
+                       BZParticleSubsystem *particleSubsystem = (BZParticleSubsystem *)userData[i];
+
+                       float t = bzUnlerp(0, l, particleTicks);
+                       size_t frame = (int)(t * ParticleSlices);
+
+                       size_t startIdx = bzFloor((spawnTime[i] + l) * 1000);
+                       size_t tableIdx1 = frame + ((startIdx + 12) % ParticleVariants) * ParticleSlices;
+                       size_t tableIdx2 = frame + ((startIdx + 34) % ParticleVariants) * ParticleSlices;
+                       size_t tableIdx3 = frame + ((startIdx + 26) % ParticleVariants) * ParticleSlices;
+                       size_t tableIdx4 = frame + ((startIdx + 74) % ParticleVariants) * ParticleSlices;
+                       size_t tableIdx5 = frame + ((startIdx + 87) % ParticleVariants) * ParticleSlices; // These numbers don't matter. Use anything.
+
+                       float a = baseAngle[i] + particleSubsystem->angleValueTable[tableIdx1];
+                       bzMatrixRotation(&local, a);
+                       angle[i] = a;
+
+                       BZVector v = bzVectorMake(particleSubsystem->velocityXValueTable[tableIdx2], particleSubsystem->velocityYValueTable[tableIdx2]);
+                       bzMatrixTransformVector(&v, &v, &local);
+
+                       positionX[i] += v.x;
+                       positionY[i] += v.y;
+                       size[i] = particleSubsystem->sizeValueTable[tableIdx3];
+                       color[i] = particleSubsystem->colorValueTable[tableIdx4];
+                       alpha[i] = particleSubsystem->alphaValueTable[tableIdx5];
+               }
+       }
+}
diff --git a/src/bz/fx/particle_system.h b/src/bz/fx/particle_system.h
new file mode 100644 (file)
index 0000000..4ccfc17
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef BZ_FX_PARTICLE_SYSTEM_H
+#define BZ_FX_PARTICLE_SYSTEM_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZParticleSystem BZParticleSystem;
+typedef BZParticleSystem * BZParticleSystemID;
+
+extern BZParticleSystemID bzFXLoadParticleSystem(BZMemoryArenaID arena, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/fx/particle_system_internal.h b/src/bz/fx/particle_system_internal.h
new file mode 100644 (file)
index 0000000..8172184
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef BZ_FX_PARTICLE_SYSTEM_INTERNAL_H
+#define BZ_FX_PARTICLE_SYSTEM_INTERNAL_H
+
+#include <bz/fx/particle_system.h>
+
+#include <bz/math/matrix.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern size_t bzFXSpawnParticleSystemParticles(BZParticleSystemID particleSystem, BZIdentifierHash eventIdentifier, const BZVector *spawnPosition, const float spawnAngle, size_t maxCount, size_t currentIdx, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]);
+extern void bzFXUpdateParticleSystemParticles(BZParticleSystemID particleSystem, size_t count, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/game/actor.c b/src/bz/game/actor.c
new file mode 100644 (file)
index 0000000..d742655
--- /dev/null
@@ -0,0 +1,2 @@
+#include <bz/game/actor_internal.h>
+
diff --git a/src/bz/game/actor.h b/src/bz/game/actor.h
new file mode 100644 (file)
index 0000000..21694eb
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef BZ_GAME_ACTOR_H
+#define BZ_GAME_ACTOR_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZActor BZActor;
+typedef BZActor * BZActorID;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/game/actor_internal.h b/src/bz/game/actor_internal.h
new file mode 100644 (file)
index 0000000..6fd912e
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef BZ_GAME_ACTOR_INTERNAL_H
+#define BZ_GAME_ACTOR_INTERNAL_H
+
+#include <bz/game/actor.h>
+
+#include <bz/scripting/script.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZActor {
+       char identifier[kBZMaxIdentifierLength]; // fixme
+       BZIdentifierHash identifierHash;
+       
+       //BZScriptInstanceID actorScript;
+       uint32_t parameters[16]; // FIXME
+       SVMModuleInstance *instance;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/game/scene.c b/src/bz/game/scene.c
new file mode 100644 (file)
index 0000000..907fccb
--- /dev/null
@@ -0,0 +1,553 @@
+#include <bz/game/scene_internal.h>
+
+#include <bz/collision/particle_collision.h>
+//#include <bz/fx/agent_simulation.h>
+#include <bz/fx/particle_simulation.h>
+#include <bz/fx/particle_system.h>
+#include <bz/game/actor_internal.h>
+#include <bz/game/tilemap.h>
+#include <bz/gfx/aseprite.h>
+#include <bz/gfx/font_internal.h>
+#include <bz/gfx/gfx_internal.h>
+#include <bz/gfx/particle_drawing.h>
+#include <bz/math/math.h>
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/scripting/bindings_internal.h>
+#include <bz/scripting/script.h>
+#include <bz/types/identifier_internal.h>
+#include <parson.h>
+#include <string.h> // strncpy
+
+struct BZSceneActorDefinition {
+       //char identifier[kBZMaxIdentifierLength]; // fixme
+       BZIdentifierHash identifierHash;
+       SVMModule *module;
+       size_t instanceSize;
+       int32_t priority;
+       size_t spawnCount;
+};
+typedef struct BZSceneActorDefinition BZSceneActorDefinition;
+
+/*struct BZSceneParticleSystem {
+       char identifier[kBZMaxIdentifierLength]; // fixme
+       BZIdentifierHash identifierHash;
+       BZParticleSystemID particleSystem;
+};
+typedef struct BZSceneParticleSystem BZSceneParticleSystem;*/
+
+struct BZSceneLayer {
+       char identifier[kBZMaxIdentifierLength]; // fixme
+       BZIdentifierHash identifierHash;
+       BZDrawQueueID drawQueue;
+       BZTilemapID tilemap;
+       BZParticleSimulationID particleSimulation;
+       BZCollisionSpaceID collisionSpace;
+       BZIdentifierHash particleCollisionTagHash;
+       float particleCollisionRadiusMultiplier;
+       int outputBuffer;
+};
+
+////#define kMaxActorInstances 50//256
+
+struct BZSceneActor {
+       BZActor actor;
+       BZScriptBindingMetadata scriptMetadata;
+       int32_t priority;
+       bool running;
+};
+typedef struct BZSceneActor BZSceneActor;
+
+#define kBZScriptingEnvironmentPublicCount 256
+#define kBZScriptingEnvironmentEventCount 256
+
+struct BZScriptingEnvironmentPublic {
+       BZIdentifierHash identifierHash;
+       SVMOperand value;
+};
+typedef struct BZScriptingEnvironmentPublic BZScriptingEnvironmentPublic;
+
+struct BZSceneEvent {
+       BZIdentifierHash identifierHash;
+       BZSceneEventData data;
+};
+typedef struct BZSceneEvent BZSceneEvent;
+
+struct BZScene {
+//     BZScriptingEnvironmentID sceneEnvironment;
+//     BZScriptID sceneScript;
+//     BZScriptBindingMetadata sceneScriptMetadata;
+//     BZScriptInstanceID sceneScriptInstance;
+       BZSceneActorDefinition *actorDefinitions;
+       size_t actorDefinitionCount;
+       BZSceneLayer *layers;
+       size_t layerCount;
+       uint64_t ticks;
+
+       int32_t minActorPriority;
+       int32_t maxActorPriority;
+
+       size_t lastActorIndex;
+       size_t actorInstanceCount;
+       BZSceneActor *actorInstances;
+
+       BZScriptingEnvironmentPublic publics[kBZScriptingEnvironmentPublicCount];
+
+       BZAudioPlaybackEngineID audioEngine;
+
+//     BZAgentSimulationID agentSimulation;
+
+       size_t eventCount;
+       BZSceneEvent *currentEvents;
+       size_t nextEnqueuedEvent;
+       BZSceneEvent *enqueuedEvents;
+
+//     char targetSceneIdentifier[kBZMaxIdentifierLength]; // fixme
+};
+
+uint8_t gSystemTicksPerSecond;
+
+static void bzGameSceneActorLoadDefinition(BZSceneActorDefinition *definitionOut, BZSceneID scene, BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+       definitionOut->identifierHash = bzIdentifierHashFromIdentifier(identifier);
+
+       BZResourceID handle = bzResourcesOpenResource("scene", "assets/actors/%s.actor.json", identifier);
+       bzAssertMessage(handle != NULL, "Invalid actor definition '%s'", identifier);
+
+       size_t length = bzResourcesFileLength(handle);
+       char *data = alloca(length);//(char *)bzMemoryAlloc(kBZSystemMemoryArena, length);//alloca(length); // FIXME, need working space off the stack
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *actorDefinitionJson = json_parse_string(data);
+       JSON_Object *actorDefinitionJsonObject = json_object(actorDefinitionJson);
+
+       const char *scriptName = json_object_get_string(actorDefinitionJsonObject, "script");
+       if (scriptName != NULL) {
+               definitionOut->module = bzScriptingLoadScriptModule(arena, "%s", scriptName);
+               definitionOut->instanceSize = svmGetModuleInstanceSize(definitionOut->module);
+       }
+
+       definitionOut->priority = json_object_get_number(actorDefinitionJsonObject, "priority");
+       scene->minActorPriority = bzMin(scene->minActorPriority, definitionOut->priority);
+       scene->maxActorPriority = bzMax(scene->maxActorPriority, definitionOut->priority);
+
+       json_value_free(actorDefinitionJson);
+
+       bzLog("Opened actor %s", identifier);
+}
+
+static BZActorID bzGameSceneSetupActor(BZSceneActor *sceneActor, BZSceneID scene, const BZSceneActorDefinition *definition, const char *identifierFmt, ...) {
+       BZActorID actor = &sceneActor->actor;
+       BZScriptBindingMetadata *metadata = &sceneActor->scriptMetadata;
+
+       bzInsertIdentifier(actor->identifier, identifierFmt);
+       actor->identifierHash = bzIdentifierHashFromIdentifier(actor->identifier);
+
+       bzAssertMessage(definition != NULL, "Could not find definition for '%s'", actor->identifier);
+
+       sceneActor->priority = definition->priority;
+
+       actor->instance = svnResetModuleInstanceMemory(definition->module, actor->instance, metadata, bzScriptingBindingsLookupNativeFunction);
+       bzScriptingInitialiseMetadata((BZScriptBindingMetadata *)&sceneActor->scriptMetadata, actor, scene, scene->lastActorIndex);
+
+       sceneActor->running = true;
+
+       return actor;
+}
+
+BZSceneID bzGameSceneSwitch(BZMemoryArenaID arena, BZAudioPlaybackEngineID audioEngine, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZSceneID scene = bzMemoryAlloc(arena, sizeof(BZScene));
+       
+       BZResourceID handle = bzResourcesOpenResource("scene", "assets/scenes/%s.scene.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+       char *data = alloca(length);//(char *)bzMemoryAlloc(kBZSystemMemoryArena, length);//alloca(length); // FIXME, need working space off the stack
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *sceneJson = json_parse_string(data);
+       JSON_Object *sceneJsonObject = json_object(sceneJson);
+
+       const char *soundbankName = json_object_get_string(sceneJsonObject, "soundbank");
+       if (soundbankName != NULL && audioEngine != NULL) {
+               scene->audioEngine = audioEngine;
+               bzAudioPlaybackUseSoundbank(scene->audioEngine, arena, soundbankName);
+       }
+
+       const char *spritesheetName = json_object_get_string(sceneJsonObject, "spritesheet");
+       if (spritesheetName != NULL) {
+               size_t imageWidth, imageHeight;
+               void *imageData = bzGfxLoadAsepriteImage(arena, &imageWidth, &imageHeight, spritesheetName); // FIXME, temp arena
+               bzGfxPrepareSpritesheet(arena, imageWidth, imageHeight, imageData);
+               //free(imageData);
+       }
+
+       const char *fontName = json_object_get_string(sceneJsonObject, "font");
+       BZFont *font = bzGfxLoadFont(arena, fontName); // FIXME, temp arena??
+       bzGfxPrepareFont(arena, font);
+
+       JSON_Array *actorDefinitionsArray = json_object_get_array(sceneJsonObject, "actors");
+       size_t actorDefinitionsArrayCount = json_array_get_count(actorDefinitionsArray);
+       scene->actorDefinitionCount = actorDefinitionsArrayCount;
+
+       size_t startDefinition = 0;
+       const char *sceneActorName = json_object_get_string(sceneJsonObject, "scene_actor");
+       if (sceneActorName != NULL) {
+               scene->actorDefinitionCount += 1;
+               startDefinition = 1;
+       }
+
+       scene->actorDefinitions = (BZSceneActorDefinition *)bzMemoryAlloc(arena, scene->actorDefinitionCount * sizeof(BZSceneActorDefinition));
+       
+       size_t maxInstanceSize = 0;
+
+       if (sceneActorName != NULL) {
+               bzGameSceneActorLoadDefinition(&scene->actorDefinitions[0], scene, arena, "%s", sceneActorName); // fixme
+               maxInstanceSize = scene->actorDefinitions[0].instanceSize;
+       }
+
+       for (size_t i = 0; i < actorDefinitionsArrayCount; ++i) {
+               const char *actorName = json_array_get_string(actorDefinitionsArray, i);
+               size_t outIdx = i + startDefinition;
+               bzGameSceneActorLoadDefinition(&scene->actorDefinitions[outIdx], scene, arena, "%s", actorName); // fixme
+               maxInstanceSize = bzMax(maxInstanceSize, scene->actorDefinitions[outIdx].instanceSize);
+       }
+
+       // FIXME, alignment for instance size
+
+       scene->lastActorIndex = 0;
+       scene->actorInstanceCount = 1024;
+
+       scene->actorInstances = (BZSceneActor *)bzMemoryAlloc(arena, scene->actorInstanceCount * sizeof(BZSceneActor));
+       for (size_t i = 0; i < scene->actorInstanceCount; ++i) {
+               scene->actorInstances[i].actor.instance = bzMemoryAlloc(arena, maxInstanceSize);
+       }
+
+       if (sceneActorName != NULL) {
+               bzGameSceneSetupActor(&scene->actorInstances[0], scene, &scene->actorDefinitions[0], "scene");
+       }
+
+//     int hasAgents = json_object_get_boolean(sceneJsonObject, "agents");
+//     if (hasAgents > 0) {
+//             scene->agentSimulation = bzFXCreateAgentSimulation(arena, scene->actorInstanceCount, "%s.agents", identifier);
+//     }
+
+       JSON_Array *layerDefinitionsArray = json_object_get_array(sceneJsonObject, "layers");
+       scene->layerCount = json_array_get_count(layerDefinitionsArray);
+       scene->layers = bzMemoryAlloc(arena, scene->layerCount * sizeof(BZSceneLayer));
+       for (size_t i = 0; i < scene->layerCount; ++i) {
+               JSON_Object *layerDefinition = json_array_get_object(layerDefinitionsArray, i);
+               stbsp_snprintf(scene->layers[i].identifier, kBZMaxIdentifierLength, "%s", json_object_get_string(layerDefinition, "identifier"));
+               scene->layers[i].identifierHash = bzIdentifierHashFromIdentifier(scene->layers[i].identifier);
+
+               const char *particleSystemName = json_object_get_string(layerDefinition, "particles");
+               if (particleSystemName != NULL) {
+                       BZParticleSystemID particleSystem = bzFXLoadParticleSystem(arena, "%s", particleSystemName); // FIXME, pool these...
+                       int hasCollision2 = json_object_get_boolean(layerDefinition, "collision"); // FIXME
+                       scene->layers[i].particleSimulation = bzFXCreateParticleSimulation(arena, (hasCollision2 > 0) ? 1024 : (5 * 1024), particleSystem, "%s.particles", scene->layers[i].identifier); // FIXME, memory
+               }
+
+               if (json_object_has_value_of_type(layerDefinition, "output", JSONNumber)) {
+                       scene->layers[i].outputBuffer = (int)json_object_get_number(layerDefinition, "output");
+                       if (particleSystemName == NULL || json_object_get_boolean(layerDefinition, "custom-particle") > 0) {
+                               scene->layers[i].drawQueue = bzGfxDrawQueueCreate(arena, "scene.%s.drawQueue", scene->layers[i].identifier);
+                       }
+               }
+
+               const char *tilemapName = json_object_get_string(layerDefinition, "tilemap");
+               if (tilemapName != NULL) {
+                       scene->layers[i].tilemap = bzGameLoadTilemap(arena, "%s", tilemapName);
+               }
+
+               int hasCollision = json_object_get_boolean(layerDefinition, "collision");
+               if (hasCollision > 0) {
+                       if (scene->layers[i].particleSimulation == NULL) {
+                               scene->layers[i].collisionSpace = bzCollisionMakeSpace(arena, scene->actorInstanceCount, "%s.collision", scene->layers[i].identifier);
+                       } else {
+                               scene->layers[i].collisionSpace = bzCollisionMakeSpace(arena, 1024, "%s.particle.collision", scene->layers[i].identifier);
+
+                               if (json_object_has_value_of_type(layerDefinition, "collision-radius", JSONNumber)) {
+                                       scene->layers[i].particleCollisionRadiusMultiplier = json_object_get_number(layerDefinition, "collision-radius");
+                               } else {
+                                       scene->layers[i].particleCollisionRadiusMultiplier = 1.0f;
+                               }
+                               scene->layers[i].particleCollisionTagHash = bzIdentifierHashFromIdentifier(particleSystemName);
+                       }
+               }
+       }
+
+       scene->currentEvents = bzMemoryAlloc(arena, kBZScriptingEnvironmentEventCount * sizeof(BZSceneEvent));
+       scene->enqueuedEvents = bzMemoryAlloc(arena, kBZScriptingEnvironmentEventCount * sizeof(BZSceneEvent));
+
+       json_value_free(sceneJson);
+
+       bzLog("Opened scene %s", identifier);
+
+       return scene;
+}
+
+bool bzGameSceneUpdate(BZSceneID scene, uint8_t pauseFlags, uint8_t systemTicks) {
+       float deltaTime = (float)systemTicks / (float)gSystemTicksPerSecond;
+
+       for (size_t i = 0; i < scene->layerCount; ++i) {
+               BZSceneLayer *layer = &scene->layers[i];
+               if (layer->particleSimulation == NULL) {
+                       bzGfxDrawQueueClear(layer->drawQueue);
+               }
+               if (layer->collisionSpace != NULL) {
+                       bzCollisionResetSpace(layer->collisionSpace);
+               }
+       }
+
+//     if (scene->agentSimulation != NULL) {
+//             SVMOperand playerX;
+//             bzScriptingGetEnvironmentPublic(&playerX, scene, bzIdentifierHashFromIdentifier("shipX"));
+//
+//             SVMOperand playerY;
+//             bzScriptingGetEnvironmentPublic(&playerY, scene, bzIdentifierHashFromIdentifier("shipY"));
+//
+//             BZVector playerPos = bzVectorMake(FIXED_TO_FLOAT(playerX.floatLiteral), FIXED_TO_FLOAT(playerY.floatLiteral));
+//
+//             bzFXUpdateAgentSimulation(scene->agentSimulation, deltaTime, NULL, &playerPos);
+//     }
+
+       for (size_t i = 0; i < scene->layerCount; ++i) {
+               BZSceneLayer *layer = &scene->layers[i];
+               if (layer->particleSimulation != NULL) {
+                       bzFXUpdateParticleSystem(layer->particleSimulation, deltaTime);
+                       if (layer->collisionSpace != NULL) {
+                               bzCollisionPopulateParticles(layer->collisionSpace, layer->particleSimulation, layer->particleCollisionTagHash, layer->particleCollisionRadiusMultiplier);
+                       }
+               }
+       }
+
+       for (int32_t priority = scene->minActorPriority; priority <= scene->maxActorPriority; ++priority) {
+               for (size_t i = 0; i < scene->actorInstanceCount; ++i) {
+                       BZSceneActor *sceneActor = &scene->actorInstances[i];
+                       if (sceneActor->running && sceneActor->priority == priority) {
+                               SVMRunOutcome outcome = svmRunModuleInstance(sceneActor->actor.instance);
+                               sceneActor->running = (outcome == SVMRunOutcomeSuspended);
+                       }
+               }
+       }
+       
+       scene->ticks += systemTicks;
+
+       scene->eventCount = scene->nextEnqueuedEvent;
+       scene->nextEnqueuedEvent = 0;
+
+       BZSceneEvent *tmp = scene->currentEvents;
+       scene->currentEvents = scene->enqueuedEvents;
+       scene->enqueuedEvents = tmp;
+
+       return scene->actorInstances[0].running;
+}
+
+void bzGameSceneDraw(BZSceneID scene) {
+       for (size_t i = 0; i < scene->layerCount; ++i) {
+               bzSetOutputBuffer(scene->layers[i].outputBuffer);
+               if (scene->layers[i].particleSimulation != NULL) {
+                       bzGfxDrawParticles(scene->layers[i].particleSimulation, scene->layers[i].drawQueue);
+               } else {
+                       bzGfxDrawQueueRun(scene->layers[i].drawQueue);
+               }
+               
+               //if (scene->layers[i].collisionSpace != NULL) {
+               //      bzCollisionDrawDebug(scene->layers[i].collisionSpace);
+               //}
+
+//             if (i == 3 && scene->agentSimulation != NULL) { // FIXME
+//                     bzFXAgentSimulationDrawDebug(scene->agentSimulation);
+//             }
+       }
+
+       
+}
+
+BZActorID bzGameSceneAddActor(BZSceneID scene, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZIdentifierHash definitionIdentifierHash = bzIdentifierHashFromIdentifier(identifier);
+       BZSceneActorDefinition *definition = NULL;
+       for (size_t i = 0; i < scene->actorDefinitionCount; ++i) { // fixme
+               if (definitionIdentifierHash == scene->actorDefinitions[i].identifierHash) {
+                       definition = &scene->actorDefinitions[i];
+                       break;
+               }
+       }
+       bzAssertMessage(definition != NULL, "Undefined actor '%s'", identifier);
+
+       BZSceneActor *sceneActor = NULL;
+       for (size_t i = 0; i < scene->actorInstanceCount; ++i) {
+               ++scene->lastActorIndex;
+
+               size_t idx = (scene->lastActorIndex % (scene->actorInstanceCount - 1)) + 1; // 0 is only for the scene script
+               if (scene->actorInstances[idx].running == false) {
+                       sceneActor = &scene->actorInstances[idx];
+                       break;
+               }
+       }
+       bzAssertMessage(sceneActor != NULL, "Out of actors");
+
+       definition->spawnCount += 1;
+
+       return bzGameSceneSetupActor(sceneActor, scene, definition, "%s-%u", identifier, definition->spawnCount);
+}
+
+BZActorID bzGameSceneFindActor(BZSceneID scene, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+       BZIdentifierHash identifierHash = bzIdentifierHashFromIdentifier(identifier);
+       for (size_t i = 0; i < scene->actorInstanceCount; ++i) { // fixme
+               if (scene->actorInstances[i].actor.identifierHash == identifierHash) {
+                       return &scene->actorInstances[i].actor;
+               }
+       }
+       return NULL;
+}
+
+float bzGameSceneGetTime(BZSceneID scene) {
+       float time = (float)scene->ticks / (float)gSystemTicksPerSecond;
+       return time;
+}
+
+bool bzGameSceneQueryEvent(BZSceneEventData *dataOut, BZSceneID scene, BZIdentifierHash eventIdentifier) {
+       for (size_t i = 0; i < scene->eventCount; ++i) {
+               BZSceneEvent *event = &scene->currentEvents[i];
+               if (event->identifierHash == eventIdentifier) {
+                       if (dataOut != NULL) {
+                               *dataOut = event->data;
+                       }
+                       return true;
+               }
+       }
+       return false;
+}
+
+void bzGameScenePostEvent(BZSceneID scene, BZIdentifierHash eventIdentifier, const BZSceneEventData *data) {
+       bzAssert(scene->nextEnqueuedEvent < kBZScriptingEnvironmentEventCount);
+       BZSceneEvent *event = &scene->enqueuedEvents[scene->nextEnqueuedEvent++];
+       event->identifierHash = eventIdentifier;
+       if (data != NULL) {
+               event->data = *data;
+       }
+}
+
+BZSceneLayer *getLayer(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
+       for (size_t i = 0; i < scene->layerCount; ++i) {
+               if (scene->layers[i].identifierHash == layerIdentifierHash) {
+                       return &scene->layers[i];
+               }
+       }
+       return NULL;
+}
+
+BZTilemapID bzGameGetSceneLayerTilemap(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
+       BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
+       if (layer != NULL) {
+               return layer->tilemap;
+       } else {
+               return NULL;
+       }
+}
+
+BZDrawQueueID bzGameGetSceneLayerDrawQueue(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
+       BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
+       if (layer != NULL) {
+               return layer->drawQueue;
+       } else {
+               return NULL;
+       }
+}
+
+BZParticleSimulationID bzGameGetSceneLayerParticleSimulation(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
+       BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
+       if (layer != NULL) {
+               return layer->particleSimulation;
+       } else {
+               return NULL;
+       }
+}
+
+BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace(BZSceneID scene, BZIdentifierHash layerIdentifierHash) {
+       BZSceneLayer *layer = getLayer(scene, layerIdentifierHash);
+       if (layer != NULL) {
+               return layer->collisionSpace;
+       } else {
+               return NULL;
+       }
+}
+
+
+/*
+extern void bzGameSceneSetTimeTicksPerSecond(uint8_t tps);
+extern void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags); // Pause timer on these flags
+extern float bzGameSceneTime(uint8_t timeIdx);
+
+void bzGameSceneSetTimeTicksPerSecond(uint8_t tps) {
+       sceneTPS = tps;
+}
+
+void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags) {
+       times[timeIdx].pauseFlags = pauseFlags;
+}
+
+float bzGameSceneTime(uint8_t timeIdx) {
+       float time = (float)times[timeIdx].ticks / (float)sceneTPS;
+       return time;
+}
+*/
+
+//BZAgentSimulationID bzGameGetAgentSimulation(BZSceneID scene) {
+//     return scene->agentSimulation;
+//}
+
+BZAudioPlaybackEngineID bzGameGetAudioPlaybackEngine(BZSceneID scene) {
+       return scene->audioEngine;
+}
+
+bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZSceneID scene, BZIdentifierHash identifierHash) {
+       for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) {
+               if (scene->publics[i].identifierHash == identifierHash) {
+                       if (out != NULL) {
+                               out->__raw = scene->publics[i].value.__raw;
+                       }
+                       return true;
+               }
+       }
+       return false;
+}
+
+void bzScriptingSetEnvironmentPublic(BZSceneID scene, BZIdentifierHash identifierHash, SVMOperand value) {
+       for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) {
+               if (scene->publics[i].identifierHash == 0) {
+                       scene->publics[i].identifierHash = identifierHash;
+               }
+               if (scene->publics[i].identifierHash == identifierHash) {
+                       scene->publics[i].value.__raw = value.__raw;
+                       return;
+               } 
+       }
+       bzError("Out of globals!!");
+}
+
+size_t bzGameGetSceneLayerCount(BZSceneID scene) {
+       return scene->layerCount;
+}
+
+BZSceneLayerID bzGameGetSceneLayerAtIndex(BZSceneID scene, size_t idx) {
+       bzAssert(idx < scene->layerCount);
+       return &scene->layers[idx];
+}
+
+
+BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace2(BZSceneLayerID layer) {
+       return layer->collisionSpace;
+}
+
+BZParticleSimulationID bzGameGetSceneLayerParticleSimulation2(BZSceneLayerID layer) {
+       return layer->particleSimulation;
+}
diff --git a/src/bz/game/scene.h b/src/bz/game/scene.h
new file mode 100644 (file)
index 0000000..6fb195a
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef BZ_GAME_SCENE_H
+#define BZ_GAME_SCENE_H
+
+#include <bz/audio/playback.h>
+#include <bz/game/actor.h>
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZSceneEventData {
+       union {
+               uint32_t integerValue;
+               const char *stringValue;
+               void *value;
+       };
+};
+typedef struct BZSceneEventData BZSceneEventData;
+
+typedef struct BZScene BZScene;
+typedef BZScene * BZSceneID;
+
+extern BZSceneID bzGameSceneSwitch(BZMemoryArenaID arena, BZAudioPlaybackEngineID audioEngine, const char *identifierFmt, ...);
+
+//extern void bzGameSceneGetTargetSceneIdentifier(char *identifierOut, BZSceneID scene); // We wipe our memory so we need to copy this out
+
+//.extern BZMemoryArenaID bzGameSceneAddActorExt(BZGameSceneActorConfiguration **configurationOutPtr, const char *identifierFmt, ...);
+extern bool bzGameSceneUpdate(BZSceneID scene, uint8_t pauseFlags, uint8_t systemTicks);
+extern void bzGameSceneDraw(BZSceneID scene);
+
+extern BZActorID bzGameSceneAddActor(BZSceneID scene, const char *identifierFmt, ...);
+extern BZActorID bzGameSceneFindActor(BZSceneID scene, const char *identifierFmt, ...);
+
+extern float bzGameSceneGetTime(BZSceneID scene);
+
+extern bool bzGameSceneQueryEvent(BZSceneEventData *dataOut, BZSceneID scene, BZIdentifierHash eventIdentifier);
+extern void bzGameScenePostEvent(BZSceneID scene, BZIdentifierHash eventIdentifier, const BZSceneEventData *data);
+
+extern BZIdentifierHash bzSceneChangeEventIdentifier;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/game/scene_identifiers.cpp b/src/bz/game/scene_identifiers.cpp
new file mode 100644 (file)
index 0000000..4a3ae46
--- /dev/null
@@ -0,0 +1,4 @@
+#include <bz/game/scene.h>
+#include <bz/scripting/static_crc32.hpp>
+
+BZIdentifierHash bzSceneChangeEventIdentifier = COMPILE_TIME_CRC32_STR("sceneChange");
diff --git a/src/bz/game/scene_internal.h b/src/bz/game/scene_internal.h
new file mode 100644 (file)
index 0000000..421bc4d
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef BZ_GAME_SCENE_INTERNAL_H
+#define BZ_GAME_SCENE_INTERNAL_H
+
+#include <bz/game/scene.h>
+
+#include <bz/collision/collision.h>
+//#include <bz/fx/agent_simulation.h>
+#include <bz/fx/particle_simulation.h>
+#include <bz/game/tilemap.h>
+#include <bz/gfx/draw_queue.h>
+#include <bz/math/matrix.h>
+#include <bz/types/identifier.h>
+#include <sun/vm/vm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZScriptBindingMetadata {
+       BZActorID actor;
+       BZSceneID scene;
+       BZIdentifierHash layer;
+       size_t sortIdx;
+       BZDrawQueueID drawQueue;
+       BZVector position;
+       float angle;
+       BZVector scale;
+       BZMatrix matrix;
+       uint32_t uuid;
+//     BZAgentID agent;
+};
+typedef struct BZScriptBindingMetadata BZScriptBindingMetadata;
+
+typedef struct BZSceneLayer BZSceneLayer;
+typedef BZSceneLayer * BZSceneLayerID;
+
+extern size_t bzGameGetSceneLayerCount(BZSceneID scene);
+extern BZSceneLayerID bzGameGetSceneLayerAtIndex(BZSceneID scene, size_t idx);
+
+extern BZParticleSimulationID bzGameGetSceneLayerParticleSimulation2(BZSceneLayerID layer); // FIXME, what do we want to do...
+extern BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace2(BZSceneLayerID layer);
+
+extern BZTilemapID bzGameGetSceneLayerTilemap(BZSceneID scene, BZIdentifierHash layerIdentifierHash);
+extern BZDrawQueueID bzGameGetSceneLayerDrawQueue(BZSceneID scene, BZIdentifierHash layerIdentifierHash);
+extern BZParticleSimulationID bzGameGetSceneLayerParticleSimulation(BZSceneID scene, BZIdentifierHash layerIdentifierHash);
+extern BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace(BZSceneID scene, BZIdentifierHash layerIdentifierHash);
+
+//extern BZAgentSimulationID bzGameGetAgentSimulation(BZSceneID scene);
+
+extern BZAudioPlaybackEngineID bzGameGetAudioPlaybackEngine(BZSceneID scene);
+
+extern void bzGameSceneSetTargetSceneIdentifier(BZSceneID scene, const char *identifier);
+
+extern bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZSceneID scene, BZIdentifierHash identifierHash);
+extern void bzScriptingSetEnvironmentPublic(BZSceneID scene, BZIdentifierHash identifierHash, SVMOperand value);
+
+extern void bzScriptingInitialiseMetadata(BZScriptBindingMetadata *metadata, BZActorID actor, BZSceneID scene, uint32_t uuid);
+
+extern uint8_t gSystemTicksPerSecond;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/game/tilemap.c b/src/bz/game/tilemap.c
new file mode 100644 (file)
index 0000000..3600fdb
--- /dev/null
@@ -0,0 +1,181 @@
+#include <bz/game/tilemap_internal.h>
+
+#include <bz/math/random.h>
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+#include <parson.h>
+
+static uint32_t tilemapAddTileTagFlag(BZTilemapID tilemap, BZIdentifierHash tag) {
+       for (size_t i = 0; i < 32; ++i) {
+               if (tag == tilemap->tags[i]) {
+                       return 1 << i;
+               }
+
+               if (tilemap->tags[i] == 0) {
+                       tilemap->tags[i] = tag;
+                       return 1 << i;
+               }
+       }
+
+       bzAssertMessage(false, "Out of flags");
+
+       return 0;
+}
+
+BZTilemapID bzGameLoadTilemap(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZResourceID handle = bzResourcesOpenResource("tilemap", "assets/tilemaps/%s.tilemap.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+       char *data = (char *)alloca(length); // FIXME, need working space off the stack
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *tilemapJson = json_parse_string(data);
+       JSON_Object *tilemapJsonObject = json_object(tilemapJson);
+
+       size_t tilemapWidth = json_object_get_number(tilemapJsonObject, "width");
+       size_t tilemapHeight = json_object_get_number(tilemapJsonObject, "height");
+
+       BZTilemap *tilemap = (BZTilemap *)bzMemoryAlloc(arena, sizeof(BZTilemap) + tilemapWidth * tilemapHeight * sizeof(BZTilemapTile));
+       tilemap->width = tilemapWidth;
+       tilemap->height = tilemapHeight;
+
+       JSON_Array *tilesArray = json_object_get_array(tilemapJsonObject, "tiles");
+       size_t tilesArraySize = json_array_get_count(tilesArray);
+       for (size_t y = 0, i = 0; y < tilemap->height; ++y) {
+               for (size_t x = 0; x < tilemap->width; ++x, ++i) {
+                       if (i < tilesArraySize) {
+                               uint8_t t = (uint8_t)json_array_get_number(tilesArray, i);
+                               tilemap->tiles[i].tileDefinitionIdx = t;
+                               tilemap->tiles[i].spriteIdx = 0;
+                               
+                               bzAssert(t < 256); // fixme, define
+                               tilemap->tileDefinitions[t].spriteIdx = t;
+                               tilemap->tileDefinitions[t].height = 1;
+                               tilemap->tileDefinitions[t].tagsFlag = 0;
+                       } else {
+                               tilemap->tiles[i].tileDefinitionIdx = 0;
+                               tilemap->tiles[i].spriteIdx = 0;
+                       }
+               }
+       }
+
+       JSON_Array *tileTagArray = json_object_get_array(tilemapJsonObject, "tags");
+       size_t tileTagArraySize = json_array_get_count(tileTagArray);
+       for (size_t i = 0; i < tileTagArraySize; ++i) {
+               JSON_Object *tileTagObject = json_array_get_object(tileTagArray, i);
+               BZIdentifierHash tag = bzIdentifierHashFromIdentifier(json_object_get_string(tileTagObject, "tag"));
+               
+               uint32_t tagsFlag = tilemapAddTileTagFlag(tilemap, tag);
+
+               JSON_Array *tileArray = json_object_get_array(tileTagObject, "tiles");
+               size_t tileArraySize = json_array_get_count(tileArray);
+               for (size_t j = 0; j < tileArraySize; ++j) {
+                       size_t t = (size_t)json_array_get_number(tileArray, j);
+                       bzAssert(t < 256); // fixme, define
+                       tilemap->tileDefinitions[t].tagsFlag |= tagsFlag;
+               }
+       }
+
+       JSON_Array *tileDefinitionArray = json_object_get_array(tilemapJsonObject, "definitions");
+       size_t tileDefinitionArraySize = json_array_get_count(tileDefinitionArray);
+       for (size_t i = 0; i < tileDefinitionArraySize; ++i) {
+               JSON_Object *tileDefinitionObject = json_array_get_object(tileDefinitionArray, i);
+               size_t tileIndex = json_object_get_number(tileDefinitionObject, "tile");
+               bzAssert(tileIndex < 256); // fixme
+               
+               if (json_object_has_value(tileDefinitionObject, "height")) {
+                       tilemap->tileDefinitions[tileIndex].height = json_object_get_number(tileDefinitionObject, "height");
+               }
+
+               if (json_object_has_value(tileDefinitionObject, "tags")) {
+                       JSON_Array *tileTagArray = json_object_get_array(tileDefinitionObject, "tags");
+                       size_t tileTagArraySize = json_array_get_count(tileTagArray);
+                       for (size_t j = 0; j < tileTagArraySize; ++j) {
+                               BZIdentifierHash tag = bzIdentifierHashFromIdentifier(json_array_get_string(tileTagArray, j));
+                               
+                               uint32_t tagsFlag = tilemapAddTileTagFlag(tilemap, tag);
+
+                               tilemap->tileDefinitions[tileIndex].tagsFlag |= tagsFlag;
+                       }
+               }
+
+               if (json_object_has_value(tileDefinitionObject, "sprite")) {
+                       JSON_Array *tileSpritesArray = json_object_get_array(tileDefinitionObject, "sprite");
+                       size_t tileSpritesArraySize = json_array_get_count(tileSpritesArray);
+                       for (size_t y = 0, j = 0; y < tilemap->height; ++y) {
+                               for (size_t x = 0; x < tilemap->width; ++x, ++j) {
+                                       if (tilemap->tiles[j].tileDefinitionIdx == tileIndex) {
+                                               tilemap->tiles[j].spriteIdx = (uint8_t)json_array_get_number(tileSpritesArray, bzRandomInteger(tileSpritesArraySize));
+                                       }
+                               }
+                       }
+               }
+       }
+
+       json_value_free(tilemapJson);
+
+       return tilemap;
+}
+
+uint8_t bzGameGetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my) {
+       size_t tileIdx = my * tilemap->width + mx;
+
+       //uint8_t spriteIdx = 0;
+
+       uint8_t tileDefinitionIdx = tilemap->tiles[tileIdx].tileDefinitionIdx;
+       /*if (tileDefinitionIdx > 0) {
+               spriteIdx = tilemap->tiles[tileIdx].spriteIdx;
+               if (spriteIdx == 0) {
+                       spriteIdx = tilemap->tileDefinitions[tileDefinitionIdx].spriteIdx;
+               }
+       }*/
+
+       return tileDefinitionIdx;
+}
+
+uint8_t bzGameGetTilemapSprite(BZTilemapID tilemap, uint8_t mx, uint8_t my) {
+       size_t tileIdx = my * tilemap->width + mx;
+       
+       uint8_t spriteIdx = 0;
+
+       uint8_t tileDefinitionIdx = bzGameGetTilemapTile(tilemap, mx, my);
+       if (tileDefinitionIdx > 0) {
+               spriteIdx = tilemap->tiles[tileIdx].spriteIdx;
+               if (spriteIdx == 0) {
+                       spriteIdx = tilemap->tileDefinitions[tileDefinitionIdx].spriteIdx;
+               }
+       }
+
+       return spriteIdx;
+}
+
+uint8_t bzGameSetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my, uint8_t idx) {
+       size_t tileIdx = my * tilemap->width + mx;
+       tilemap->tiles[tileIdx].spriteIdx = idx;
+       return bzGameGetTilemapTile(tilemap, mx, my);
+}
+
+bool bzGameGetTilemapHasFlag(BZTilemapID tilemap, uint8_t mx, uint8_t my, BZIdentifierHash tag) {
+       uint32_t flagMask = bzGameCalculateTilemapFlagsMask(tilemap, 1, &tag);
+       uint8_t tileDefinitionIdx = bzGameGetTilemapTile(tilemap, mx, my);
+       return (tilemap->tileDefinitions[tileDefinitionIdx].tagsFlag & flagMask) > 0;
+}
+
+uint32_t bzGameCalculateTilemapFlagsMask(BZTilemapID tilemap, size_t tagCount, BZIdentifierHash tags[]) {
+       uint32_t flagMask = 0;
+
+       for (size_t i = 0; i < tagCount; ++i) {
+               for (size_t j = 0; j < 32; ++j) {
+                       if (tags[i] == tilemap->tags[j]) {
+                               flagMask |= 1 << j;
+                               break;
+                       }
+               }
+       }
+
+       return flagMask;
+}
diff --git a/src/bz/game/tilemap.h b/src/bz/game/tilemap.h
new file mode 100644 (file)
index 0000000..8e625d5
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef BZ_GAME_TILEMAP_H
+#define BZ_GAME_TILEMAP_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZTilemap BZTilemap;
+typedef BZTilemap * BZTilemapID;
+
+extern BZTilemapID bzGameLoadTilemap(BZMemoryArenaID arena, const char *identifierFmt, ...);
+
+extern uint8_t bzGameGetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my);
+extern uint8_t bzGameSetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my, uint8_t idx);
+
+extern uint8_t bzGameGetTilemapSprite(BZTilemapID tilemap, uint8_t mx, uint8_t my);
+extern bool bzGameGetTilemapHasFlag(BZTilemapID tilemap, uint8_t mx, uint8_t my, BZIdentifierHash tag);
+
+extern uint32_t bzGameCalculateTilemapFlagsMask(BZTilemapID tilemap, size_t tagCount, BZIdentifierHash tags[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/game/tilemap_internal.h b/src/bz/game/tilemap_internal.h
new file mode 100644 (file)
index 0000000..89ba298
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef BZ_GAME_TILEMAP_INTERNAL_H
+#define BZ_GAME_TILEMAP_INTERNAL_H
+
+#include <bz/game/tilemap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZTilemapTileDefinition {
+       //bool replaceSprite;
+       uint8_t spriteIdx;
+       int height;
+       uint32_t tagsFlag;
+};
+typedef struct BZTilemapTileDefinition BZTilemapTileDefinition;
+
+struct BZTilemapTile {
+       uint8_t spriteIdx;
+       uint8_t tileDefinitionIdx;
+};
+typedef struct BZTilemapTile BZTilemapTile;
+
+struct BZTilemap {
+       size_t width;
+       size_t height;
+       BZIdentifierHash tags[32];
+       BZTilemapTileDefinition tileDefinitions[256];
+       BZTilemapTile tiles[];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/aseprite.c b/src/bz/gfx/aseprite.c
new file mode 100644 (file)
index 0000000..69dc446
--- /dev/null
@@ -0,0 +1,467 @@
+#include <bz/gfx/aseprite_internal.h>
+
+#include <bz/math/math.h>
+#include <bz/memory/allocator.h>
+#include <bz/renderer/palette_internal.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+#include <stdio.h> // Apparently stb_image.h need this??
+#include <stb_image.h>
+
+#include <assert.h> // Static assertions
+
+#define PACKED_STRUCT __attribute__((__packed__))
+
+typedef uint8_t  ASE_BYTE;
+typedef uint16_t ASE_WORD;
+typedef int16_t  ASE_SHORT;
+typedef uint32_t ASE_DWORD;
+typedef uint32_t ASE_LONG;
+typedef uint32_t ASE_FIXED;
+typedef float    ASE_FLOAT;
+typedef double   ASE_DOUBLE;
+typedef uint64_t ASE_QWORD;
+typedef int64_t  ASE_LONG64;
+
+
+
+struct PACKED_STRUCT ASE_STRING {
+       ASE_WORD length;
+       //ASE_BYTE characters[];
+};
+typedef struct ASE_STRING ASE_STRING;
+static_assert(sizeof(ASE_STRING) == 2, "ASE_STRING is wrong size");
+
+struct PACKED_STRUCT ASE_POINT {
+       ASE_LONG x;
+       ASE_LONG y;
+};
+typedef struct ASE_POINT ASE_POINT;
+static_assert(sizeof(ASE_POINT) == 8, "ASE_POINT is wrong size");
+
+struct PACKED_STRUCT ASE_SIZE {
+       ASE_LONG width;
+       ASE_LONG height;
+};
+typedef struct ASE_SIZE ASE_SIZE;
+static_assert(sizeof(ASE_SIZE) == 8, "ASE_SIZE is wrong size");
+
+struct PACKED_STRUCT ASE_RECT {
+       ASE_POINT origin;
+       ASE_SIZE size;
+};
+typedef struct ASE_RECT ASE_RECT;
+static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size");
+
+struct PACKED_STRUCT ASE_PIXEL_RBZA {
+       ASE_BYTE r;
+       ASE_BYTE g;
+       ASE_BYTE b;
+       ASE_BYTE a;
+};
+typedef struct ASE_PIXEL_RBZA ASE_PIXEL_RBZA;
+static_assert(sizeof(ASE_PIXEL_RBZA) == 4, "ASE_PIXEL_RBZA is wrong size");
+
+struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE {
+       ASE_BYTE v;
+       ASE_BYTE a;
+};
+typedef struct ASE_PIXEL_GRAYSCALE ASE_PIXEL_GRAYSCALE;
+static_assert(sizeof(ASE_PIXEL_GRAYSCALE) == 2, "ASE_PIXEL_GRAYSCALE is wrong size");
+
+struct PACKED_STRUCT ASE_PIXEL_INDEXED {
+       ASE_BYTE idx;
+};
+typedef struct ASE_PIXEL_INDEXED ASE_PIXEL_INDEXED;
+static_assert(sizeof(ASE_PIXEL_INDEXED) == 1, "ASE_PIXEL_INDEXED is wrong size");
+
+struct PACKED_STRUCT ASE_Header {
+       ASE_DWORD fileSize;
+       ASE_WORD magicNumber; // 0xA5E0
+       ASE_WORD frames;
+       ASE_WORD width;
+       ASE_WORD height;
+       ASE_WORD depth;
+       ASE_DWORD flags;
+       ASE_WORD ___speed;
+       ASE_DWORD zero1;
+       ASE_DWORD zero2;
+       ASE_BYTE transparentColorIdx;
+       ASE_BYTE ___ignore[3];
+       ASE_WORD numColors;
+       ASE_BYTE pixelWidth;
+       ASE_BYTE pixelHeight;
+       ASE_SHORT gridX;
+       ASE_SHORT gridY;
+       ASE_WORD gridWidth;
+       ASE_WORD gridHeight;
+       ASE_BYTE ___reserved[84];
+};
+typedef struct ASE_Header ASE_Header;
+static_assert(sizeof(ASE_Header) == 128, "ASE_Header is wrong size");
+
+struct PACKED_STRUCT ASE_Frame {
+       ASE_DWORD frameBytes;
+       ASE_WORD magicNumber; // 0xF1FA
+       ASE_WORD ___numChunks;
+       ASE_WORD duration;
+       ASE_BYTE ___reserved[2];
+       ASE_DWORD numChunks;
+};
+typedef struct ASE_Frame ASE_Frame;
+static_assert(sizeof(ASE_Frame) == 16, "ASE_Frame is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk {
+       ASE_DWORD chunkSize;
+       ASE_WORD type;
+       //ASE_BYTE data[];
+};
+typedef struct ASE_Chunk ASE_Chunk;
+static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size");
+
+#if 0
+struct PACKED_STRUCT ASE_Chunk_OldPalette {
+       ASE_WORD numPackets;
+       //BZAsepriteOldPalettePacket packets[];
+};
+typedef struct ASE_Chunk_OldPalette ASE_Chunk_OldPalette;
+static_assert(sizeof(ASE_Chunk_OldPalette) == 2, "ASE_Chunk_OldPalette is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet {
+       ASE_BYTE skip;
+       ASE_BYTE numColors;
+       //BZAsepriteOldPaletteColor colors[];
+};
+typedef struct ASE_Chunk_OldPalette_Packet ASE_Chunk_OldPalette_Packet;
+static_assert(sizeof(ASE_Chunk_OldPalette_Packet) == 2, "ASE_Chunk_OldPalette_Packet is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet_Color {
+       ASE_BYTE r;
+       ASE_BYTE g;
+       ASE_BYTE b;
+};
+typedef struct ASE_Chunk_OldPalette_Packet_Color ASE_Chunk_OldPalette_Packet_Color;
+static_assert(sizeof(ASE_Chunk_OldPalette_Packet_Color) == 3, "ASE_Chunk_OldPalette_Packet_Color is wrong size");
+#endif
+
+struct PACKED_STRUCT ASE_Chunk_Layer {
+       ASE_WORD flags;
+       ASE_WORD layerType;
+       ASE_WORD childLevel;
+       ASE_WORD width;
+       ASE_WORD height;
+       ASE_WORD blendMode;
+       ASE_BYTE opacity;
+       ASE_BYTE ___reserved[3];
+       ASE_STRING name;
+};
+typedef struct ASE_Chunk_Layer ASE_Chunk_Layer;
+static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Layer_Tileset {
+       ASE_DWORD tilesetIndex;
+};
+typedef struct ASE_Chunk_Layer_Tileset ASE_Chunk_Layer_Tileset;
+static_assert(sizeof(ASE_Chunk_Layer_Tileset) == 4, "ASE_Chunk_Layer_Tileset is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Cel {
+       ASE_WORD layerIndex;
+       ASE_SHORT positionX;
+       ASE_SHORT positionY;
+       ASE_BYTE opacity;
+       ASE_WORD celType;
+       ASE_SHORT zIndex;
+       ASE_BYTE ___reserved[5];
+};
+typedef struct ASE_Chunk_Cel ASE_Chunk_Cel;
+static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Cel_RawImage {
+       ASE_WORD pixelWidth;
+       ASE_WORD pixelHeight;
+       //ASE_BYTE pixelData[];
+};
+typedef struct ASE_Chunk_Cel_RawImage ASE_Chunk_Cel_RawImage;
+static_assert(sizeof(ASE_Chunk_Cel_RawImage) == 4, "ASE_Chunk_Cel_RawImage is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Cel_LinkedCel {
+       ASE_WORD framePosition;
+};
+typedef struct ASE_Chunk_Cel_LinkedCel ASE_Chunk_Cel_LinkedCel;
+static_assert(sizeof(ASE_Chunk_Cel_LinkedCel) == 2, "ASE_Chunk_Cel_LinkedCel is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Cel_CompressedImage {
+       ASE_WORD pixelWidth;
+       ASE_WORD pixelHeight;
+       //ASE_BYTE pixelData[];
+};
+typedef struct ASE_Chunk_Cel_CompressedImage ASE_Chunk_Cel_CompressedImage;
+static_assert(sizeof(ASE_Chunk_Cel_CompressedImage) == 4, "ASE_Chunk_Cel_CompressedImage is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Cel_CompressedTilemap {
+       ASE_WORD tileWidth;
+       ASE_WORD tileHeight;
+       ASE_WORD tileBits;
+       ASE_DWORD tileIDBitmap;
+       ASE_DWORD flipXBitmap;
+       ASE_DWORD flipYBitmap;
+       ASE_DWORD rotationBitmap;
+       ASE_BYTE ___reserved[10];
+       //ASE_DWORD tileData[];
+};
+typedef struct ASE_Chunk_Cel_CompressedTilemap ASE_Chunk_Cel_CompressedTilemap;
+static_assert(sizeof(ASE_Chunk_Cel_CompressedTilemap) == 32, "ASE_Chunk_Cel_CompressedTilemap is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Cel_Extra {
+       ASE_DWORD flags;
+       ASE_FIXED positionX;
+       ASE_FIXED positionY;
+       ASE_FIXED width;
+       ASE_FIXED height;
+       ASE_BYTE ___reserved[16];
+};
+typedef struct ASE_Chunk_Cel_Extra ASE_Chunk_Cel_Extra;
+static_assert(sizeof(ASE_Chunk_Cel_Extra) == 36, "ASE_Chunk_Cel_Extra is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_ColorProfile {
+       ASE_WORD type;
+       ASE_WORD flags;
+       ASE_FIXED gamma;
+       ASE_BYTE ___reserved[8];
+};
+typedef struct ASE_Chunk_ColorProfile ASE_Chunk_ColorProfile;
+static_assert(sizeof(ASE_Chunk_ColorProfile) == 16, "ASE_Chunk_ColorProfile is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_ColorProfile_ICC {
+       ASE_DWORD length;
+       //ASE_BYTE data[];
+};
+typedef struct ASE_Chunk_ColorProfile_ICC ASE_Chunk_ColorProfile_ICC;
+static_assert(sizeof(ASE_Chunk_ColorProfile_ICC) == 4, "ASE_Chunk_ColorProfile_ICC is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_ExternalFiles {
+       ASE_DWORD entries;
+       ASE_BYTE ___reserved[8];
+};
+typedef struct ASE_Chunk_ExternalFiles ASE_Chunk_ExternalFiles;
+static_assert(sizeof(ASE_Chunk_ExternalFiles) == 12, "ASE_Chunk_ExternalFiles is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry {
+       ASE_DWORD entryID;
+       ASE_BYTE type;
+       ASE_BYTE ___reserved[7];
+       ASE_STRING externalFilename;
+};
+typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry;
+static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Mask {
+       ASE_SHORT positionX;
+       ASE_SHORT positionY;
+       ASE_WORD width;
+       ASE_WORD height;
+       ASE_BYTE ___reserved[8];
+       ASE_STRING maskName;
+};
+typedef struct ASE_Chunk_Mask ASE_Chunk_Mask;
+static_assert(sizeof(ASE_Chunk_Mask) == 18, "ASE_Chunk_Mask is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Tags {
+       ASE_WORD numTags;
+       ASE_BYTE ___reserved[8];
+};
+typedef struct ASE_Chunk_Tags ASE_Chunk_Tags;
+static_assert(sizeof(ASE_Chunk_Tags) == 10, "ASE_Chunk_Tags is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Tags_Tag {
+       ASE_WORD fromFrame;
+       ASE_WORD toFrame;
+       ASE_BYTE loopDirection;
+       ASE_WORD repeat;
+       ASE_BYTE ___reserved[6];
+       ASE_BYTE ___tagRBZ[3];
+       ASE_BYTE ___extra;
+       ASE_STRING tagName;
+};
+typedef struct ASE_Chunk_Tags_Tag ASE_Chunk_Tags_Tag;
+static_assert(sizeof(ASE_Chunk_Tags_Tag) == 19, "ASE_Chunk_Tags_Tag is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Palette {
+       ASE_DWORD paletteSize;
+       ASE_DWORD firstColorIdx;
+       ASE_DWORD lastColorIdx;
+       ASE_BYTE ___reserved[8];
+};
+typedef struct ASE_Chunk_Palette ASE_Chunk_Palette;
+static_assert(sizeof(ASE_Chunk_Palette) == 20, "ASE_Chunk_Palette is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Palette_Entry {
+       ASE_WORD flags;
+       ASE_BYTE r;
+       ASE_BYTE g;
+       ASE_BYTE b;
+       ASE_BYTE a;
+};
+typedef struct ASE_Chunk_Palette_Entry ASE_Chunk_Palette_Entry;
+static_assert(sizeof(ASE_Chunk_Palette_Entry) == 6, "ASE_Chunk_Palette_Entry is wrong size");
+
+struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name {
+       ASE_STRING name;
+};
+typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name;
+static_assert(sizeof(ASE_Chunk_Palette_Entry_Name) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size");
+
+void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier);
+       //PHYSFS_sint64 length = PHYSFS_fileLength(handle);
+       //void *data = alloca(length);
+       //PHYSFS_readBytes(handle, data, length);
+       
+       ASE_Header header;
+       bzResourcesReadBytes(handle, &header, sizeof(header));
+       
+       bzAssert(header.magicNumber == 0xA5E0);
+
+       size_t pixelDataSize;
+       switch (header.depth) {
+               case 8:
+                       pixelDataSize = sizeof(ASE_PIXEL_INDEXED);
+                       break;
+
+               case 16:
+                       pixelDataSize = sizeof(ASE_PIXEL_GRAYSCALE);
+                       break;
+
+               case 32:
+                       pixelDataSize = sizeof(ASE_PIXEL_RBZA);
+                       break;
+       
+               default:
+                       assert(false);//, "Invalid image");
+                       break;
+       }
+
+       uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.width * header.height * pixelDataSize);
+       *widthOut = (size_t)header.width;
+       *heightOut = (size_t)header.height;
+
+       for (ASE_WORD frame = 0; frame < header.frames; ++frame) {
+               ASE_Frame frame;
+               bzResourcesReadBytes(handle, &frame, sizeof(frame));
+
+               for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) {
+                       size_t chunkStartPosition = bzResourcesTell(handle);
+
+                       ASE_Chunk chunk;
+                       bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
+
+                       switch (chunk.type) {
+                               case 0x2005: {
+                                       ASE_Chunk_Cel cel;
+                                       bzResourcesReadBytes(handle, &cel, sizeof(cel));
+
+                                       if (cel.celType == 2) {
+                                               bzMemoryArenaPushWatermark(arena);
+
+                                               ASE_Chunk_Cel_CompressedImage image;
+                                               bzResourcesReadBytes(handle, &image, sizeof(image));
+
+                                               size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk);
+                                               int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode!
+                                               bzResourcesReadBytes(handle, compressedData, compressedSize);
+
+                                               size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize;
+                                               int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize);
+                                               stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize);
+
+                                               for (size_t y = 0; y < image.pixelHeight; ++y) {
+                                                       for (size_t x = 0; x < image.pixelWidth; ++x) {
+                                                               size_t outX = cel.positionX + x;
+                                                               size_t outY = cel.positionY + y;
+
+                                                               for (size_t i = 0; i < pixelDataSize; ++i) {
+                                                                       outData[(outY * header.width + outX) * pixelDataSize + i] = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i];
+                                                               }
+                                                       }
+                                               }
+
+                                               bzMemoryArenaPopWatermark(arena);
+
+                                               // FIXME
+                                               //bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
+                                       } else {
+                                               // Skip this chunk...
+                                               bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
+                                       }
+
+                                       break;
+                               }
+                               
+                               default:
+                                       // Skip this chunk...
+                                       bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
+                                       break;
+                       }
+               }
+       }
+       
+       bzResourcesCloseResource(handle);
+
+       return outData;
+}
+
+size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename) {
+       BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename);
+       
+       ASE_Header header;
+       bzResourcesReadBytes(handle, &header, sizeof(header));
+       
+       bzAssert(header.magicNumber == 0xA5E0);
+
+       size_t colorCount = 0;
+
+       for (ASE_WORD frame = 0; frame < header.frames; ++frame) {
+               ASE_Frame frame;
+               bzResourcesReadBytes(handle, &frame, sizeof(frame));
+
+               for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) {
+                       size_t chunkStartPosition = bzResourcesTell(handle);
+
+                       ASE_Chunk chunk;
+                       bzResourcesReadBytes(handle, &chunk, sizeof(chunk));
+
+                       switch (chunk.type) {
+                               case 0x2019: {
+                                       ASE_Chunk_Palette palette;
+                                       bzResourcesReadBytes(handle, &palette, sizeof(palette));
+
+                                       if (palette.firstColorIdx < maxColors) {
+                                               size_t lastIdx = bzMin(palette.lastColorIdx + 1, maxColors);
+                                               for (size_t i = palette.firstColorIdx; i < lastIdx; ++i) {
+                                                       ASE_Chunk_Palette_Entry paletteEntry;
+                                                       bzResourcesReadBytes(handle, &paletteEntry, sizeof(paletteEntry));
+                                                       colorsOut[i] = bzPaletteMakeColor(paletteEntry.r, paletteEntry.g, paletteEntry.b);
+                                               }
+                                               colorCount = bzMax(colorCount, lastIdx);
+                                       }
+                                       
+                                       // FIXME
+                                       bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
+
+                                       break;
+                               }
+                               
+                               default:
+                                       // Skip this chunk...
+                                       bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize);
+                                       break;
+                       }
+               }
+       }
+       
+       bzResourcesCloseResource(handle);
+
+       return colorCount;
+}
\ No newline at end of file
diff --git a/src/bz/gfx/aseprite.h b/src/bz/gfx/aseprite.h
new file mode 100644 (file)
index 0000000..a5d540c
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_GFX_ASEPRITE_H
+#define BZ_GFX_ASEPRITE_H
+
+#include <bz/memory/arena.h>
+#include <bz/renderer/palette.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/aseprite_internal.h b/src/bz/gfx/aseprite_internal.h
new file mode 100644 (file)
index 0000000..bc69849
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_GFX_ASEPRITE_INTERNAL_H
+#define BZ_GFX_ASEPRITE_INTERNAL_H
+
+#include <bz/gfx/aseprite.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/draw_queue.c b/src/bz/gfx/draw_queue.c
new file mode 100644 (file)
index 0000000..a070d60
--- /dev/null
@@ -0,0 +1,112 @@
+#include <bz/gfx/draw_queue.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/memory/arena_internal.h>
+#include <string.h> // memcpy
+
+static BZMemoryArena instructionArena;
+
+struct BZDrawQueueInstruction {
+       //BZRect cullRect;
+       BZDrawQueueCall drawCall;
+       void *userParameter;
+
+       uint8_t sortIdx;
+       struct BZDrawQueueInstruction *sortedNextInstruction;
+};
+typedef struct BZDrawQueueInstruction BZDrawQueueInstruction;
+
+// FIXME, make this better?? Dynamic??
+#define kNumberDrawInstructions 512
+#define kDrawInstructionMemory 32
+
+struct BZDrawQueue {
+       size_t enqueuedInstructions;
+       BZDrawQueueInstruction instructions[kNumberDrawInstructions];
+       uint8_t *instructionMemory;
+       BZMemoryArena instructionArena;
+       BZDrawQueueInstruction *sortedFirstInstruction[kNumberDrawInstructions];
+       BZDrawQueueInstruction *sortedLastInstruction[kNumberDrawInstructions];
+};
+
+#define kInstructionMemorySize kNumberDrawInstructions * kDrawInstructionMemory
+
+//static size_t numDrawQueues = 0;
+//static BZDrawQueue drawQueues[300];
+
+BZDrawQueueID bzGfxDrawQueueCreate(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+//     bzLog("Creating queue size %d", sizeof(BZDrawQueue));
+       BZDrawQueueID drawQueue = (BZDrawQueueID)bzMemoryAlloc(arena, sizeof(BZDrawQueue));
+       bzAssert(drawQueue != NULL);
+       drawQueue->instructionMemory = bzMemoryAlloc(arena, kInstructionMemorySize);
+       bzMemoryArenaSetup(&drawQueue->instructionArena, drawQueue->instructionMemory, kInstructionMemorySize, false); // Don't zero this out because it's just some temp memory stuff
+       bzGfxDrawQueueClear(drawQueue);
+       return drawQueue;
+//     return &drawQueues[numDrawQueues++];
+}
+
+void bzGfxDrawQueueTeardown(BZDrawQueueID drawQueue) {
+
+}
+
+size_t bzGfxDrawQueueEnqueuedInstructionCount(BZDrawQueueID drawQueue) {
+       return drawQueue->enqueuedInstructions;
+}
+
+void bzGfxDrawQueueClear(BZDrawQueueID drawQueue) {
+       drawQueue->enqueuedInstructions = 0;
+       
+       for (size_t i = 0; i < kNumberDrawInstructions; ++i) {
+               drawQueue->sortedFirstInstruction[i] = NULL;
+               drawQueue->sortedLastInstruction[i] = NULL;
+       }
+
+       bzMemoryArenaReset(&drawQueue->instructionArena);
+}
+
+/*void bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, BZDrawQueueCall drawCall, void *userParameter) {
+       BZDrawQueueInstruction *instruction = &drawQueue->instructions[drawQueue->enqueuedInstructions++];
+       instruction->drawCall = drawCall;
+       instruction->userParameter = userParameter;
+}*/
+
+
+extern void *bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall, size_t userParameterSize) {
+       bzAssert(drawQueue->enqueuedInstructions < kNumberDrawInstructions);
+
+       BZDrawQueueInstruction *instruction = &drawQueue->instructions[drawQueue->enqueuedInstructions++];
+       instruction->sortedNextInstruction = NULL;
+       //instruction->cullRect = cullRect;
+       instruction->drawCall = drawCall;
+       instruction->userParameter = bzMemoryAlloc(&drawQueue->instructionArena, userParameterSize);
+
+       if (drawQueue->sortedFirstInstruction[sortIdx] == NULL) {
+               drawQueue->sortedFirstInstruction[sortIdx] = instruction;
+               drawQueue->sortedLastInstruction[sortIdx] = instruction;
+       } else {
+               drawQueue->sortedLastInstruction[sortIdx]->sortedNextInstruction = instruction;
+               drawQueue->sortedLastInstruction[sortIdx] = instruction;
+       }
+
+       return instruction->userParameter;
+}
+
+void bzGfxDrawQueueRun(BZDrawQueueID drawQueue) {
+       for (size_t i = 0; i < kNumberDrawInstructions; ++i) {
+               bzGfxDrawQueueRunSortBin(drawQueue, i);
+       }
+
+//     for (size_t i = 0; i < drawQueue->enqueuedInstructions; ++i) {
+//             //if (true) { // FIXME, cull
+//                     drawQueue->instructions[i].drawCall(drawQueue->instructions[i].userParameter);
+//             //}
+//     }
+}
+
+void bzGfxDrawQueueRunSortBin(BZDrawQueueID drawQueue, uint8_t sortIdx) {
+       BZDrawQueueInstruction *instruction = drawQueue->sortedFirstInstruction[sortIdx];
+       while (instruction != NULL) {
+               instruction->drawCall(instruction->userParameter);
+               instruction = instruction->sortedNextInstruction;
+       }
+}
diff --git a/src/bz/gfx/draw_queue.h b/src/bz/gfx/draw_queue.h
new file mode 100644 (file)
index 0000000..5880fc7
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef BZ_GFX_DRAW_QUEUE_H
+#define BZ_GFX_DRAW_QUEUE_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZDrawQueue BZDrawQueue;
+typedef BZDrawQueue * BZDrawQueueID;
+
+typedef void (*BZDrawQueueCall)(void *userParameter);
+typedef void (*BZDrawQueueSortFunction)(void);
+
+extern BZDrawQueueID bzGfxDrawQueueCreate(BZMemoryArenaID arena, const char *identifierFmt, ...);
+extern void bzGfxDrawQueueTeardown(BZDrawQueueID drawQueue);
+
+extern void bzGfxDrawQueueSetSortFunction(BZDrawQueueID drawQueue, BZDrawQueueSortFunction sortFunction); // FIXME, use this for map thing.
+
+extern size_t bzGfxDrawQueueEnqueuedInstructionCount(BZDrawQueueID drawQueue);
+
+extern void bzGfxDrawQueueClear(BZDrawQueueID drawQueue);
+//extern void bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, BZRect cullRect, BZDrawQueueCall drawCall, void *userParameter);
+//extern BZMemoryArenaID bzGfxDrawQueueEnqueueExt(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall);
+//extern void bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall, size_t userParameterSize, void *userParameter);
+extern void *bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall, size_t userParameterSize);
+
+extern void bzGfxDrawQueueRun(BZDrawQueueID drawQueue);
+extern void bzGfxDrawQueueRunSortBin(BZDrawQueueID drawQueue, uint8_t sortIdx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/drawing.c b/src/bz/gfx/drawing.c
new file mode 100644 (file)
index 0000000..4fcc6cb
--- /dev/null
@@ -0,0 +1,148 @@
+#include <bz/gfx/drawing.h>
+
+#include <bz/math/math.h>
+#include <bz/gfx/gfx.h>
+#include <bz/gfx/tilemap.h>
+#include <string.h>
+
+#define DRAW_DATA(name, members, ...) \
+       struct data##name { members; }; typedef struct data##name data##name; \
+       static void dispatch##name(void *userParam) { data##name *data = (data##name *)userParam; name(__VA_ARGS__); } \
+
+#define DRAW_DATA_DISPATCH(drawQueue, name, ...) \
+       if (drawQueue != NULL) { \
+               data##name *data = (data##name *)bzGfxDrawQueueEnqueue(drawQueue, sortIdx, dispatch##name, sizeof(data##name)); \
+               *data = (data##name){ __VA_ARGS__ }; \
+       } else {  \
+               name(__VA_ARGS__); \
+       }
+
+DRAW_DATA(bzPSet, float x; float y; int c, data->x, data->y, data->c)
+DRAW_DATA(bzCls, uint8_t c, data->c)
+DRAW_DATA(bzCamera, float x; float y, data->x, data->y)
+DRAW_DATA(bzCirc, int x; int y; int r; int c, data->x, data->y, data->r, data->c)
+DRAW_DATA(bzCircFill, int x; int y; int r; int c, data->x, data->y, data->r, data->c)
+DRAW_DATA(bzLine, int x0; int y0; int x1; int y1; int c, data->x0, data->y0, data->x1, data->y1, data->c)
+DRAW_DATA(bzRect, int x0; int y0; int x1; int y1; int c, data->x0, data->y0, data->x1, data->y1, data->c)
+DRAW_DATA(bzRectFill, int x0; int y0; int x1; int y1; int c, data->x0, data->y0, data->x1, data->y1, data->c)
+DRAW_DATA(bzTriangle, int x0; int y0; int x1; int y1; int x2; int y2; int c, data->x0, data->y0, data->x1, data->y1, data->x2, data->y2, data->c)
+DRAW_DATA(bzSpr, int n; int x; int y, data->n, data->x, data->y)
+DRAW_DATA(bzSprExt, int n; int x; int y; int w; int h; bool flipX; bool flipY, data->n, data->x, data->y, data->w, data->h, data->flipX, data->flipY)
+DRAW_DATA(bzSSpr, int sx; int sy; int sw; int sh; int dx; int dy, data->sx, data->sy, data->sw, data->sh, data->dx, data->dy)
+DRAW_DATA(bzSSprExt, int sx; int sy; int sw; int sh; int dx; int dy; int dw; int dh; bool flipX; bool flipY, data->sx, data->sy, data->sw, data->sh, data->dx, data->dy, data->dw, data->dh, data->flipX, data->flipY)
+DRAW_DATA(bzPrint, int x; int y; int c; char string[], data->x, data->y, data->c, data->string)
+DRAW_DATA(bzFillP, uint16_t p, data->p)
+DRAW_DATA(bzSetPaletteColor, int palette; int colorIdx; int color, data->palette, data->colorIdx, data->color)
+DRAW_DATA(bzSetOutputBuffer, int idx, data->idx)
+DRAW_DATA(bzGfxMap, BZTilemapID tilemap; int tx; int ty; int x; int y; int mw; int mh; BZIdentifierHash tag, data->tilemap, data->tx, data->ty, data->x, data->y, data->mw, data->mh, data->tag)
+
+void bzDrawPSet(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y, int c) {
+       BZVector pos = bzVectorMake(x, y);
+       bzMatrixTransformVector(&pos, &pos, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzPSet, pos.x, pos.y, c)
+}
+
+void bzDrawCls(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint8_t c) {
+       DRAW_DATA_DISPATCH(drawQueue, bzCls, c)
+}
+
+void bzDrawCamera(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y) {
+       DRAW_DATA_DISPATCH(drawQueue, bzCamera, x, y)
+}
+
+void bzDrawCirc(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c) {
+       BZVector pos = bzVectorMake(x, y);
+       BZVector radius = bzVectorMake(r, 0);
+       bzMatrixTransformVector(&pos, &pos, mtx);
+       bzMatrixScaleRotateVector(&radius, &radius, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzCirc, pos.x, pos.y, bzVectorMagnitude(&radius), c) // FIXME, RADIUS
+}
+
+void bzDrawCircFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c) {
+       BZVector pos = bzVectorMake(x, y);
+       BZVector radius = bzVectorMake(r, 0);
+       bzMatrixTransformVector(&pos, &pos, mtx);
+       bzMatrixScaleRotateVector(&radius, &radius, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzCircFill, pos.x, pos.y, bzVectorMagnitude(&radius), c) // FIXME, RADIUS
+}
+
+void bzDrawLine(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c) {
+       BZVector pos0 = bzVectorMake(x0, y0);
+       BZVector pos1 = bzVectorMake(x1, y1);
+       bzMatrixTransformVector(&pos0, &pos0, mtx);
+       bzMatrixTransformVector(&pos1, &pos1, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzLine, pos0.x, pos0.y, pos1.x, pos1.y, c)
+}
+
+void bzDrawRect(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c) {
+       BZVector pos0 = bzVectorMake(x0, y0);
+       BZVector pos1 = bzVectorMake(x1, y1);
+       bzMatrixTransformVector(&pos0, &pos0, mtx);
+       bzMatrixTransformVector(&pos1, &pos1, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzRect, pos0.x, pos0.y, pos1.x, pos1.y, c) // FIXME, rotation??
+}
+
+void bzDrawRectFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c) {
+       BZVector pos0 = bzVectorMake(x0, y0);
+       BZVector pos1 = bzVectorMake(x1, y1);
+       bzMatrixTransformVector(&pos0, &pos0, mtx);
+       bzMatrixTransformVector(&pos1, &pos1, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzRectFill, pos0.x, pos0.y, pos1.x, pos1.y, c) // FIXME, rotation??
+}
+
+void bzDrawTriangle(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int x2, int y2, int c) {
+       BZVector pos0 = bzVectorMake(x0, y0);
+       BZVector pos1 = bzVectorMake(x1, y1);
+       BZVector pos2 = bzVectorMake(x2, y2);
+       bzMatrixTransformVector(&pos0, &pos0, mtx);
+       bzMatrixTransformVector(&pos1, &pos1, mtx);
+       bzMatrixTransformVector(&pos2, &pos2, mtx);
+       DRAW_DATA_DISPATCH(drawQueue, bzTriangle, pos0.x, pos0.y, pos1.x, pos1.y, pos2.x, pos2.y, c)
+}
+
+void bzDrawSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y) {
+       DRAW_DATA_DISPATCH(drawQueue, bzSpr, n, x, y)
+}
+
+void bzDrawSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y, int w, int h, bool flipX, bool flipY) {
+       DRAW_DATA_DISPATCH(drawQueue, bzSprExt, n, x, y, w, h, flipX, flipY)
+}
+
+void bzDrawSSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy) {
+       DRAW_DATA_DISPATCH(drawQueue, bzSSpr, sx, sy, sw, sh, dx, dy)
+}
+
+void bzDrawSSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flipX, bool flipY) {
+       DRAW_DATA_DISPATCH(drawQueue, bzSSprExt, sx, sy, sw, sh, dx, dy, dw, dh, flipX, flipY)
+}
+
+void bzDrawPrint(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int color, const char *text) {
+       BZVector pos = bzVectorMake(x, y);
+       bzMatrixTransformVector(&pos, &pos, mtx);
+       if (drawQueue != NULL) {
+               size_t dataSize = sizeof(databzPrint) + strlen(text);
+               databzPrint *data = (databzPrint *)bzGfxDrawQueueEnqueue(drawQueue, sortIdx, dispatchbzPrint, dataSize);
+               data->x = pos.x;
+               data->y = pos.y;
+               data->c = color;
+               strcpy(data->string, text);
+       } else {
+               bzPrint(pos.x, pos.y, color, text);
+       }
+}
+
+void bzDrawFillP(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint16_t p) {
+       DRAW_DATA_DISPATCH(drawQueue, bzFillP, p)
+}
+
+void bzDrawSetPaletteColor(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int palette, int colorIdx, int color) {
+       DRAW_DATA_DISPATCH(drawQueue, bzSetPaletteColor, palette, colorIdx, color)
+}
+
+void bzDrawSetOutputBuffer(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int idx) {
+       DRAW_DATA_DISPATCH(drawQueue, bzSetOutputBuffer, idx)
+}
+
+void bzDrawMap(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, BZIdentifierHash tag) {
+       DRAW_DATA_DISPATCH(drawQueue, bzGfxMap, tilemap, tx, ty, x, y, mw, mh, tag)
+}
diff --git a/src/bz/gfx/drawing.h b/src/bz/gfx/drawing.h
new file mode 100644 (file)
index 0000000..95a2bb6
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef BZ_GFX_DRAWING_H
+#define BZ_GFX_DRAWING_H
+
+#include <bz/gfx/draw_queue.h>
+#include <bz/game/tilemap.h>
+#include <bz/math/matrix.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//extern void bzDrawClip(BZDrawQueueID drawQueue, uint8_t sortIdx, int x, int y, int w, int h);
+
+extern void bzDrawPSet(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y, int c);
+
+//extern bool bzFGet(int n, int f);
+//extern void bzFSet(int n, int f, bool v);
+
+extern void bzDrawCls(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint8_t c);
+
+extern void bzDrawCamera(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y);
+
+extern void bzDrawCirc(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c);
+extern void bzDrawCircFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c);
+
+//extern void bzDrawOval(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c);
+//extern void bzDrawOvalFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c);
+
+extern void bzDrawLine(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c);
+
+extern void bzDrawRect(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c);
+extern void bzDrawRectFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c);
+
+extern void bzDrawTriangle(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int x2, int y2, int c);
+
+extern void bzDrawSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y);
+extern void bzDrawSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y, int w, int h, bool flipX, bool flipY);
+
+extern void bzDrawSSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy);
+extern void bzDrawSSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flipX, bool flipY);
+
+extern void bzDrawPrint(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int color, const char *text);
+
+extern void bzDrawFillP(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint16_t p);
+
+extern void bzDrawSetPaletteColor(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int palette, int colorIdx, int color);
+
+extern void bzDrawSetOutputBuffer(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int idx);
+
+extern void bzDrawMap(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, BZIdentifierHash tag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/font.c b/src/bz/gfx/font.c
new file mode 100644 (file)
index 0000000..fdfada1
--- /dev/null
@@ -0,0 +1,138 @@
+#include <bz/gfx/font_internal.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+#include <stb_image.h>
+#include <stb_truetype.h>
+
+struct BZFontCodepoint {
+       unsigned char sprite[32*32];
+       int advanceWidth, leftSideBearing;
+       int x0, y0;
+       int x1, y1;
+};
+typedef struct BZFontCodepoint BZFontCodepoint;
+
+struct BZFont {
+       stbtt_fontinfo ttfFontInfo;
+       float scale;
+       int ascent;
+       int descent;
+       int lineGap;
+       int baseline;
+       BZFontCodepoint codepoints[256];
+};
+
+BZFontID bzGfxLoadFont(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZResourceID handle = bzResourcesOpenResource("font", "assets/fonts/%s.ttf", identifier);
+       size_t length = bzResourcesFileLength(handle);
+       void *data = (uint8_t *)bzMemoryAlloc(arena, length);
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       BZFont *font = (BZFont *)bzMemoryAlloc(arena, sizeof(BZFont)); // FIXME
+       stbtt_InitFont(&font->ttfFontInfo, data, stbtt_GetFontOffsetForIndex(data, 0));
+       font->scale = stbtt_ScaleForPixelHeight(&font->ttfFontInfo, 20);
+
+       stbtt_GetFontVMetrics(&font->ttfFontInfo, &font->ascent, &font->descent, &font->lineGap);
+
+//     int baselineX0, baselineY0, baselineX1, baselineY1;
+//     stbtt_GetFontBoundingBox(&font->ttfFontInfo, &baselineX0, &baselineY0, &baselineX1, &baselineY1);
+       font->baseline = (float)font->ascent * font->scale;
+
+       for (int c = 0; c < 256; ++c) {
+               BZFontCodepoint *codepoint = &font->codepoints[c];
+               stbtt_MakeCodepointBitmap(&font->ttfFontInfo, codepoint->sprite, 32, 32, 32, font->scale, font->scale, c); // fixme, box size
+               stbtt_GetCodepointHMetrics(&font->ttfFontInfo, c, &codepoint->advanceWidth, &codepoint->leftSideBearing);
+               codepoint->advanceWidth *= font->scale;
+               stbtt_GetCodepointBitmapBox(&font->ttfFontInfo, c, font->scale, font->scale, &codepoint->x0, &codepoint->y0, &codepoint->x1, &codepoint->y1);
+               //stbtt_GetCodepointBox(&font->ttfFontInfo, c, &codepoint->x0, &codepoint->y0, &codepoint->x1, &codepoint->y1);
+               //codepoint->x0 = (float)codepoint->x0 * font->scale;
+               //codepoint->y0 = (float)codepoint->y0 * font->scale;
+               //codepoint->x1 = (float)codepoint->x1 * font->scale;
+               //codepoint->y1 = (float)codepoint->y1 * font->scale;
+       }
+       
+       return font;
+}
+
+/*
+
+
+
+//  Displaying a character:
+//    Compute the bounding box of the character. It will contain signed values
+//    relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
+//    then the character should be displayed in the rectangle from
+//    <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
+//
+//  Advancing for the next character:
+//    Call GlyphHMetrics, and compute 'current_point += SF * advance'.
+
+
+
+
+
+
+char buffer[24<<20];
+unsigned char screen[20][79];
+
+int main(int arg, char **argv)
+{
+   stbtt_fontinfo font;
+   int i,j,ascent,baseline,ch=0;
+   float scale, xpos=2; // leave a little padding in case the character extends left
+   char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
+
+   fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
+   stbtt_InitFont(&font, buffer, 0);
+
+   scale = stbtt_ScaleForPixelHeight(&font, 15);
+   stbtt_GetFontVMetrics(&font, &ascent,0,0);
+   baseline = (int) (ascent*scale);
+
+   while (text[ch]) {
+      int advance,lsb,x0,y0,x1,y1;
+      float x_shift = xpos - (float) floor(xpos);
+      stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
+      stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
+      stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
+      // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
+      // because this API is really for baking character bitmaps into textures. if you want to render
+      // a sequence of characters, you really need to render each bitmap to a temp buffer, then
+      // "alpha blend" that into the working buffer
+      xpos += (advance * scale);
+      if (text[ch+1])
+         xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
+      ++ch;
+   }
+
+   for (j=0; j < 20; ++j) {
+      for (i=0; i < 78; ++i)
+         putchar(" .:ioVM@"[screen[j][i]>>5]);
+      putchar('\n');
+   }
+
+   return 0;
+}
+*/
+
+
+void bzGfxRenderFont(BZFontRenderPixelSetFunction pixelSetFunction, int *xPosition, int *yPosition, BZFontID font, int color, const char *text) {
+       for (const char *c = text; *c != '\0'; ++c) {
+               BZFontCodepoint *codepoint = &font->codepoints[*c];
+
+               for (int y = 0, i = 0; y < 32; ++y) {
+                       for (int x = 0; x < 32; ++x, ++i) {
+                               if (codepoint->sprite[i] > 100) {
+                                       pixelSetFunction(*xPosition + x + codepoint->x0, *yPosition + font->baseline + y + codepoint->y0, color);
+                               }
+                       }
+               }
+
+               *xPosition += codepoint->advanceWidth;
+       }
+}
diff --git a/src/bz/gfx/font.h b/src/bz/gfx/font.h
new file mode 100644 (file)
index 0000000..e4a72c0
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef BZ_GFX_FONT_H
+#define BZ_GFX_FONT_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZFont BZFont;
+typedef BZFont * BZFontID;
+
+extern BZFontID bzGfxLoadFont(BZMemoryArenaID arena, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/font_internal.h b/src/bz/gfx/font_internal.h
new file mode 100644 (file)
index 0000000..cd0b3b4
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_GFX_FONT_INTERNAL_H
+#define BZ_GFX_FONT_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bz/gfx/font.h>
+
+typedef void (*BZFontRenderPixelSetFunction)(int, int, int); 
+
+extern void bzGfxRenderFont(BZFontRenderPixelSetFunction pixelSetFunction, int *xPosition, int *yPosition, BZFontID font, int color, const char *text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/gfx.c b/src/bz/gfx/gfx.c
new file mode 100644 (file)
index 0000000..0f26e3b
--- /dev/null
@@ -0,0 +1,692 @@
+#include <bz/gfx/gfx_internal.h>
+
+#include <bz/gfx/font_internal.h>
+#include <bz/math/math.h>
+#include <bz/memory/allocator.h>
+
+#include <stdlib.h>
+#include <string.h> // memset.....
+
+// http://members.chello.at/~easyfilter/Bresenham.pdf
+
+size_t bufferStride = 0;
+size_t bufferStrideShift = 0;
+static uint8_t *drawBuffer;
+static uint8_t *paletteBuffer;
+static uint8_t *maskBuffer;
+static uint8_t *overlayBuffer;
+
+uint8_t *bzGfxCompositedBuffer;
+
+static uint8_t *currentBuffer;
+
+static int32_t canvasWidth = 0; // These need to be signed because of comparisons (don't want to keep casting...)
+static int32_t canvasHeight = 0;
+
+static float cameraX = 0;
+static float cameraY = 0;
+static int cameraXInt = 0;
+static int cameraYInt = 0;
+static uint16_t fillPattern;
+
+static BZMatrix globalViewMatrix = { .a = 1, .b = 0, .c = 0, .d = 1, .x = 0, .y = 0 };
+//static BZMatrix globalLocalMatrix = { .a = 1, .b = 0, .c = 0, .d = 1, .x = 0, .y = 0 };
+
+//static bool fillPattern[4][4];
+
+#define _bzBufferLocation(buffer, x, y) buffer[((size_t)(y) << bufferStrideShift) + (size_t)(x)]
+#define fillPatternBit(x,y) (((fillPattern >> (12 - (((y + cameraYInt) & 0b11) * 4)) >> (3 - ((x + cameraXInt) & 0b11))) & 1) * 255)
+#define patternVal(buffer, x,y,c) ((_bzBufferLocation(buffer, x, y) & ~(fillPatternBit(x, y))) | ((c) & fillPatternBit(x, y)))
+
+//#define _bzBufferSet(buffer, x, y, value) buffer[((y) << bufferStrideShift) + (x)] = (value)
+#define _bzBufferSet(buffer, x, y, value) _bzBufferLocation(buffer, x, y) = patternVal(buffer, x, y, value)
+#define _bzBufferSetSafe(buffer, x, y, value) { if ((x)>= 0 && (x)<canvasWidth && (y)>= 0 && (y)<canvasHeight) _bzBufferSet(buffer, x, y, value); }
+#define _bzBufferSetAssert(buffer, x, y, value) { bzAssert(buffer!=NULL);bzAssertMessage((x)>= 0 && (x)<canvasWidth, "invalid x: %d   %s, %d", x, __FILE__, __LINE__);bzAssertMessage((y)>= 0 && (y)<canvasHeight, "invalid y: %d   %s, %d", y, __FILE__, __LINE__);_bzBufferSet(buffer, x, y, value); }
+#define _bzBufferSetNull(buffer, x, y, value) {}
+
+#define bzBufferSet _bzBufferSet
+
+#define swap(a, b) { typeof(a) tmp = b; b = a; a = tmp; }
+
+#define bresenhamVariables(id, x0, y0, x1, y1) \
+       int id##xEnd = x1, id##yEnd = y1; \
+       int id##deltaX = bzAbs(x1 - x0); \
+       int id##deltaY = -bzAbs(y1 - y0); \
+       int id##signX = bzSgn(x1 - x0); \
+       int id##signY = bzSgn(y1 - y0); \
+       int id##err = id##deltaX + id##deltaY; \
+       int id##x = x0, id##y = y0;
+
+#define bresenhamStep(id) \
+       int id##e2 = id##err << 1; \
+       if (id##e2 >= id##deltaY) { \
+               if (id##x == id##xEnd) break; \
+               id##err += id##deltaY; \
+               id##x += id##signX; \
+       } \
+       if (id##e2 <= id##deltaX) { \
+               if (id##y == id##yEnd) break; \
+               id##err += id##deltaX; \
+               id##y += id##signY; \
+       }
+
+//uint32_t intermediateBuffer[canvasHeight][canvasWidth];
+
+#define SS_WIDTH 128
+#define SS_HEIGHT 128
+#define kSpriteSheetStrideShift 7
+
+//static uint8_t spritesheet[SS_HEIGHT][SS_WIDTH];
+static uint8_t *spritesheet = NULL;
+
+static BZFont *currentFont;
+
+/*uint8_t palettes[16][16] = {
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+};*/
+
+static size_t gPaletteCount;
+static size_t gPaletteColorCount;
+static uint8_t *gPalettes;
+
+//BZRect bzCalculateCullRect() {
+//     return bzRectMake(cameraX, cameraY, canvasWidth, SS_HEIGHT);
+//}
+
+void bzGfxPreparePalettes(BZMemoryArenaID arena, size_t paletteCount, size_t colorCount) {
+       gPaletteCount = paletteCount;
+       gPaletteColorCount = colorCount;
+       gPalettes = bzMemoryAlloc(arena, gPaletteCount * gPaletteColorCount * sizeof(uint8_t));
+
+       for (size_t p = 0, i = 0; p < gPaletteCount; ++p) {
+               for (size_t c = 0; c < gPaletteColorCount; ++c, ++i) {
+                       gPalettes[i] = c;
+               }
+       }
+}
+
+void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height) {
+       canvasWidth = width;
+       canvasHeight = height;
+
+       bufferStrideShift = __SIZE_WIDTH__ - __builtin_clzl(canvasWidth - 1);
+       bufferStride = 1 << bufferStrideShift;
+
+       size_t canvasMemorySize = bufferStride * height * sizeof(uint8_t);
+       drawBuffer = bzMemoryAlloc(arena, canvasMemorySize);
+       paletteBuffer = bzMemoryAlloc(arena, canvasMemorySize);
+       maskBuffer = bzMemoryAlloc(arena, canvasMemorySize);
+       overlayBuffer = bzMemoryAlloc(arena, canvasMemorySize);
+       bzGfxCompositedBuffer = bzMemoryAlloc(arena, canvasMemorySize);
+
+       bzSetOutputBuffer(0);
+
+       bzLog("Data: %d %d %d %d", canvasWidth, canvasMemorySize, bufferStrideShift, bufferStride);
+}
+
+void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data) {
+       uint8_t *imageData = (uint8_t *)data;
+       bzLog("Preparing spritesheet %dx%d", width, height);
+
+       size_t spritesheetMemorySize = width * height * sizeof(uint8_t);
+       spritesheet = bzMemoryAlloc(arena, spritesheetMemorySize);
+       memcpy(spritesheet, data, spritesheetMemorySize);
+}
+
+void bzGfxPrepareFont(BZMemoryArenaID arena, void *font) {
+       currentFont = font;
+}
+
+static bool prepareFrame(int *minXOut, int *minYOut, int *maxXOut, int *maxYOut, int x0, int y0, int x1, int y1, int *clippedLeftX, int *clippedTopY, int *clippedRightX, int *clippedBottomY) {
+       int xMin = x0, xMax = x1;
+       int yMin = y0, yMax = y1;
+
+       if (x1 < x0) {
+               xMin = x1;
+               xMax = x0;
+       }
+
+       if (y1 < y0) {
+               yMin = y1;
+               yMax = y0;
+       }
+
+       int xMinOut = xMin/* - cameraX*/, xMaxOut = xMax/* - cameraX*/;
+       int yMinOut = yMin/* - cameraY*/, yMaxOut = yMax/* - cameraY*/;
+       
+       if (xMaxOut < 0 || yMaxOut < 0 || xMinOut >= canvasWidth || yMinOut >= canvasHeight) {
+               return false;
+       }
+
+       int safeXMinOut = bzMax(0, xMinOut);
+       int safeXMaxOut = bzMin(canvasWidth - 1, xMaxOut);
+       int safeYMinOut = bzMax(0, yMinOut);
+       int safeYMaxOut = bzMin(canvasHeight - 1, yMaxOut);
+
+       *minXOut = safeXMinOut;
+       *minYOut = safeYMinOut;
+       *maxXOut = safeXMaxOut;
+       *maxYOut = safeYMaxOut;
+
+       if (clippedLeftX != NULL) {
+               *clippedLeftX = safeXMinOut - xMinOut;
+               *clippedTopY  = safeYMinOut - yMinOut;
+
+               *clippedRightX  = xMaxOut - safeXMaxOut;
+               *clippedBottomY = yMaxOut - safeYMaxOut;
+       }
+
+       return true;
+}
+
+static bool preparePositionXY(int *xOut, int *yOut, float x, float y) {
+       //BZVector origin = bzVectorMake(, );
+       //bzMatrixTransformVector(&origin, &origin, &globalViewMatrix);
+       BZVector position = bzVectorMake(x, y);
+       bzMatrixTransformVector/*bzMatrixTranslateVector*/(&position, &position, &globalViewMatrix);
+       //bzMatrixTransformVector(&position, &position, &globalViewMatrix);
+       //bzVectorAdd(&position, &origin, &position);
+       *xOut = bzFloor(position.x - cameraX);
+       *yOut = bzFloor(position.y - cameraY);
+       return true;
+}
+
+static bool prepareSizeXY(int *xOut, int *yOut, float x, float y) {
+       BZVector size = bzVectorMake(x, y);
+       bzMatrixScaleRotateVector(&size, &size, &globalViewMatrix);
+       *xOut = bzFloor(size.x);
+       if (yOut != NULL) *yOut = bzFloor(size.y);
+       return size.x > 0 && size.y > 0;
+}
+
+static bool prepareRadius(int *rOut, float r) {
+       BZVector size = bzVectorMake(r, 0);
+       bzMatrixScaleRotateVector(&size, &size, &globalViewMatrix);
+       *rOut = bzFloor(bzAbs(size.x) + bzAbs(size.y));
+       return *rOut > 0;
+}
+
+static bool prepareColor(int *colorOut, int color) {
+       *colorOut = gPalettes[color % gPaletteColorCount]; // FIXME
+       return true;
+       /*int c = (color < gPaletteColorCount) ? gPalettes[color] : color;
+       if (c > 0) {
+               *colorOut = c & globalBlendColor;
+               return true;
+       } else {
+               return false;
+       }*/
+}
+
+void bzPSet(BZCoordinate x, BZCoordinate y, BZPaletteColor c) {
+       //if (c == 0) return;
+
+       //c = ((c < gPaletteColorCount) ? gPalettes[c] : c);
+
+       prepareColor(&c, c);
+
+       int ox, oy;
+       preparePositionXY(&ox, &oy, x, y);
+       if (ox >= 0 && ox < canvasWidth && oy >=0 && oy < canvasHeight) {
+               bzBufferSet(currentBuffer, ox, oy, c);
+       }
+}
+
+BZPaletteColor bzSGet(int x, int y) {
+       bzAssert(spritesheet != NULL);
+       if (x >= 0 && x < SS_WIDTH && y >=0 && y < SS_HEIGHT) {
+               return spritesheet[(y << kSpriteSheetStrideShift) + x];
+       } else {
+               return 0;
+       }
+}
+
+//void bzSSet(int x, int y, int c);
+
+//bool bzFGet(int n, int f);
+//void bzFSet(int n, int f, bool v);
+
+void bzCls(BZPaletteColor c) {
+       memset(currentBuffer, c, bufferStride * canvasHeight);
+
+       //memset(&buffer[currentBuffer], c, BUFFER_WIDTH * canvasHeight);
+
+//     for (size_t i = 0; i < )
+
+//     for (size_t y = 0; y < canvasHeight; ++y) {
+//             for (size_t x = 0; x < canvasWidth; ++x) {
+//                     bzBufferSet(currentBuffer, x, y, c);
+//             }
+//     }
+
+       //bzCamera(0, 0);
+       bzFillP(0x0000);
+}
+
+void bzGetCamera(BZCoordinate *x, BZCoordinate *y) {
+       if (x != NULL) *x = cameraX;
+       if (y != NULL) *y = cameraY;
+}
+
+void bzCamera(BZCoordinate x, BZCoordinate y) {
+       cameraX = x;
+       cameraY = y;
+       cameraXInt = bzFloor(cameraX);
+       cameraYInt = bzFloor(cameraY);
+}
+
+void bzCirc(BZCoordinate xm, BZCoordinate ym, BZCoordinate r, BZPaletteColor c) {
+       int oxm, oym, or, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (preparePositionXY(&oxm, &oym, xm, ym) && prepareRadius(&or, r) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, oxm - or, oym - or, oxm + or, oym + or, NULL, NULL, NULL, NULL)) {
+               int x = -or;
+               int y = 0;
+               int err = 2 - 2 * or;
+
+               do {
+                       int x0 = oxm-x;
+                       int y0 = oym+y;
+                       if (x0 >= 0 && x0 < canvasWidth && y0 >=0 && y0 < canvasHeight) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, x0, y0, cOut);
+                       }
+
+                       int x1 = oxm-y;
+                       int y1 = oym-x;
+                       if (x1 >= 0 && x1 < canvasWidth && y1 >=0 && y1 < canvasHeight) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, x1, y1, cOut);
+                       }
+
+                       int x2 = oxm+x;
+                       int y2 = oym-y;
+                       if (x2 >= 0 && x2 < canvasWidth && y2 >=0 && y2 < canvasHeight) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, x2, y2, cOut);
+                       }
+
+                       int x3 = oxm+y;
+                       int y3 = oym+x;
+                       if (x3 >= 0 && x3 < canvasWidth && y3 >=0 && y3 < canvasHeight) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, x3, y3, cOut);
+                       }
+
+                       or = err;
+
+                       if (or <= y) err += (++y << 1) + 1;
+                       if (or > x || err > y) err += (++x << 1) + 1;
+               } while (x <= 0);
+       }
+}
+
+void bzCircFill(BZCoordinate xm, BZCoordinate ym, BZCoordinate r, BZPaletteColor c) {
+       int oxm, oym, or, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (preparePositionXY(&oxm, &oym, xm, ym) && prepareRadius(&or, r) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, oxm - or, oym - or, oxm + or, oym + or, NULL, NULL, NULL, NULL)) {
+               int x = -or;
+               int y = 0;
+               int err = 2 - 2 * or;
+
+               do {
+                       int fy1 = oym - bzAbs(y);
+                       int fy2 = oym + bzAbs(y);
+                       int fx  = bzMax(oxm - bzAbs(x), 0);
+                       int tx  = bzMin(oxm + bzAbs(x), canvasWidth - 1); // FIXME, not as many abs
+
+                       bool validFy1 = fy1 >= 0 && fy1 < canvasHeight;
+                       bool validFy2 = fy2 >= 0 && fy2 < canvasHeight;
+
+                       if (validFy1 && validFy2) {
+                               for (int px = fx; px <= tx; ++px) {
+                                       bzBufferSet(currentBuffer, px, fy1, cOut); // FIXME, speed
+                                       bzBufferSet(currentBuffer, px, fy2, cOut);
+                               }
+                       } else if (validFy1) {
+                               for (int px = fx; px <= tx; ++px) {
+                                       bzBufferSet(currentBuffer, px, fy1, cOut);
+                               }
+                       } else if (validFy2) {
+                               for (int px = fx; px <= tx; ++px) {
+                                       bzBufferSet(currentBuffer, px, fy2, cOut);
+                               }
+                       }
+
+                       or = err;
+
+                       if (or <= y) err += (++y << 1) + 1;
+                       if (or > x || err > y) err += (++x << 1) + 1;
+               } while (x <= 0);
+       }
+}
+
+//void bzOval(int x0, int y0, int x1, int y1, int c);
+//void bzOvalFill(int x0, int y0, int x1, int y1, int c);
+
+void bzLine(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) {
+       int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               bresenhamVariables(line_, ox0, oy0, ox1, oy1)
+
+               for (;;) {
+                       if (line_x >= 0 && line_x < canvasWidth && line_y >=0 && line_y < canvasHeight) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, line_x, line_y, cOut);
+                       }
+
+                       bresenhamStep(line_)
+               }
+       }
+}
+
+void bzRect(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) {
+       int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               if (clipLeft == 0 && clipRight == 0) {
+                       for (int py = yMinOut; py <= yMaxOut; ++py) {
+                               bzBufferSet(currentBuffer, xMinOut, py, cOut);
+                               bzBufferSet(currentBuffer, xMaxOut, py, cOut);
+                       }
+               } else if (clipLeft == 0) {
+                       for (int py = yMinOut; py <= yMaxOut; ++py) {
+                               bzBufferSet(currentBuffer, xMinOut, py, cOut);
+                       }
+               } else if (clipRight == 0) {
+                       for (int py = yMinOut; py <= yMaxOut; ++py) {
+                               bzBufferSet(currentBuffer, xMaxOut, py, cOut);
+                       }
+               }
+
+               if (clipTop == 0 && clipBottom == 0) {
+                       for (int px = xMinOut; px <= xMaxOut; ++px) {
+                               bzBufferSet(currentBuffer, px, yMinOut, cOut);
+                               bzBufferSet(currentBuffer, px, yMaxOut, cOut);
+                       }
+               } else if (clipTop == 0) {
+                       for (int px = xMinOut; px <= xMaxOut; ++px) {
+                               bzBufferSet(currentBuffer, px, yMinOut, cOut);
+                       }
+               } else if (clipBottom == 0) {
+                       for (int px = xMinOut; px <= xMaxOut; ++px) {
+                               bzBufferSet(currentBuffer, px, yMaxOut, cOut);
+                       }
+               }
+       }
+}
+
+void bzRectFill(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) {
+       int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               for (int py = yMinOut; py <= yMaxOut; ++py) {
+                       for (int px = xMinOut; px <= xMaxOut; ++px) {
+                               bzBufferSet(currentBuffer, px, py, cOut);
+                       }
+               }
+       }
+}
+
+void bzTriangle(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZCoordinate x2, BZCoordinate y2, BZPaletteColor c) {
+       int ox0, oy0, ox1, oy1, ox2, oy2;
+       
+       preparePositionXY(&ox0, &oy0, x0, y0);
+       preparePositionXY(&ox1, &oy1, x1, y1);
+       preparePositionXY(&ox2, &oy2, x2, y2);
+
+       if (oy2 < oy1 || (oy2 == oy1 && ox2 < ox1)) {
+               swap(ox1, ox2);
+               swap(oy1, oy2);
+       }
+
+       // after this, x0&y0 are always the smallest
+       if (oy1 < oy0 || (oy1 == oy0 && ox1 < ox0)) {
+               swap(ox0, ox1);
+               swap(oy0, oy1);
+       }
+
+       // one final comparison to swap for mid values
+       if (oy2 < oy1 || (oy2 == oy1 && ox2 < ox1)) {
+               swap(ox1, ox2);
+               swap(oy1, oy2);
+       }
+
+       // The above sorts pairs by Y position, but X isn't sorted
+       int minX = bzMin(bzMin(ox0, ox1), ox2);
+       int maxX = bzMax(bzMax(ox0, ox1), ox2);
+
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, minX, oy0, maxX, oy2, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               bresenhamVariables(triangleLow_,  ox0, oy0, ox1, oy1)
+               bresenhamVariables(triangleMid_,  ox1, oy1, ox2, oy2)
+               bresenhamVariables(triangleHigh_, ox0, oy0, ox2, oy2)
+
+               for (;;) {
+                       int targetY = triangleLow_y;
+                       
+                       int fromX1 = triangleLow_x, fromX2 = triangleLow_x;
+                       while (triangleLow_y == targetY) {
+                               fromX1 = triangleLow_x, fromX2 = triangleLow_x;
+                               bresenhamStep(triangleLow_)
+                       }
+
+                       int toX1 = triangleHigh_x, toX2 = triangleHigh_x;
+                       targetY = triangleHigh_y;
+                       while (triangleHigh_y == targetY) {
+                               toX1 = triangleHigh_x, toX2 = triangleHigh_x;
+                               bresenhamStep(triangleHigh_)
+                       }
+                       
+                       if (targetY >=0 && targetY < canvasHeight) {
+                               int fromX, toX;
+
+                               if (fromX1 < toX2) {
+                                       fromX = bzMin(fromX1, fromX2);
+                                       toX = bzMax(toX1, toX2);
+                               } else {
+                                       fromX = bzMin(toX1, toX2);
+                                       toX = bzMax(fromX1, fromX2);
+                               }
+
+                               if (!((fromX < 0 && toX < 0) || (fromX >= canvasWidth && toX >= canvasWidth))) {
+                                       fromX = bzMax(0, bzMin(fromX, canvasWidth - 1));
+                                       toX = bzMax(0, bzMin(toX, canvasWidth - 1));
+
+                                       for (int x = fromX; x <= toX; ++x) {
+                                               bzBufferSet(currentBuffer, x, targetY, cOut);
+                                       }
+                               }
+                       }
+
+                       if (targetY >= oy1) {
+                               break;
+                       }
+               }
+
+               for (;;) {
+                       int targetY = triangleMid_y;
+                       
+                       int fromX1 = triangleMid_x, fromX2 = triangleMid_x;
+                       while (triangleMid_y == targetY) {
+                               fromX1 = triangleMid_x, fromX2 = triangleMid_x;
+                               bresenhamStep(triangleMid_)
+                       }
+
+                       int toX1 = triangleHigh_x, toX2 = triangleHigh_x;
+                       targetY = triangleHigh_y;
+                       while (triangleHigh_y == targetY) {
+                               toX1 = triangleHigh_x, toX2 = triangleHigh_x;
+                               bresenhamStep(triangleHigh_)
+                       }
+                       
+                       if (targetY >=0 && targetY < canvasHeight) {
+                               int fromX, toX;
+
+                               if (fromX1 < toX2) {
+                                       fromX = bzMin(fromX1, fromX2);
+                                       toX = bzMax(toX1, toX2);
+                               } else {
+                                       fromX = bzMin(toX1, toX2);
+                                       toX = bzMax(fromX1, fromX2);
+                               }
+
+                               if (!((fromX < 0 && toX < 0) || (fromX >= canvasWidth && toX >= canvasWidth))) {
+                                       fromX = bzMax(0, bzMin(fromX, canvasWidth - 1));
+                                       toX = bzMax(0, bzMin(toX, canvasWidth - 1));
+
+                                       for (int x = fromX; x <= toX; ++x) {
+                                               bzBufferSet(currentBuffer, x, targetY, cOut);
+                                       }
+                               }
+                       }
+
+                       if (targetY >= oy2) {
+                               break;
+                       }
+               }
+       }
+}
+
+void bzSpr(size_t n, BZCoordinate x, BZCoordinate y) {
+       bzSprExt(n, x, y, 1, 1, false, false);
+}
+
+void bzSprExt(size_t n, BZCoordinate x, BZCoordinate y, BZCoordinate w, BZCoordinate h, bool flipX, bool flipY) {
+       int px = (n % 16) * 8;
+       int py = (n / 16) * 8;
+       int pw = w * 8;
+       int ph = h * 8;
+       bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY);
+}
+
+void bzSSpr(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy) {
+       bzSSprExt(sx, sy, sw, sh, dx, dy, sw, sh, false, false);
+}
+
+void bzSSprExt(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy, BZCoordinate dw, BZCoordinate dh, bool flipX, bool flipY) {
+       bzAssert(spritesheet != NULL);
+       int odx, ody, odw, odh, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (preparePositionXY(&odx, &ody, dx, dy) && prepareSizeXY(&odw, &odh, dw, dh) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, odx, ody, odx + odw - 1, ody + odh - 1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               int osx = bzFloor(sx);
+               int osy = bzFloor(sy);
+               int osw = bzFloor(sw);
+               int osh = bzFloor(sh);
+
+               for (size_t y = 0; y < osh - (clipTop + clipBottom); ++y) {
+                       for (size_t x = 0; x < osw - (clipLeft + clipRight); ++x) {
+                               int color = gPalettes[spritesheet[((clipTop+osy+y) << kSpriteSheetStrideShift) + clipLeft+osx+x]];
+                               if (color > 0) bzBufferSet(currentBuffer, xMinOut+x, yMinOut+y, color); // FIXME, scaled up and 0 check removal??
+                       }
+               }
+       }
+       
+       /*int fromX = 0;
+       int toX = sw;
+       int dX = 1;
+
+       if (flipX) {
+               fromX = toX - 1;
+               toX = -1;
+               dX = -1;
+       }
+
+       int fromY = 0;
+       int toY = sh;
+       int dY = 1;
+
+       if (flipY) {
+               fromY = toY - 1;
+               toY = -1;
+               dY = -1;
+       }
+
+       for (int rY = fromY, wY = dy; rY != toY; rY += dY, ++wY) {
+               for (int rX = fromX, wX = dx; rX != toX; rX += dX, ++wX) {
+                       bzPSet(wX, wY, bzSGet(sx + rX, sy + rY));
+               }
+       }*/
+}
+
+void bzFillP(BZPattern p) {
+       fillPattern = ~p;
+}
+
+static void bzPrintPSet(int x, int y, int c) {
+       if (x >= 0 && x < canvasWidth && y >=0 && y < canvasHeight) {
+               //c = ((c < gPaletteColorCount) ? gPalettes[c] : c);
+               prepareColor(&c, c);
+               bzBufferSet(currentBuffer, x, y, c);
+       }
+}
+
+void bzPrint(BZCoordinate x, BZCoordinate y, BZPaletteColor color, const char *text) {
+       int ox, oy;
+       preparePositionXY(&ox, &oy, x, y);
+       bzGfxRenderFont(bzPrintPSet, &ox, &oy, currentFont, (int)color, text);
+       //if (preparePositionXY(&ox, &oy, x, y)) {
+       //      bzGfxRenderFont(bzPSet, &ox, &oy, currentFont, (int)color, text);
+               // FIXME, this won't work
+       //}
+}
+
+void bzSetPaletteColor(size_t palette, BZPaletteColor colorIdx, BZPaletteColor color) {
+       gPalettes[(palette * gPaletteColorCount) + (colorIdx % gPaletteColorCount)] = color;
+}
+
+void bzSetGlobalViewMatrix(const BZMatrix *mtx) {
+       bzMatrixCopy(&globalViewMatrix, mtx);
+}
+
+//void bzSetGlobalLocalMatrix(const BZMatrix *mtx) {
+//     bzMatrixCopy(&globalLocalMatrix, mtx);
+//}
+
+//void bzSetGlobalBlendColor(BZPaletteColor color) {
+//     globalBlendColor = color;
+//}
+
+void bzSetOutputBuffer(size_t idx) {
+       bzAssert(idx >= 0 && idx < 4);
+
+       switch (idx) {
+               case 0:
+                       currentBuffer = drawBuffer;
+                       break;
+               
+               case 1:
+                       currentBuffer = paletteBuffer;
+                       break;
+
+               case 2:
+                       currentBuffer = maskBuffer;
+                       break;
+
+               case 3:
+                       currentBuffer = overlayBuffer;
+                       break;
+
+               default: // FIXME
+                       bzError("invalid output buffer");
+                       break;
+       }
+}
+
+void bzGfxComposite() {
+       bzPerfTimerStart("game.blit.composite");
+
+       uint8_t *drawBufferReadHead = drawBuffer;
+       uint8_t *paletteBufferReadHead = paletteBuffer;
+       uint8_t *maskBufferReadHead = maskBuffer;
+       uint8_t *overlayBufferReadHead = overlayBuffer;
+       uint8_t *outputBufferWriteHead = bzGfxCompositedBuffer;
+       for (size_t i = 0, end = bufferStride * canvasHeight; i < end; ++i) {
+               //*outputBufferWriteHead = (palettes[*paletteBufferReadHead + 1][*drawBufferReadHead] & *maskBufferReadHead) | *overlayBufferReadHead;
+               *outputBufferWriteHead = *overlayBufferReadHead > 0 ? gPalettes[*overlayBufferReadHead] : gPalettes[(*paletteBufferReadHead + 1) *gPaletteColorCount + *drawBufferReadHead];
+
+               ++drawBufferReadHead;
+               ++paletteBufferReadHead;
+               ++maskBufferReadHead;
+               ++overlayBufferReadHead;
+               ++outputBufferWriteHead;
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+}
diff --git a/src/bz/gfx/gfx.h b/src/bz/gfx/gfx.h
new file mode 100644 (file)
index 0000000..770997c
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef BZ_GFX_GFX_H
+#define BZ_GFX_GFX_H
+
+#include <bz/math/matrix.h>
+#include <bz/renderer/render_pass.h>
+#include <bz/types/common.h>
+#include <bz/types/rect.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef struct BZCanvas BZCanvas;
+//typedef BZCanvas * BZCanvasID;
+
+//static const int kBZGfxCanvasWidth = 256;
+//static const int kBZGfxCanvasHeight = 128;
+
+extern BZRenderPassID bzGFXRenderPass;
+
+typedef float BZCoordinate;
+typedef uint8_t BZPaletteColor;
+typedef uint16_t BZPattern;
+
+//extern BZRect bzCalculateCullRect(void);
+
+//extern void bzClip(int x, int y, int w, int h);
+
+extern void bzPSet(BZCoordinate x, BZCoordinate y, BZPaletteColor c);
+
+extern BZPaletteColor bzSGet(int x, int y);
+//extern void bzSSet(int x, int y, int c);
+
+//extern bool bzFGet(int n, int f);
+//extern void bzFSet(int n, int f, bool v);
+
+extern void bzCls(BZPaletteColor c);
+
+extern void bzGetCamera(BZCoordinate *x, BZCoordinate *y);
+extern void bzCamera(BZCoordinate x, BZCoordinate y);
+
+extern void bzCirc(BZCoordinate x, BZCoordinate y, BZCoordinate r, BZPaletteColor c);
+extern void bzCircFill(BZCoordinate x, BZCoordinate y, BZCoordinate r, BZPaletteColor c);
+
+//extern void bzOval(int x0, int y0, int x1, int y1, int c);
+//extern void bzOvalFill(int x0, int y0, int x1, int y1, int c);
+
+extern void bzLine(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c);
+
+extern void bzRect(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c);
+extern void bzRectFill(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c);
+
+extern void bzTriangle(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZCoordinate x2, BZCoordinate y2, BZPaletteColor c);
+
+extern void bzSpr(size_t n, BZCoordinate x, BZCoordinate y);
+extern void bzSprExt(size_t n, BZCoordinate x, BZCoordinate y, BZCoordinate w, BZCoordinate h, bool flipX, bool flipY);
+
+extern void bzSSpr(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy);
+extern void bzSSprExt(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy, BZCoordinate dw, BZCoordinate dh, bool flipX, bool flipY);
+
+extern void bzPrint(BZCoordinate x, BZCoordinate y, BZPaletteColor color, const char *text);
+
+extern void bzFillP(BZPattern p);
+
+extern void bzSetPaletteColor(size_t palette, BZPaletteColor colorIdx, BZPaletteColor color);
+
+extern void bzSetGlobalViewMatrix(const BZMatrix *mtx);
+//extern void bzSetGlobalLocalMatrix(const BZMatrix *mtx);
+//extern void bzSetGlobalBlendColor(BZPaletteColor color);
+
+extern void bzSetOutputBuffer(size_t idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/gfx_internal.h b/src/bz/gfx/gfx_internal.h
new file mode 100644 (file)
index 0000000..c03c73a
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef BZ_GFX_GFX_INTERNAL_H
+#define BZ_GFX_GFX_INTERNAL_H
+
+#include <bz/gfx/gfx.h>
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzGfxPreparePalettes(BZMemoryArenaID arena, size_t paletteCount, size_t colorCount);
+extern void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height);
+
+extern void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data);
+extern void bzGfxPrepareFont(BZMemoryArenaID arena, void *font);
+
+extern void bzGfxComposite(void);
+
+extern uint8_t *bzGfxCompositedBuffer;
+extern size_t bufferStride; // FIXME, name
+extern size_t bufferStrideShift; // FIXME, name
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/particle_drawing.c b/src/bz/gfx/particle_drawing.c
new file mode 100644 (file)
index 0000000..d02a297
--- /dev/null
@@ -0,0 +1,80 @@
+#include <bz/gfx/particle_drawing.h>
+
+#include <bz/fx/particle_simulation_internal.h>
+#include <bz/gfx/gfx.h>
+
+//static void drawBasicParticle(size_t particleCount, float ticks, float spawnTick[], float lifetimes[], float positionX[], float positionY[], float sizes[], float colors[], float alpha[]);
+
+void bzGfxDrawParticles(BZParticleSimulationID simulation, BZDrawQueueID drawQueue) {
+       BZMatrix m;
+       
+       for (size_t i = 0; i < simulation->maxParticles; ++i) {
+               float particleStartTicks = simulation->spawnTime[i];
+               float lifetime = simulation->lifetime[i];
+               if (particleStartTicks + lifetime >= simulation->time) {
+                       if (simulation->alpha[i] < 0.25f) {
+                               bzFillP(0);
+                       } else if (simulation->alpha[i] < 0.50f) { 
+                               bzFillP(23130);//bzFillP(23130);
+                       } else if (simulation->alpha[i] < 0.75f) {
+                               bzFillP(32125);//bzFillP(32125);
+                       } else {
+                               bzFillP(32735);//bzFillP(0xFF); // FIXME
+                       }
+
+                       bzSetPaletteColor(0, 0, simulation->color[i]);
+                       //bzSetGlobalBlendColor(simulation->color[i]);
+
+                       bzMatrixSRT(&m, simulation->positionX[i], simulation->positionY[i], simulation->angle[i], simulation->size[i], simulation->size[i]);
+                       bzSetGlobalViewMatrix(&m);
+                       //bzMatrixTRS(&m, 0, 0, simulation->angle[i], simulation->size[i], simulation->size[i]);
+                       //bzSetGlobalLocalMatrix(&m);
+
+                       if (drawQueue != NULL) {
+                               bzGfxDrawQueueRun(drawQueue);
+                       } else {
+                               bzCircFill(0, 0, 1, 0);
+                       }
+               }
+       }
+       bzFillP(0);
+
+       bzMatrixIdentity(&m);
+       bzSetGlobalViewMatrix(&m);
+       //bzSetGlobalLocalMatrix(&m);
+       bzSetPaletteColor(0, 0, 0);
+       //bzSetGlobalBlendColor(0xFF);
+
+
+//void bzGfxDrawParticles(BZParticleSimulationID simulation) {
+
+
+       // FIXME, allow registering draw lists for particles
+
+//     BZParticleSimulationDrawFunction drawFunction = drawBasicParticle; // FIXME, allow register others
+//     drawFunction(simulation->maxParticles, simulation->time, simulation->spawnTime, simulation->lifetime, simulation->positionX, simulation->positionY, simulation->size, simulation->color, simulation->alpha);
+}
+
+/*static void drawBasicParticle(size_t particleCount, float time, float spawnTime[], float lifetimes[], float positionX[], float positionY[], float sizes[], float colors[], float alpha[]) {
+       for (size_t i = 0; i < particleCount; ++i) {
+               float particleStartTicks = spawnTime[i];
+               float lifetime = lifetimes[i];
+               if (particleStartTicks + lifetime >= time) {
+                       if (alpha[i] < 0.25f) {
+                               bzFillP(0);
+                       } else if (alpha[i] < 0.50f) { 
+                               bzFillP(23130);//bzFillP(23130);
+                       } else if (alpha[i] < 0.75f) {
+                               bzFillP(32125);//bzFillP(32125);
+                       } else {
+                               bzFillP(32735);//bzFillP(0xFF); // FIXME
+                       }
+
+                       bzGfxDrawQueueRun(BZDrawQueueID drawQueue);
+                       //bzCircFill(positionX[i], positionY[i], sizes[i], colors[i]);
+               }
+       }
+       bzFillP(0);
+}*/
+
+//nil,32800.99988,â–’,â–‘,32735.99988
\ No newline at end of file
diff --git a/src/bz/gfx/particle_drawing.h b/src/bz/gfx/particle_drawing.h
new file mode 100644 (file)
index 0000000..aef8576
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef BZ_GFX_PARTICLE_DRAWING_H
+#define BZ_GFX_PARTICLE_DRAWING_H
+
+#include <bz/fx/particle_simulation.h>
+#include <bz/gfx/draw_queue.h>
+#include <bz/math/vector.h>
+#include <bz/types/common.h>
+//#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef void (*BZParticleSimulationDrawFunction)(size_t particleCount, float ticks, float spawnTick[], float lifetimes[], float positionsX[], float positionsY[], float sizes[], float colors[], float alpha[]);
+
+//extern void bzGfxRegisterParticleDrawFunction(BZParticleSimulationDrawFunction function, void *userParameter);
+
+extern void bzGfxDrawParticles(BZParticleSimulationID simulation, BZDrawQueueID drawQueue);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/tilemap.c b/src/bz/gfx/tilemap.c
new file mode 100644 (file)
index 0000000..7dac115
--- /dev/null
@@ -0,0 +1,61 @@
+#include <bz/gfx/tilemap_internal.h>
+
+#include <bz/game/tilemap_internal.h>
+#include <bz/gfx/gfx_internal.h>
+#include <bz/math/math.h>
+
+void bzGfxMap(BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, uint32_t mask) {
+       size_t mapFromX = bzMax(0, tx);
+       size_t mapToX = bzMin(tilemap->width, tx + mw);
+
+       size_t mapFromY = bzMax(0, ty);
+       size_t mapToY = bzMin(tilemap->height, ty + mh);
+
+       size_t tilesX = mapToX - mapFromX;
+       size_t tilesY = mapToY - mapFromY;
+
+       for (size_t mty = 0; mty < tilesY; ++mty) {
+               for (size_t mtx = 0; mtx < tilesX; ++mtx) {
+                       size_t mx = mapFromX + mtx;
+                       size_t my = mapFromY + mty;
+
+                       size_t idx = my * tilemap->width + mx;
+                       BZTilemapTile *tile = &tilemap->tiles[idx];
+                       BZTilemapTileDefinition *tileDefinition = &tilemap->tileDefinitions[tile->tileDefinitionIdx];
+                       
+                       //uint8_t tileIdx = tile->spriteIdx;
+                       //if (tileIdx == 0) {
+                       //      tileIdx = tileDefinition->spriteIdx;
+                       //}
+
+                       if (mask == 0 || (tileDefinition->tagsFlag & mask) > 0) {
+                               uint8_t spriteIdx = bzGameGetTilemapSprite(tilemap, mx, my);
+                       
+                       //if (tileIdx > 0) {
+                               //Tile *tile = &tiles[tileIdx];
+
+                               //bool flagsSet = ignoreMask;
+
+                               //if (flagsSet == false) {
+                               //      uint8_t flags = tile->flags;// | mapTile->flags;
+                               //      flagsSet = (flags & userFlagMask) == userFlagValues;
+                               //}
+
+                               //if (flagsSet) {
+                                       /*if (mapTile->replaceSprite) {
+                                               spriteIdx = mapTile->spriteIdx;
+                                       } else if (tile->replaceSprite) {
+                                               spriteIdx = tile->spriteIdx;
+                                       }*/
+
+                                       if (spriteIdx > 0) {
+                                               int height = bzMax(tileDefinition->height, 1);
+                                               for (int i = 0; i < height; ++i) {
+                                                       bzSpr(spriteIdx - i * 16, x + mtx * 8, y + mty * 8 - i * 8); // Fixme, just sspr it.
+                                               }
+                                       }
+                               //}
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/bz/gfx/tilemap.h b/src/bz/gfx/tilemap.h
new file mode 100644 (file)
index 0000000..e70eac3
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_GFX_TILEMAP_H
+#define BZ_GFX_TILEMAP_H
+
+#include <bz/game/tilemap.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzGfxMap(BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, uint32_t mask);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/gfx/tilemap_internal.h b/src/bz/gfx/tilemap_internal.h
new file mode 100644 (file)
index 0000000..d5f2e55
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_GFX_TILEMAP_INTERNAL_H
+#define BZ_GFX_TILEMAP_INTERNAL_H
+
+#include <bz/gfx/tilemap.h>
+
+#include <bz/game/tilemap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//extern void bzGfxPrepareTilemap(BZMemoryArenaID arena, BZTilemapID tilemap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/impl.c b/src/bz/impl.c
new file mode 100644 (file)
index 0000000..674883a
--- /dev/null
@@ -0,0 +1,12 @@
+// This file is exclusively for single header implementations
+
+#define STB_IMAGE_IMPLEMENTATION
+#define STBI_NO_STDIO
+#define STBI_ONLY_PNG // STBI_ONLY_ZLIB
+#include <stb_image.h>
+
+#define STB_SPRINTF_IMPLEMENTATION
+#include <stb_sprintf.h>
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include <stb_truetype.h>
diff --git a/src/bz/input/input.c b/src/bz/input/input.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/bz/input/input.h b/src/bz/input/input.h
new file mode 100644 (file)
index 0000000..169578c
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef BZ_INPUT_INPUT_H
+#define BZ_INPUT_INPUT_H
+
+#include <bz/input/input_id.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzInputProcessFrame(void);
+
+extern bool bzInputGetBooleanValue(BZInputID inputID);
+extern int bzInputGetIntegerValue(BZInputID inputID);
+extern float bzInputGetFloatValue(BZInputID inputID);
+
+// FIXME, remove these
+extern bool bzInputBtn(int id);
+extern bool bzInputBtnP(int id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/input/input_id.h b/src/bz/input/input_id.h
new file mode 100644 (file)
index 0000000..dd9f958
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef BZ_INPUT_INPUT_ID_H
+#define BZ_INPUT_INPUT_ID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZInput BZInput;
+typedef const BZInput * BZInputID;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/input/input_id_internal.h b/src/bz/input/input_id_internal.h
new file mode 100644 (file)
index 0000000..a0feb17
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_INPUT_INPUT_ID_INTERNAL_H
+#define BZ_INPUT_INPUT_ID_INTERNAL_H
+
+#include <bz/input/input_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZInput {
+       unsigned int id;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/input/input_internal.h b/src/bz/input/input_internal.h
new file mode 100644 (file)
index 0000000..181c3a8
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef BZ_INPUT_INPUT_INTERNAL_H
+#define BZ_INPUT_INPUT_INTERNAL_H
+
+#include <bz/input/input.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzInputInit(void);
+extern void bzInputTeardown(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/input/platform.h b/src/bz/input/platform.h
new file mode 100644 (file)
index 0000000..315be46
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_INPUT_PLATFORM_H
+#define BZ_INPUT_PLATFORM_H
+
+#include <bz/input/input_id.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern BZInputID bzQuitInputID;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/math/math.c b/src/bz/math/math.c
new file mode 100644 (file)
index 0000000..f00ad2c
--- /dev/null
@@ -0,0 +1,51 @@
+#include <bz/math/math.h>
+
+#include <math.h>
+
+float bzSine(float a) {
+       return sinf(bzLerp(0.0f, 2.0f * M_PI, fmodf(a, 1.0f)));
+}
+
+float bzCosine(float a) {
+       return cosf(bzLerp(0.0f, 2.0f * M_PI, fmodf(a, 1.0f)));
+}
+
+extern float bzInverseSine(float a) {
+       return bzUnlerp(0.0f, 2.0f * M_PI, fmodf(asinf(a), 2.0f * M_PI));
+}
+
+extern float bzInverseCosine(float a) {
+       return bzUnlerp(0.0f, 2.0f * M_PI, fmodf(acosf(a), 2.0f * M_PI));
+}
+
+float bzSqrt(float a) {
+       return sqrtf(a);
+}
+
+float bzAtan2(float x, float y) {
+       float a = atan2f(y, x);
+       float ca = bzUnlerp(0.0f, 2.0f * M_PI, a);
+       return ca;
+}
+
+float bzAngleDelta(float from, float to) {
+       float delta = fmodf(((to - from) + 2), 1);
+       if (delta >= 0.5f) {
+               delta = -(1.0f - delta);
+       }
+       return delta;
+}
+
+float bzDistance(float x1, float y1, float x2, float y2) {
+       return sqrtf(bzDistanceSquared(x1, y1, x2, y2));
+}
+
+float bzDistanceSquared(float x1, float y1, float x2, float y2) {
+       float dx = x2 - x1;
+       float dy = y2 - y1;
+       return dx * dx + dy * dy;
+}
+
+bool bzDistanceCheck(float x1, float y1, float x2, float y2, float distance) {
+       return bzDistanceSquared(x1, y1, x2, y2) <= distance * distance;
+}
diff --git a/src/bz/math/math.h b/src/bz/math/math.h
new file mode 100644 (file)
index 0000000..f1ebd95
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef BZ_MATH_MATH_H
+#define BZ_MATH_MATH_H
+
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define bzAbs(a) ((a) < 0.0f ? -(a) : (a))
+
+#define bzMin(a, b) ((a) < (b) ? (a) : (b))
+#define bzMax(a, b) ((a) > (b) ? (a) : (b))
+#define bzMid(a, b, c) (bzMin(bzMin(bzMax((a), (b)), bzMax((b), (c))), bzMax((a), (c))))
+
+#define bzMMin(a, b) (bzAbs(a) < bzAbs(b) ? (a) : (b))
+#define bzMMax(a, b) (bzAbs(a) > bzAbs(b) ? (a) : (b))
+
+#define bzClamp(a, b, v) (bzMin(bzMax(v, bzMin((a), (b))), bzMax((a), (b))))
+
+#define bzSgn(v)  (bzAbs(v) < 0.001f ? 0.0f : ((v) >= 0.0f ? 1.0f : -1.0f))
+
+#define bzLerp(a, b, t) ((a) * (1 - (t)) + (b) * (t))
+#define bzUnlerp(a, b, v) (((v) - (a)) / ((b) - (a)))
+
+#define bzFloor(v) ((float)((int)(v)))
+#define bzRound(v) (bzFloor(v + bzSgn(v) * 0.5f))
+#define bzCeil(v) (bzFloor(v) + (bzFloor(v) < (v)))
+
+extern float bzSine(float a);
+extern float bzCosine(float a);
+
+extern float bzInverseSine(float a);
+extern float bzInverseCosine(float a);
+
+extern float bzSqrt(float a);
+
+extern float bzAtan2(float x, float y); // To match PICO-8
+extern float bzAngleDelta(float from, float to);
+
+extern float bzDistance(float x1, float y1, float x2, float y2);
+extern float bzDistanceSquared(float x1, float y1, float x2, float y2);
+extern bool bzDistanceCheck(float x1, float y1, float x2, float y2, float distance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/math/matrix.c b/src/bz/math/matrix.c
new file mode 100644 (file)
index 0000000..0450e7b
--- /dev/null
@@ -0,0 +1,130 @@
+#include <bz/math/matrix.h>
+
+#include <bz/math/math.h>
+
+void bzMatrixSet(BZMatrix *matrixOut, float a, float b, float c, float d, float x, float y) {
+       matrixOut->a = a;
+       matrixOut->b = b;
+       matrixOut->c = c;
+       matrixOut->d = d;
+       matrixOut->x = x;
+       matrixOut->y = y;
+}
+
+void bzMatrixCopy(BZMatrix *matrixOut, const BZMatrix *matrix) {
+       float a = matrix->a;
+       float b = matrix->b;
+       float c = matrix->c;
+       float d = matrix->d;
+       float x = matrix->x;
+       float y = matrix->y;
+       bzMatrixSet(matrixOut, a, b, c, d, x, y);
+}
+
+void bzMatrixInverse(BZMatrix *matrixOut, const BZMatrix *matrix) {
+       float det = 1.0f / (matrix->a * matrix->d - matrix->b * matrix->c);
+       float a = matrix->d * det;
+       float b = -(matrix->b) * det;
+       float c = -(matrix->c) * det;
+       float d = matrix->a * det;
+       float x = (matrix->b * matrix->y - matrix->d * matrix->x) * det;
+       float y = -(matrix->a * matrix->y - matrix->c * matrix->x) * det;
+       bzMatrixSet(matrixOut, a, b, c, d, x, y);
+}
+
+void bzMatrixMultiply(BZMatrix *matrixOut, const BZMatrix *matrix1, const BZMatrix *matrix2) {
+       float a = matrix1->a * matrix2->a + matrix1->b * matrix2->c;
+       float b = matrix1->a * matrix2->b + matrix1->b * matrix2->d;
+       float c = matrix1->c * matrix2->a + matrix1->d * matrix2->c;
+       float d = matrix1->c * matrix2->b + matrix1->d * matrix2->d;
+       float x = matrix1->a * matrix2->x + matrix1->b * matrix2->y + matrix1->x;
+       float y = matrix1->c * matrix2->x + matrix1->d * matrix2->y + matrix1->y;
+       bzMatrixSet(matrixOut, a, b, c, d, x, y);
+}
+
+void bzMatrixIdentity(BZMatrix *matrixOut) {
+       bzMatrixSet(matrixOut, 1, 0, 0, 1, 0, 0);
+}
+
+void bzMatrixRotation(BZMatrix *matrixOut, float angle) {
+       float ct = bzCosine(angle);
+       float st = bzSine(angle);
+       bzMatrixSet(matrixOut, ct, -st, st, ct, 0, 0);
+}
+
+void bzMatrixTranslation(BZMatrix *matrixOut, float translateX, float translateY) {
+       bzMatrixSet(matrixOut, 1, 0, 0, 1, translateX, translateY);
+}
+
+void bzMatrixScale(BZMatrix *matrixOut, float scaleX, float scaleY) {
+       bzMatrixSet(matrixOut, scaleX, 0, 0, scaleY, 0, 0);
+}
+
+/*void bzMatrixTRS(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY) {
+       BZMatrix t, r, s;
+       bzMatrixTranslation(&t, translateX, translateY);
+       bzMatrixRotation(&r, angle);
+       bzMatrixScale(&s, scaleX, scaleY);
+       bzMatrixMultiply(&r, &r, &s);
+       bzMatrixMultiply(&t, &t, &r);
+       bzMatrixCopy(matrixOut, &t);
+       //float ct = bzCosine(angle);
+       //float st = bzSine(angle);
+       //bzMatrixSet(matrixOut, scaleX * ct, scaleX * -st, scaleY * st, scaleY * ct, translateX, translateY);
+}*/
+
+void bzMatrixSRT(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY) {
+       float ct = bzCosine(angle);
+       float st = bzSine(angle);
+       bzMatrixSet(matrixOut, scaleX * ct, scaleX * -st, scaleY * st, scaleY * ct, translateX, translateY);
+
+/*     BZMatrix s, r, t;
+       bzMatrixScale(&s, scaleX, scaleY);
+       bzMatrixRotation(&r, angle);
+       bzMatrixTranslation(&t, translateX, translateY);
+       bzMatrixMultiply(&r, &r, &s);
+       bzMatrixMultiply(&t, &t, &r);
+       bzMatrixCopy(matrixOut, &t);*/
+//     bzMatrixMultiply(&r, &r, &t);
+//     bzMatrixMultiply(&s, &s, &r);
+//     bzMatrixCopy(matrixOut, &s);
+//     float ct = bzCosine(angle);
+//     float st = bzSine(angle);
+//     bzMatrixSet(matrixOut, scaleX * ct, scaleX * -st, scaleY * st, scaleY * ct, translateX, translateY);
+}
+
+void bzMatrixShear(BZMatrix *matrixOut, float shearX, float shearY) {
+       bzMatrixSet(matrixOut, 1, shearX, shearY, 1, 0, 0);
+}
+
+void bzMatrixOrtho(BZMatrix *matrixOut, float left, float top, float right, float bottom) {
+       float a = 2.0f / (right - left);
+       float d = 2.0f / (top - bottom);
+       float x = -(right + left) / (right - left);
+       float y = -(top + bottom) / (top - bottom);
+       bzMatrixSet(matrixOut, a, 0, 0, d, x, y);
+}
+
+void bzMatrixScaleVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) {
+       float x = vector->x * bzSqrt(matrix->a * matrix->a + matrix->b * matrix->b);
+       float y = vector->y * bzSqrt(matrix->c * matrix->c + matrix->d * matrix->d);
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzMatrixScaleRotateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) {
+       float x = (vector->x * matrix->a) + (vector->y * matrix->b);
+       float y = (vector->x * matrix->c) + (vector->y * matrix->d);
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzMatrixTranslateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) {
+       float x = vector->x + matrix->x;
+       float y = vector->y + matrix->y;
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzMatrixTransformVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) {
+       float x = (vector->x * matrix->a) + (vector->y * matrix->b) + matrix->x;
+       float y = (vector->x * matrix->c) + (vector->y * matrix->d) + matrix->y;
+       bzVectorSet(vectorOut, x, y);
+}
diff --git a/src/bz/math/matrix.h b/src/bz/math/matrix.h
new file mode 100644 (file)
index 0000000..b49b6e5
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef BZ_MATH_MATRIX_H
+#define BZ_MATH_MATRIX_H
+
+#include <bz/math/vector.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZMatrix {
+       float a;
+       float b;
+       float c;
+       float d;
+       float x;
+       float y;
+};
+typedef struct BZMatrix BZMatrix;
+
+
+extern void bzMatrixSet(BZMatrix *matrixOut, float a, float b, float c, float d, float x, float y);
+extern void bzMatrixCopy(BZMatrix *matrixOut, const BZMatrix *matrix);
+
+extern void bzMatrixInverse(BZMatrix *matrixOut, const BZMatrix *matrix);
+extern void bzMatrixMultiply(BZMatrix *matrixOut, const BZMatrix *matrix1, const BZMatrix *matrix2);
+
+extern void bzMatrixIdentity(BZMatrix *matrixOut);
+extern void bzMatrixRotation(BZMatrix *matrixOut, float angle);
+extern void bzMatrixTranslation(BZMatrix *matrixOut, float translateX, float translateY);
+extern void bzMatrixScale(BZMatrix *matrixOut, float scaleX, float scaleY);
+//extern void bzMatrixTRS(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY);
+extern void bzMatrixSRT(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY);
+extern void bzMatrixShear(BZMatrix *matrixOut, float shearX, float shearY);
+extern void bzMatrixOrtho(BZMatrix *matrixOut, float left, float top, float right, float bottom);
+
+//extern void bzMatrixScaleVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix); // NB: Expensive
+extern void bzMatrixTranslateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix);
+extern void bzMatrixScaleRotateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix);
+extern void bzMatrixTransformVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/math/random.c b/src/bz/math/random.c
new file mode 100644 (file)
index 0000000..30f6e6c
--- /dev/null
@@ -0,0 +1,43 @@
+#include <bz/math/random.h>
+
+#include <bz/math/math.h>
+#include <pcg_basic.h>
+#include <stdarg.h>
+
+float bzRandomFloat(float max) {
+       uint32_t v = pcg32_random() % 1000;
+       float value = (float)v / 1000.0f;
+       return value * max;
+       //return ((float)pcg32_random() / (float)UINTMAX_MAX);// * max;
+}
+
+float bzRandomFloatRange(float v1, float v2) {
+       if (v1 == v2) {
+               return v1;
+       } else {
+               float min = bzMin(v1, v2);
+               float max = bzMax(v1, v2);
+               float v = bzRandomFloat(bzAbs(max - min)) * bzSgn(max - min);
+               return min + v;
+       }
+}
+
+uint32_t bzRandomInteger(uint32_t max) {
+       return pcg32_boundedrand(max);
+}
+
+int32_t bzRandomIntegerRange(int32_t v1, int32_t v2) {
+       if (v1 == v2) {
+               return v1;
+       } else {
+               int32_t min = bzMin(v1, v2);
+               int32_t max = bzMax(v1, v2);
+               int32_t v = bzRandomInteger(bzAbs(max - min)) * bzSgn(max - min);
+               return min + v;
+       }
+}
+
+void *bzRandomArrayValue(size_t count, size_t arrayTypeSize, void *array) {
+       uint32_t idx = bzRandomInteger(count);
+       return array + idx * arrayTypeSize;
+}
diff --git a/src/bz/math/random.h b/src/bz/math/random.h
new file mode 100644 (file)
index 0000000..4a3df98
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef BZ_MATH_RANDOM_H
+#define BZ_MATH_RANDOM_H
+
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern float bzRandomFloat(float max);
+extern float bzRandomFloatRange(float v1, float v2);
+extern uint32_t bzRandomInteger(uint32_t max);
+extern int32_t bzRandomIntegerRange(int32_t v1, int32_t v2);
+extern void *bzRandomArrayValue(size_t count, size_t arrayTypeSize, void *array);
+//extern void *bzRandomArrayValue(pcg32_random_t *rng, size_t count, size_t arrayTypeSize, void *array);
+
+#define bzRandom(T, ...) (*((T *)bzRandomArrayValue(sizeof((T[]){__VA_ARGS__})/sizeof(T), sizeof(T), (T[]){__VA_ARGS__})))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/math/vector.c b/src/bz/math/vector.c
new file mode 100644 (file)
index 0000000..665acca
--- /dev/null
@@ -0,0 +1,79 @@
+#include <bz/math/vector.h>
+
+#include <bz/math/math.h>
+
+void bzVectorCopy(BZVector *vectorOut, const BZVector *vector) {
+       float x = vector->x;
+       float y = vector->y;
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzVectorMakeAngle(BZVector *vectorOut, float angle) {
+       float x = bzCosine(angle);
+       float y = bzSine(angle);
+       bzVectorSet(vectorOut, x, y);
+
+}
+
+void bzVectorInverse(BZVector *vectorOut, const BZVector *vector) {
+       float x = -(vector->x);
+       float y = -(vector->y);
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzVectorTangent(BZVector *vectorOut, const BZVector *vector) {
+       float x = -(vector->y);
+       float y = vector->x;
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzVectorAdd(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2) {
+       float x = vector1->x + vector2->x;
+       float y = vector1->y + vector2->y;
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzVectorSubtract(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2) {
+       float x = vector1->x - vector2->x;
+       float y = vector1->y - vector2->y;
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzVectorScale(BZVector *vectorOut, const BZVector *vector, float scale) {
+       float x = vector->x * scale;
+       float y = vector->y * scale;
+       bzVectorSet(vectorOut, x, y);
+}
+
+void bzVectorNormalized(BZVector *vectorOut, const BZVector *vector) {
+       float magnitude = bzVectorMagnitude(vector);
+       float x = vector->x / magnitude;
+       float y = vector->y / magnitude;
+       bzVectorSet(vectorOut, x, y);
+}
+
+float bzVectorMagnitude(const BZVector *vector) {
+       float sqMagnitude = bzVectorMagnitudeSquared(vector);
+       float magnitude = bzSqrt(sqMagnitude);
+       return magnitude;
+}
+
+float bzVectorMagnitudeSquared(const BZVector *vector) {
+       return bzVectorDot(vector, vector);
+}
+
+float bzVectorDot(const BZVector *vector1, const BZVector *vector2) {
+       float dot = (vector1->x * vector2->x) + (vector1->y * vector2->y);
+       return dot;
+}
+
+float bzVectorCross(const BZVector *vector1, const BZVector *vector2) {
+       float crossZ = (vector1->x * vector2->y) - (vector1->y * vector2->x);
+       return crossZ;
+}
+
+void bzVectorLerp(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2, float t) {
+       vectorOut->x = bzLerp(vector1->x, vector2->x, t);
+       vectorOut->y = bzLerp(vector1->y, vector2->y, t);
+}
+
diff --git a/src/bz/math/vector.h b/src/bz/math/vector.h
new file mode 100644 (file)
index 0000000..c1d2520
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef BZ_MATH_VECTOR_H
+#define BZ_MATH_VECTOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZVector {
+       float x;
+       float y;
+};
+typedef struct BZVector BZVector;
+
+static inline BZVector *bzVectorSet(BZVector *vectorOut, float x, float y) {
+    vectorOut->x = x;
+    vectorOut->y = y;
+    return vectorOut;
+}
+
+extern void bzVectorCopy(BZVector *vectorOut, const BZVector *vector);
+
+static inline BZVector bzVectorMake(float x, float y) {
+    BZVector v = {
+        .x = x,
+        .y = y,
+    };
+    return v;
+}
+
+extern void bzVectorMakeAngle(BZVector *vectorOut, float angle);
+
+extern void bzVectorInverse(BZVector *vectorOut, const BZVector *vector);
+extern void bzVectorTangent(BZVector *vectorOut, const BZVector *vector);
+
+extern void bzVectorAdd(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2);
+extern void bzVectorSubtract(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2);
+
+extern void bzVectorScale(BZVector *vectorOut, const BZVector *vector, float scale);
+extern void bzVectorNormalized(BZVector *vectorOut, const BZVector *vector);
+
+extern float bzVectorMagnitude(const BZVector *vector);
+extern float bzVectorMagnitudeSquared(const BZVector *vector);
+extern float bzVectorDot(const BZVector *vector1, const BZVector *vector2);
+
+extern float bzVectorCross(const BZVector *vector1, const BZVector *vector2);
+
+extern void bzVectorLerp(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2, float t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/memory/allocator.c b/src/bz/memory/allocator.c
new file mode 100644 (file)
index 0000000..a0516fc
--- /dev/null
@@ -0,0 +1,2 @@
+#include <bz/memory/allocator.h>
+
diff --git a/src/bz/memory/allocator.h b/src/bz/memory/allocator.h
new file mode 100644 (file)
index 0000000..8e0fb22
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef BZ_MEMORY_ALLOCATOR_H
+#define BZ_MEMORY_ALLOCATOR_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void *_bzMemoryAlloc(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber);
+extern void *_bzMemoryAllocTmp(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber);
+
+#define bzMemoryAlloc(arena, size) _bzMemoryAlloc(arena, size, __FILE__, __LINE__)
+#define bzMemoryAllocTmp(arena, size) _bzMemoryAlloc(arena, size, __FILE__, __LINE__)
+
+/* NB: We don't free. Junk the entire arena. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/memory/arena.c b/src/bz/memory/arena.c
new file mode 100644 (file)
index 0000000..079bb8f
--- /dev/null
@@ -0,0 +1,143 @@
+#include <bz/memory/arena_internal.h>
+
+#include <bz/memory/allocator.h>
+#include <stdalign.h>
+#include <stddef.h>
+#include <string.h> // memset... :rolleyes:
+
+/*struct BZExpandedPointer {
+       BZMemoryArenaID arena;
+       uint8_t data[];
+};
+typedef struct BZExpandedPointer BZExpandedPointer;*/
+
+//const size_t kMaxUserArenas = 32;
+#define kMaxUserArenas 32 // FIXME
+static BZMemoryArena userArenas[kMaxUserArenas];
+static size_t nextUserArena = 0;
+
+BZMemoryArenaID kBZSystemMemoryArena = &userArenas[0];
+
+static size_t allocateSize(size_t size) {
+       size_t alignment = alignof(max_align_t); // FIXME, check this alignment stuff...
+       size_t alignmentPadding = (alignment - (size % alignment)) % alignment;
+       size_t fullSize = size + alignmentPadding;// + sizeof(BZExpandedPointer);
+       return fullSize;
+}
+
+BZMemoryArenaID bzMemoryArenaCreate(BZMemoryArenaID hostArena, size_t size, const char *nameFmt, ...) {
+       bzAssert(size == allocateSize(size));
+       if (nextUserArena < kMaxUserArenas) {
+               return bzMemoryArenaAllocate(&userArenas[++nextUserArena], hostArena, size);
+       } else {
+               bzError("Too many arenas");
+               return NULL;
+       }
+}
+
+void bzMemoryArenaReset(BZMemoryArenaID arena) {
+       bzMemoryArenaSetup(arena, arena->memory, arena->maxSize, arena->zeroOut);
+}
+
+void bzMemoryArenaResetTmp(BZMemoryArenaID arena) {
+       if (arena->zeroOut) {
+               memset(&arena->memory[arena->maxSize - arena->allocationOffsetTop], 0, arena->allocationOffsetTop);
+       }
+       arena->allocationOffsetTop = 0;
+}
+
+BZMemoryArenaID bzMemoryArenaSetup(BZMemoryArena *arenaOut, void *memory, size_t size, bool zeroOut) {
+       arenaOut->maxSize = size;
+       arenaOut->memory = memory;
+       arenaOut->numWatermarks = 0;
+       arenaOut->zeroOut = zeroOut;
+
+       arenaOut->allocationOffsetBottom = 0;
+       arenaOut->allocationOffsetTop = 0;
+
+       if (arenaOut->zeroOut) {
+               // We're gonna interop with C++, so we really need to do this.
+               memset(memory, 0, size);
+       }
+
+       return arenaOut;
+}
+
+BZMemoryArenaID bzMemoryArenaAllocate(BZMemoryArena *arenaOut, BZMemoryArenaID arenaFrom, size_t size) {
+       void *memory = bzMemoryAlloc(arenaFrom, size);
+       return bzMemoryArenaSetup(arenaOut, memory, size, true);
+}
+
+void bzMemoryArenaPushWatermark(BZMemoryArenaID arena) {
+       if (arena->numWatermarks < MAX_WATERMARKS) {
+               arena->watermarks[arena->numWatermarks++] = arena->allocationOffsetBottom;
+       } else {
+               bzError("Watermarked too many times!!");
+       }
+}
+
+void bzMemoryArenaPopWatermark(BZMemoryArenaID arena) {
+       if (arena->numWatermarks > 0) {
+               size_t lastAllocation = arena->allocationOffsetBottom;
+               arena->allocationOffsetBottom = arena->watermarks[--arena->numWatermarks];
+               
+               if (arena->zeroOut) {
+                       // We're gonna interop with C++, so we really need to do this.
+                       // Zero out anything that got used.
+                       memset(&arena->memory[arena->allocationOffsetBottom], 0, lastAllocation - arena->allocationOffsetBottom);
+               }
+       } else {
+               bzError("Watermarked too many times!!");
+       }
+}
+
+void *_bzMemoryAlloc(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber) {
+       bzAssertMessage(arena != NULL, "No memory arena");
+
+       size_t fullSize = allocateSize(size);
+
+       void *ptr = NULL;
+       if (arena == kBZSystemMemoryArena) {
+               ptr = bzSystemAllocate(fullSize);
+//             if (arena->zeroOut) {
+                       memset(ptr, 0, fullSize); // Just do this one...
+//             }
+       } else if (arena->allocationOffsetBottom + fullSize <= arena->maxSize - arena->allocationOffsetTop) {
+               ptr = &arena->memory[arena->allocationOffsetBottom];
+               arena->allocationOffsetBottom += fullSize;
+       }
+       
+       bzAssertMessage(ptr != NULL, "Arena is out of memory, failed %s:%d", filename, lineNumber);
+
+       return ptr;
+}
+
+void *_bzMemoryAllocTmp(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber) {
+       bzAssertMessage(arena != NULL, "No memory arena");
+
+       size_t fullSize = allocateSize(size);
+
+       void *ptr = NULL;
+       if (arena == kBZSystemMemoryArena) {
+               ptr = bzSystemAllocateStack(fullSize);
+       } else if (arena->maxSize - arena->allocationOffsetTop - fullSize >= arena->allocationOffsetBottom) {
+               arena->allocationOffsetTop += fullSize;
+               ptr = &arena->memory[arena->maxSize - arena->allocationOffsetTop];
+       }
+       
+       bzAssertMessage(ptr != NULL, "Arena is out of memory, failed %s:%d", filename, lineNumber);
+
+       return ptr;
+} // FIXME, this won't work if you pass arena to other places, need more watermarking??....
+
+
+//void bzMemoryFree(void *p) {
+//     void *ptr = p;
+//     ptr -= sizeof(BZExpandedPointer);
+
+//     if (*(BZMemoryArenaID *)ptr == kBZSystemMemoryArena) {
+//             free(ptr);
+//     } else {
+//             bzLog("Asked to free arena memory that cannot be");
+//     }
+//}
diff --git a/src/bz/memory/arena.h b/src/bz/memory/arena.h
new file mode 100644 (file)
index 0000000..cf61262
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef BZ_MEMORY_STADIUM_H
+#define BZ_MEMORY_STADIUM_H
+
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZMemoryArena BZMemoryArena;
+typedef BZMemoryArena * BZMemoryArenaID;
+
+extern BZMemoryArenaID kBZSystemMemoryArena;
+
+extern BZMemoryArenaID bzMemoryArenaCreate(BZMemoryArenaID hostArena, size_t size, const char *nameFmt, ...);
+
+extern void bzMemoryArenaReset(BZMemoryArenaID arena);
+extern void bzMemoryArenaResetTmp(BZMemoryArenaID arena); // Reset temporary memory only
+
+extern void bzMemoryArenaPushWatermark(BZMemoryArenaID arena);
+extern void bzMemoryArenaPopWatermark(BZMemoryArenaID arena);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/memory/arena_internal.h b/src/bz/memory/arena_internal.h
new file mode 100644 (file)
index 0000000..8eab3ba
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef BZ_MEMORY_STADIUM_INTERNAL_H
+#define BZ_MEMORY_STADIUM_INTERNAL_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_WATERMARKS 16
+
+struct BZMemoryArena {
+       size_t maxSize;
+       uint8_t *memory;
+       bool zeroOut;
+
+       size_t allocationOffsetBottom;
+       size_t allocationOffsetTop;
+
+       size_t numWatermarks;
+       size_t watermarks[MAX_WATERMARKS];
+};
+
+extern BZMemoryArenaID bzMemoryArenaSetup(BZMemoryArena *arenaOut, void *memory, size_t size, bool zeroOut);
+extern BZMemoryArenaID bzMemoryArenaAllocate(BZMemoryArena *arenaOut, BZMemoryArenaID arenaFrom, size_t size);
+
+//extern void bzMemoryFree(void *p);
+
+extern void *bzSystemAllocate(size_t size);
+extern void *bzSystemAllocateStack(size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/renderer/palette.c b/src/bz/renderer/palette.c
new file mode 100644 (file)
index 0000000..acfbc0b
--- /dev/null
@@ -0,0 +1,74 @@
+#include <bz/renderer/palette_internal.h>
+
+#include <bz/gfx/aseprite_internal.h>
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+#include <parson.h>
+
+BZRendererPaletteID bzRendererLoadPalette(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZResourceID handle = bzResourcesOpenResource("palettes", "assets/palettes/%s.palette.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+
+       char *data = bzMemoryAllocTmp(arena, length);
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *paletteJson = json_parse_string(data);
+       JSON_Object *paletteJsonObject = json_object(paletteJson);
+
+       JSON_Array *colorNameArray = json_object_get_array(paletteJsonObject, "colors");
+       size_t colorCount = json_array_get_count(colorNameArray);
+
+       BZRendererPaletteID palette = bzMemoryAlloc(arena, sizeof(BZRendererPalette) + colorCount * sizeof(uint32_t));
+
+       const char *asepriteFilename = json_object_get_string(paletteJsonObject, "file");
+
+       palette->numColors = bzGfxLoadAsepritePalette(palette->colors, colorCount, asepriteFilename);
+
+       json_value_free(paletteJson);
+       bzMemoryArenaResetTmp(arena);
+
+       return palette;
+}
+
+/*
+#define bzPaletteMakeColor(r, g, b) ((r)&0xFF) << 0 | ((g)&0xFF) << 8 | ((b)&0xFF) << 16 | (0xFF) << 24
+
+
+{
+       "file" : "main.aseprite",
+       "colors" : [
+               "black",
+               "tone-1",
+               "tone-2",
+               "tone-3",
+               "tone-4",
+               "tone-5",
+               "tone-6",
+               "white",
+               "red-1",
+               "red-2",
+               "red-3",
+               "green-1",
+               "green-2",
+               "green-3",
+               "blue-1",
+               "blue-2",
+               "blue-3",
+               "orange-1",
+               "orange-2",
+               "orange-3",
+               "pink-1",
+               "pink-2",
+               "pink-3",
+               "yellow-1",
+               "yellow-2",
+               "yellow-3"
+       ]
+}
+*/
+
diff --git a/src/bz/renderer/palette.h b/src/bz/renderer/palette.h
new file mode 100644 (file)
index 0000000..47293fc
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef BZ_RENDERER_PALETTE_H
+#define BZ_RENDERER_PALETTE_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZRendererPalette BZRendererPalette;
+typedef BZRendererPalette * BZRendererPaletteID;
+
+extern BZRendererPaletteID bzRendererLoadPalette(BZMemoryArenaID arena, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/renderer/palette_internal.h b/src/bz/renderer/palette_internal.h
new file mode 100644 (file)
index 0000000..f386655
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef BZ_RENDERER_PALETTE_INTERNAL_H
+#define BZ_RENDERER_PALETTE_INTERNAL_H
+
+#include <bz/renderer/palette.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZRendererPalette {
+       size_t numColors;
+       uint32_t colors[];
+};
+
+#define bzPaletteMakeColor(r, g, b) ((r)&0xFF) << 0 | ((g)&0xFF) << 8 | ((b)&0xFF) << 16 | (0xFF) << 24
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/renderer/render_pass.h b/src/bz/renderer/render_pass.h
new file mode 100644 (file)
index 0000000..1371f7f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef BZ_RENDERER_RENDER_PASS_H
+#define BZ_RENDERER_RENDER_PASS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZRenderPass BZRenderPass;
+typedef BZRenderPass * BZRenderPassID;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/renderer/render_pass_internal.h b/src/bz/renderer/render_pass_internal.h
new file mode 100644 (file)
index 0000000..733ee5c
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef BZ_RENDERER_RENDER_PASS_INTERNAL_H
+#define BZ_RENDERER_RENDER_PASS_INTERNAL_H
+
+#include <bz/renderer/render_pass.h>
+
+#include <bz/renderer/palette.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//, size_t width, size_t height, void *fmt
+//typedef void (*BZRenderPassSoftwareRenderer)(uint32_t *output);
+//typedef void (*BZRenderPassSetup)(size_t width, size_t height, void *fmt);
+typedef void (*BZRenderPassSoftwareRenderer)(BZRendererPaletteID palette, uint32_t *output, size_t width, size_t height, size_t pixelWidth, size_t pixelHeight);
+
+struct BZRenderPass {
+//     BZRenderPassSetup setupHook;
+       BZRenderPassSoftwareRenderer softwareHook;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/renderer/renderer.c b/src/bz/renderer/renderer.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/bz/renderer/renderer.h b/src/bz/renderer/renderer.h
new file mode 100644 (file)
index 0000000..d796f04
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef BZ_RENDERER_RENDERER_H
+#define BZ_RENDERER_RENDERER_H
+
+#include <bz/memory/arena.h>
+#include <bz/renderer/palette.h>
+#include <bz/renderer/render_pass.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZRendererConfiguration {
+       size_t outputWidth;
+       size_t outputHeight;
+       size_t rendererWidth;
+       size_t rendererHeight;
+       size_t gameWidth;
+       size_t gameHeight;
+       BZRendererPaletteID palette;
+};
+typedef struct BZRendererConfiguration BZRendererConfiguration;
+
+typedef struct BZRenderer BZRenderer;
+typedef BZRenderer * BZRendererID;
+
+typedef struct BZRendererPass BZRendererPass;
+typedef BZRendererPass * BZRendererPassID;
+
+extern BZRendererID bzRendererInit(BZMemoryArenaID arena, const BZRendererConfiguration *configuration, const char *title);
+extern void bzRendererTeardown(BZRendererID renderer);
+
+BZRendererPassID bzRendererRegisterPass(BZRendererID renderer, BZRenderPassID renderPass);
+extern void bzRendererUnregisterPass(BZRendererPassID rendererPass);
+
+extern void bzRendererBlit(BZRendererID renderer);
+extern void bzRendererVBlank(BZRendererID renderer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/renderer/renderer_internal.h b/src/bz/renderer/renderer_internal.h
new file mode 100644 (file)
index 0000000..ac378ef
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_RENDERER_RENDERER_INTERNAL_H
+#define BZ_RENDERER_RENDERER_INTERNAL_H
+
+#include <bz/renderer/renderer.h>
+//#include <bz/renderer/renderer_configuration.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/resources/resource.c b/src/bz/resources/resource.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/bz/resources/resource.h b/src/bz/resources/resource.h
new file mode 100644 (file)
index 0000000..d72a5c8
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef BZ_RESOURCES_RESOURCE_H
+#define BZ_RESOURCES_RESOURCE_H
+
+#include <bz/memory/arena.h>
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct BZResource BZResource;
+typedef BZResource * BZResourceID;
+
+extern BZResourceID bzResourcesOpenResource(const char *type, const char *identifierFmt, ...);
+extern void bzResourcesCloseResource(BZResourceID resource);
+
+// FIXME, these should really be resource handles or something.
+extern size_t bzResourcesFileLength(BZResourceID resource);
+extern size_t bzResourcesTell(BZResourceID resource);
+extern size_t bzResourcesSeek(BZResourceID resource, size_t position);
+extern size_t bzResourcesReadBytes(BZResourceID resource, void *outputBuffer, size_t numBytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/bindings.cpp b/src/bz/scripting/bindings.cpp
new file mode 100644 (file)
index 0000000..2a88b12
--- /dev/null
@@ -0,0 +1,749 @@
+#include <bz/scripting/bindings_internal.h>
+
+#include <bz/game/scene_internal.h>
+#include <bz/game/tilemap.h>
+#include <bz/gfx/drawing.h>
+//#include <bz/gfx/gfx.h>
+#include <bz/gfx/tilemap.h>
+#include <bz/input/input.h>
+#include <bz/math/math.h>
+#include <bz/math/random.h>
+
+#include <stb_sprintf.h>
+
+#include "static_crc32.hpp"
+
+#define _FUNCTION_PARAMETER(idx,t) stack[idx].t##Literal
+#define _FUNCTION_BINDING(n, f, ...) static void native_##n(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); f( __VA_ARGS__ ); }
+#define _FUNCTION_BINDING_RTN(n, f, ...) static void native_##n(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); stack[0].__raw =  (int32_t)(f( __VA_ARGS__ )); }
+#define _FUNCTION_BINDING_RTN_WRAP(n, f, ...) static void native_##n(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); stack[0].__raw =  FIXED(f( __VA_ARGS__ )); }
+#define _FUNCTION_BINDING_RTN_WRAP_FLOAT(n, f, ...) static void native_##n(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); stack[0].__raw =  FIXED_FROM_FLOAT(f( __VA_ARGS__ )); }
+
+#define _CONTEXT_PARAMETER(name) (metadata->name)
+
+#define _RAW_PARAMETER(idx) stack[idx].__raw
+#define _INT_PARAMETER(idx) FIXED_TO_INT(_FUNCTION_PARAMETER(idx, integer))
+#define _FLOAT_PARAMETER(idx) FIXED_TO_FLOAT(_FUNCTION_PARAMETER(idx, integer))
+#define _STRING_PARAMETER(idx) svmGetString(instance, _FUNCTION_PARAMETER(idx, pointer))
+
+#define FUNCTION_BIND(n) case COMPILE_TIME_CRC32_STR(#n): return native_##n;
+
+static void calculateTransformMatrix(BZScriptBindingMetadata *metadata) {
+       bzMatrixSRT(&metadata->matrix, metadata->position.x, metadata->position.y, metadata->angle, metadata->scale.x, metadata->scale.y);
+}
+
+static void resetTransform(BZScriptBindingMetadata *metadata) {
+       bzVectorSet(&metadata->position, 0, 0);
+       metadata->angle = 0.0f;
+       bzVectorSet(&metadata->scale, 1, 1);
+       //bzMatrixIdentity(&metadata->matrix);
+       calculateTransformMatrix(metadata);
+}
+
+void bzScriptingInitialiseMetadata(BZScriptBindingMetadata *metadata, BZActorID actor, BZSceneID scene, uint32_t uuid) {
+       *metadata = (BZScriptBindingMetadata) {
+               .actor = actor,
+               .scene = scene,
+               .sortIdx = 0,
+               .drawQueue = NULL,
+               .uuid = uuid,
+       };
+       resetTransform(metadata);
+}
+
+_FUNCTION_BINDING(log, bzLog, "%s", svmGetString(instance, stack[1].pointerLiteral))
+
+static void native_logf(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       const char *formatString = _STRING_PARAMETER(1);
+
+       char outputString[256]; // fixme
+       char *o = outputString;
+
+       size_t paramIdx = 2;
+
+       for (const char *f = formatString; *f != '\0'; ++f) {
+               switch (*f) {
+                       case 's':
+                               o += stbsp_sprintf(o, "%s", _STRING_PARAMETER(paramIdx++));
+                               break;
+
+                       case 'd':
+                               o += stbsp_sprintf(o, "%d", _INT_PARAMETER(paramIdx++));
+                               break;
+               }
+       }
+
+       bzLog("%s", outputString);
+}
+
+//_FUNCTION_BINDING(logNum, bzLog, "%f", FIXED_TO_FLOAT(stack[1].floatLiteral))
+
+_FUNCTION_BINDING_RTN(sqrt, fixedSqrt, _FUNCTION_PARAMETER(1, float))
+_FUNCTION_BINDING_RTN(sin, fixedSin, _FUNCTION_PARAMETER(1, float))
+_FUNCTION_BINDING_RTN(cos, fixedCos, _FUNCTION_PARAMETER(1, float))
+_FUNCTION_BINDING_RTN(atan2, fixedAtan2, _FUNCTION_PARAMETER(1, float), _FUNCTION_PARAMETER(2, float))
+_FUNCTION_BINDING_RTN_WRAP_FLOAT(adelta, bzAngleDelta, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2))
+
+_FUNCTION_BINDING_RTN_WRAP_FLOAT(distance, bzDistance, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3), _FLOAT_PARAMETER(4))
+_FUNCTION_BINDING_RTN(distanceCheck, bzDistanceCheck, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3), _FLOAT_PARAMETER(4), _FLOAT_PARAMETER(5))
+
+_FUNCTION_BINDING_RTN(min, fixedMin, _FUNCTION_PARAMETER(1, float), _FUNCTION_PARAMETER(2, float))
+_FUNCTION_BINDING_RTN(max, fixedMax, _FUNCTION_PARAMETER(1, float), _FUNCTION_PARAMETER(2, float))
+_FUNCTION_BINDING_RTN(mid, fixedMid, _FUNCTION_PARAMETER(1, float), _FUNCTION_PARAMETER(2, float), _FUNCTION_PARAMETER(3, float))
+_FUNCTION_BINDING_RTN(sgn, fixedSgn, _FUNCTION_PARAMETER(1, float))
+_FUNCTION_BINDING_RTN(abs, fixedAbs, _FUNCTION_PARAMETER(1, float))
+_FUNCTION_BINDING_RTN(floor, fixedFloor, _FUNCTION_PARAMETER(1, float))
+_FUNCTION_BINDING_RTN(ceil, fixedCeil, _FUNCTION_PARAMETER(1, float))
+
+static void native_rnd(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       switch (parameterCount) {
+               case 0:
+                       stack[0].__raw = FIXED_FROM_FLOAT(bzRandomFloat(1));
+                       break;
+
+               case 1:
+                       stack[0].__raw = FIXED_FROM_FLOAT(bzRandomFloat(_FLOAT_PARAMETER(1)));
+                       break;
+
+               default:
+                       stack[0].__raw = FIXED_FROM_FLOAT(bzRandomFloatRange(_FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2)));
+                       break;
+       }
+}
+
+_FUNCTION_BINDING_RTN(lerp, fixedLerp, _FUNCTION_PARAMETER(1, float), _FUNCTION_PARAMETER(2, float), _FUNCTION_PARAMETER(3, float))
+
+static void native_dot(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZVector v1 = bzVectorMake(_FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2));
+       BZVector v2 = bzVectorMake(_FLOAT_PARAMETER(3), _FLOAT_PARAMETER(4));
+       float dot = bzVectorDot(&v1, &v2);
+       stack[0].floatLiteral = FIXED_FROM_FLOAT(dot);
+}
+
+_FUNCTION_BINDING_RTN_WRAP_FLOAT(time, bzGameSceneGetTime, _CONTEXT_PARAMETER(scene))
+
+//FUNCTION_BINDING_P4(clip, bzClip, integer, integer, integer, integer)
+////FUNCTION_BINDING_P3(pset, bzPSet, integer, integer, integer)
+////FUNCTION_BINDING_RTN_P2(sget, bzSGet, integer, integer)
+
+_FUNCTION_BINDING(cls, bzDrawCls, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1))//FUNCTION_BINDING_P1(cls, bzDrawCls, integer)
+_FUNCTION_BINDING(camera, bzDrawCamera, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2))//FUNCTION_BINDING_P2(camera, bzDrawCamera, integer, integer)
+_FUNCTION_BINDING(circ, bzDrawCirc, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4))//FUNCTION_BINDING_P4(circ, bzDrawCirc, integer, integer, integer, integer)
+_FUNCTION_BINDING(circfill, bzDrawCircFill, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4))//FUNCTION_BINDING_P4(circfill, bzDrawCircFill, integer, integer, integer, integer)
+_FUNCTION_BINDING(line, bzDrawLine, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4), _INT_PARAMETER(5))//FUNCTION_BINDING_P5(line, bzDrawLine, integer, integer, integer, integer, integer)
+_FUNCTION_BINDING(rect, bzDrawRect, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4), _INT_PARAMETER(5))//FUNCTION_BINDING_P5(rect, bzDrawRect, integer, integer, integer, integer, integer)
+_FUNCTION_BINDING(rectfill, bzDrawRectFill, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4), _INT_PARAMETER(5))//FUNCTION_BINDING_P5(rectfill, bzDrawRectFill, integer, integer, integer, integer, integer)
+_FUNCTION_BINDING(triangle, bzDrawTriangle, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4), _INT_PARAMETER(5), _INT_PARAMETER(6), _INT_PARAMETER(7))
+//_FUNCTION_BINDING(spr, bzDrawSpr, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3))//FUNCTION_BINDING_P3(spr, bzDrawSpr, integer, integer, integer)
+// spr ext
+_FUNCTION_BINDING(sspr, bzDrawSSpr, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4), _INT_PARAMETER(5), _INT_PARAMETER(6))//FUNCTION_BINDING_P6(sspr, bzDrawSSpr, integer, integer, integer, integer, integer, integer)
+// ext
+_FUNCTION_BINDING(print, bzDrawPrint, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _STRING_PARAMETER(4))
+_FUNCTION_BINDING(fillp, bzDrawFillP, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _RAW_PARAMETER(1))//FUNCTION_BINDING_P1(fillp, bzDrawFillP, integer)
+_FUNCTION_BINDING(palette, bzDrawSetPaletteColor, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3))//FUNCTION_BINDING_P3(palette, bzDrawSetPaletteColor, integer, integer, integer)
+_FUNCTION_BINDING(output, bzDrawSetOutputBuffer, _CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1)) //FUNCTION_BINDING_P1(output, bzSetOutputBuffer, integer)
+
+static void native_printf(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       const char *formatString = _STRING_PARAMETER(4);
+
+       char outputString[256]; // fixme
+       char *o = outputString;
+
+       size_t paramIdx = 5;
+
+       for (const char *f = formatString; *f != '\0'; ++f) {
+               switch (*f) {
+                       case 's':
+                               o += stbsp_sprintf(o, "%s", _STRING_PARAMETER(paramIdx++));
+                               break;
+
+                       case 'd':
+                               o += stbsp_sprintf(o, "%d", _INT_PARAMETER(paramIdx++));
+                               break;
+               }
+       }
+
+       bzDrawPrint(_CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), outputString);
+}
+
+static void native_spr(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       int w = (parameterCount >= 4) ? _INT_PARAMETER(4) : 1;
+       int h = (parameterCount >= 5) ? _INT_PARAMETER(5) : 1;
+       bool flipX = (parameterCount >= 6) ? _INT_PARAMETER(6) : false;
+       bool flipY = (parameterCount >= 7) ? _INT_PARAMETER(7) : false;
+       bzDrawSprExt(_CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), w, h, flipX, flipY);
+}
+
+static void native_map(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZTilemapID tilemap = bzGameGetSceneLayerTilemap(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer));
+       uint32_t mask = (parameterCount >= 7) ? bzGameCalculateTilemapFlagsMask(tilemap, parameterCount - 6, &stack[7].pointerLiteral) : 0;
+       bzDrawMap(_CONTEXT_PARAMETER(drawQueue), _CONTEXT_PARAMETER(sortIdx), &_CONTEXT_PARAMETER(matrix), tilemap, _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3), _INT_PARAMETER(4), _INT_PARAMETER(5), _INT_PARAMETER(6), mask);
+}
+
+_FUNCTION_BINDING_RTN_WRAP(mget, bzGameGetTilemapTile, bzGameGetSceneLayerTilemap(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer)), _INT_PARAMETER(1), _INT_PARAMETER(2))
+_FUNCTION_BINDING(mset, bzGameSetTilemapTile, bzGameGetSceneLayerTilemap(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer)), _INT_PARAMETER(1), _INT_PARAMETER(2), _INT_PARAMETER(3))
+_FUNCTION_BINDING_RTN(mfget, bzGameGetTilemapHasFlag, bzGameGetSceneLayerTilemap(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer)), _INT_PARAMETER(1), _INT_PARAMETER(2), _RAW_PARAMETER(3))
+
+static void native_particle(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+
+       BZVector position;
+       bzVectorCopy(&position, &_CONTEXT_PARAMETER(position));
+       float angle = _CONTEXT_PARAMETER(angle);
+
+       if (parameterCount >= 3) {
+               position.x = _FLOAT_PARAMETER(2);
+               position.y = _FLOAT_PARAMETER(3);
+       }
+
+       if (parameterCount >= 4) {
+               angle = _FLOAT_PARAMETER(4);
+       }
+
+       size_t layerCount = bzGameGetSceneLayerCount(_CONTEXT_PARAMETER(scene));
+       for (size_t i = 0; i < layerCount; ++i) {
+               BZSceneLayerID layer = bzGameGetSceneLayerAtIndex(_CONTEXT_PARAMETER(scene), i);
+               BZParticleSimulationID simulation = bzGameGetSceneLayerParticleSimulation2(layer);
+               if (simulation != NULL) {
+                       bzFXSpawnParticles(simulation, stack[1].pointerLiteral, &position, angle);
+               }
+       }
+}
+
+static void native_sound(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZAudioPlaybackEngineID audioPlaybackEngine = bzGameGetAudioPlaybackEngine(_CONTEXT_PARAMETER(scene));
+       bzAudioPlaybackPostEvent(audioPlaybackEngine, stack[1].pointerLiteral);
+}
+
+static void native_speech(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZAudioPlaybackEngineID audioPlaybackEngine = bzGameGetAudioPlaybackEngine(_CONTEXT_PARAMETER(scene));
+       bzAudioPlaybackPostEvent(audioPlaybackEngine, stack[1].pointerLiteral, _STRING_PARAMETER(2));
+}
+
+static void native_speechf(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       const char *formatString = _STRING_PARAMETER(2);
+
+       char outputString[256]; // fixme
+       char *o = outputString;
+
+       size_t paramIdx = 3;
+
+       for (const char *f = formatString; *f != '\0'; ++f) {
+               switch (*f) {
+                       case 's':
+                               o += stbsp_sprintf(o, "%s", _STRING_PARAMETER(paramIdx++));
+                               break;
+
+                       case 'd':
+                               o += stbsp_sprintf(o, "%d", _INT_PARAMETER(paramIdx++));
+                               break;
+               }
+       }
+
+       BZAudioPlaybackEngineID audioPlaybackEngine = bzGameGetAudioPlaybackEngine(_CONTEXT_PARAMETER(scene));
+       bzAudioPlaybackPostEvent(audioPlaybackEngine, stack[1].pointerLiteral, outputString);
+}
+
+static void native_collision(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZCollisionSpaceID space = bzGameGetSceneLayerCollisionSpace(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer));
+       
+       bzAssert(space != NULL);
+       
+       BZCollisionShape shape;
+       BZIdentifierHash tagHash;
+       switch (stack[1].pointerLiteral) {
+               case COMPILE_TIME_CRC32_STR("circle"):
+                       shape = {
+                               .type = BZCollisionShapeTypeCircle,
+                               .circleOrigin = bzVectorMake(_FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3)),
+                               .circleRadius = _FLOAT_PARAMETER(4),
+                       };
+                       bzMatrixTransformVector(&shape.circleOrigin, &shape.circleOrigin, &_CONTEXT_PARAMETER(matrix));
+                       // FIXME, radius
+                       tagHash = _RAW_PARAMETER(5);
+                       break;
+               
+               case COMPILE_TIME_CRC32_STR("circlePerimeter"):
+                       shape = {
+                               .type = BZCollisionShapeTypeCirclePerimeter,
+                               .circleOrigin = bzVectorMake(_FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3)),
+                               .circleRadius = _FLOAT_PARAMETER(4),
+                       };
+                       bzMatrixTransformVector(&shape.circleOrigin, &shape.circleOrigin, &_CONTEXT_PARAMETER(matrix));
+                       // FIXME, radius
+                       tagHash = _RAW_PARAMETER(5);
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("triangle"):
+                       shape = {
+                               .type = BZCollisionShapeTypeTriangle,
+                               .trianglePoint1 = bzVectorMake(_FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3)),
+                               .trianglePoint2 = bzVectorMake(_FLOAT_PARAMETER(4), _FLOAT_PARAMETER(5)),
+                               .trianglePoint3 = bzVectorMake(_FLOAT_PARAMETER(6), _FLOAT_PARAMETER(7)),
+                       };
+                       bzMatrixTransformVector(&shape.trianglePoint1, &shape.trianglePoint1, &_CONTEXT_PARAMETER(matrix));
+                       bzMatrixTransformVector(&shape.trianglePoint2, &shape.trianglePoint2, &_CONTEXT_PARAMETER(matrix));
+                       bzMatrixTransformVector(&shape.trianglePoint3, &shape.trianglePoint3, &_CONTEXT_PARAMETER(matrix));
+                       tagHash = _RAW_PARAMETER(8);
+                       break;
+       }
+
+       bzCollisionAddBody(space, BZCollisionBodyTypeSolid, &shape, tagHash, instance);
+}
+
+static void native_collisionCheck(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       //BZCollisionSpaceID space = bzGameGetSceneLayerCollisionSpace(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer));
+
+       //bzAssert(space != NULL);
+
+       bool getOutput = true; // FIXME FIXME FIXME, this should only return when we need it?
+       bool removeParticles = true;
+
+       BZCollisionShape shape;
+       BZIdentifierHash tagHash;
+       switch (stack[1].pointerLiteral) {
+               case COMPILE_TIME_CRC32_STR("circle"):
+                       shape = {
+                               .type = BZCollisionShapeTypeCircle,
+                               .circleOrigin = bzVectorMake(_FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3)),
+                               .circleRadius = _FLOAT_PARAMETER(4),
+                       };
+                       bzMatrixTransformVector(&shape.circleOrigin, &shape.circleOrigin, &_CONTEXT_PARAMETER(matrix));
+                       // FIXME, radius
+                       tagHash = _RAW_PARAMETER(5);
+                       if (parameterCount >= 6) removeParticles = _INT_PARAMETER(6) != 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("circlePerimeter"):
+                       shape = {
+                               .type = BZCollisionShapeTypeCirclePerimeter,
+                               .circleOrigin = bzVectorMake(_FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3)),
+                               .circleRadius = _FLOAT_PARAMETER(4),
+                       };
+                       bzMatrixTransformVector(&shape.circleOrigin, &shape.circleOrigin, &_CONTEXT_PARAMETER(matrix));
+                       // FIXME, radius
+                       tagHash = _RAW_PARAMETER(5);
+                       if (parameterCount >= 6) removeParticles = _INT_PARAMETER(6) != 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("triangle"):
+                       shape = {
+                               .type = BZCollisionShapeTypeTriangle,
+                               .trianglePoint1 = bzVectorMake(_FLOAT_PARAMETER(2), _FLOAT_PARAMETER(3)),
+                               .trianglePoint2 = bzVectorMake(_FLOAT_PARAMETER(4), _FLOAT_PARAMETER(5)),
+                               .trianglePoint3 = bzVectorMake(_FLOAT_PARAMETER(6), _FLOAT_PARAMETER(7)),
+                       };
+                       bzMatrixTransformVector(&shape.trianglePoint1, &shape.trianglePoint1, &_CONTEXT_PARAMETER(matrix));
+                       bzMatrixTransformVector(&shape.trianglePoint2, &shape.trianglePoint2, &_CONTEXT_PARAMETER(matrix));
+                       bzMatrixTransformVector(&shape.trianglePoint3, &shape.trianglePoint3, &_CONTEXT_PARAMETER(matrix));
+                       tagHash = _RAW_PARAMETER(8);
+                       if (parameterCount >= 9) removeParticles = _INT_PARAMETER(9) != 0;
+                       break;
+       }
+
+       stack[0].__raw = 0;
+
+       size_t layerCount = bzGameGetSceneLayerCount(_CONTEXT_PARAMETER(scene));
+       for (size_t i = 0; i < layerCount; ++i) {
+               BZSceneLayerID layer = bzGameGetSceneLayerAtIndex(_CONTEXT_PARAMETER(scene), i);
+               BZCollisionSpaceID space = bzGameGetSceneLayerCollisionSpace2(layer);
+               if (space != NULL) {
+                       void *userParameter;
+                       
+                       if (getOutput) { // FIXME FIXME FIXME
+                               BZVector out;
+                               stack[0].__raw = bzCollisionResolve(&out, &userParameter, space, &shape, tagHash);
+
+                               // FIXME: This is terrible
+                               bzScriptingSetEnvironmentPublic(_CONTEXT_PARAMETER(scene), COMPILE_TIME_CRC32_STR("collisionX"), { .floatLiteral = FIXED_FROM_FLOAT(out.x) });
+                               bzScriptingSetEnvironmentPublic(_CONTEXT_PARAMETER(scene), COMPILE_TIME_CRC32_STR("collisionY"), { .floatLiteral = FIXED_FROM_FLOAT(out.y) });
+                       } else {
+                               stack[0].__raw = bzCollisionResolve(NULL, &userParameter, space, &shape, tagHash);
+                       }
+
+                       if (stack[0].__raw) {
+                               if (removeParticles) {
+                                       BZParticleSimulationID particleSimulation = bzGameGetSceneLayerParticleSimulation2(layer);
+                                       if (particleSimulation != NULL) {
+                                               bzFXDespawnParticle(particleSimulation, (size_t)userParameter);
+                                       }
+                               }
+                               break;
+                       }
+               }
+       }
+}
+
+#include <bz/game/actor_internal.h>
+static void native_actor(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZActorID actor = bzGameSceneAddActor(_CONTEXT_PARAMETER(scene), "%s", svmGetString(instance, stack[1].pointerLiteral));
+       for (size_t i = 2, j = 1; i <= parameterCount; ++i, ++j) {
+               actor->parameters[j] = stack[i].__raw;
+       }
+}
+
+static void native_find(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZActorID actor = bzGameSceneFindActor(_CONTEXT_PARAMETER(scene), "%s", svmGetString(instance, stack[1].pointerLiteral));
+       stack[0].pointerLiteral = actor->identifierHash; // FIXME, use idx or something...
+}
+
+static void native_parameter(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       stack[0].__raw = _CONTEXT_PARAMETER(actor)->parameters[FIXED_TO_INT(stack[1].integerLiteral)];
+}
+
+#include <bz/scripting/environment_internal.h>
+#include <bz/scripting/script_internal.h>
+static void native_public(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+
+       if (parameterCount >= 2) {
+               bzScriptingSetEnvironmentPublic(_CONTEXT_PARAMETER(scene), stack[1].pointerLiteral, stack[2]);
+               stack[0] = stack[2];
+       } else {
+               if (bzScriptingGetEnvironmentPublic(&stack[0], _CONTEXT_PARAMETER(scene), stack[1].pointerLiteral) == false) {
+                       stack[0].__raw = 0;
+               }
+       }
+}
+
+static void native_btn(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       switch (stack[1].pointerLiteral) {
+               case COMPILE_TIME_CRC32_STR("up"):
+                       stack[0].__raw = bzInputBtn(1) ? 1 : 0;
+                       break;
+               
+               case COMPILE_TIME_CRC32_STR("down"):
+                       stack[0].__raw = bzInputBtn(2) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("left"):
+                       stack[0].__raw = bzInputBtn(3) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("right"):
+                       stack[0].__raw = bzInputBtn(4) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("action1"):
+                       stack[0].__raw = bzInputBtn(5) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("action2"):
+                       stack[0].__raw = bzInputBtn(6) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("alpha-up"):
+                       stack[0].__raw = bzInputBtn(7) ? 1 : 0;
+                       break;
+               
+               case COMPILE_TIME_CRC32_STR("alpha-down"):
+                       stack[0].__raw = bzInputBtn(8) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("alpha-left"):
+                       stack[0].__raw = bzInputBtn(9) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("alpha-right"):
+                       stack[0].__raw = bzInputBtn(10) ? 1 : 0;
+                       break;
+
+               default:
+                       // Warn about button??
+                       stack[0].__raw = 0;
+                       break;
+       }
+}
+
+static void native_btnp(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       switch (stack[1].pointerLiteral) {
+               case COMPILE_TIME_CRC32_STR("up"):
+                       stack[0].__raw = bzInputBtnP(1) ? 1 : 0;
+                       break;
+               
+               case COMPILE_TIME_CRC32_STR("down"):
+                       stack[0].__raw = bzInputBtnP(2) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("left"):
+                       stack[0].__raw = bzInputBtnP(3) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("right"):
+                       stack[0].__raw = bzInputBtnP(4) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("action1"):
+                       stack[0].__raw = bzInputBtnP(5) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("action2"):
+                       stack[0].__raw = bzInputBtnP(6) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("alpha-up"):
+                       stack[0].__raw = bzInputBtnP(7) ? 1 : 0;
+                       break;
+               
+               case COMPILE_TIME_CRC32_STR("alpha-down"):
+                       stack[0].__raw = bzInputBtnP(8) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("alpha-left"):
+                       stack[0].__raw = bzInputBtnP(9) ? 1 : 0;
+                       break;
+
+               case COMPILE_TIME_CRC32_STR("alpha-right"):
+                       stack[0].__raw = bzInputBtnP(10) ? 1 : 0;
+                       break;
+
+               default:
+                       // Warn about button??
+                       stack[0].__raw = 0;
+                       break;
+       }
+}
+
+static void native_depth(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       _CONTEXT_PARAMETER(sortIdx) = FIXED_TO_INT(stack[1].integerLiteral);
+}
+
+static void native_layer(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       _CONTEXT_PARAMETER(layer) = stack[1].pointerLiteral;
+       _CONTEXT_PARAMETER(drawQueue) = bzGameGetSceneLayerDrawQueue(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer));
+}
+
+static void native_resetMatrix(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       resetTransform(metadata);
+       //bzMatrixIdentity(&_CONTEXT_PARAMETER(matrix));
+}
+
+static void native_translate(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       _CONTEXT_PARAMETER(position).x += _FLOAT_PARAMETER(1);
+       _CONTEXT_PARAMETER(position).y += _FLOAT_PARAMETER(2);
+       calculateTransformMatrix(metadata);
+
+//     BZMatrix m;
+//     bzMatrixTranslation(&m, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2));
+//     bzMatrixMultiply(&_CONTEXT_PARAMETER(matrix), &_CONTEXT_PARAMETER(matrix), &m);
+}
+
+static void native_rotate(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       _CONTEXT_PARAMETER(angle) += _FLOAT_PARAMETER(1);
+       calculateTransformMatrix(metadata);
+//     BZMatrix m;
+//     bzMatrixRotation(&m, _FLOAT_PARAMETER(1));
+//     bzMatrixMultiply(&_CONTEXT_PARAMETER(matrix), &_CONTEXT_PARAMETER(matrix), &m);
+}
+
+static void native_scale(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       _CONTEXT_PARAMETER(scale).x *= _FLOAT_PARAMETER(1);
+       if (parameterCount == 1) {
+               _CONTEXT_PARAMETER(scale).y *= _FLOAT_PARAMETER(1);
+       } else {
+               _CONTEXT_PARAMETER(scale).y *= _FLOAT_PARAMETER(2);
+       }
+       calculateTransformMatrix(metadata);
+       
+       //BZMatrix m;
+       //if (parameterCount == 1) {
+       //      bzMatrixScale(&m, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(1));
+       //} else {
+       //      bzMatrixScale(&m, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2));
+       //}
+       //bzMatrixMultiply(&_CONTEXT_PARAMETER(matrix), &_CONTEXT_PARAMETER(matrix), &m);
+}
+
+static void native_event(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       
+       if (parameterCount == 1) {
+               bzGameScenePostEvent(_CONTEXT_PARAMETER(scene), _RAW_PARAMETER(1), NULL);
+       } else if (parameterCount >= 2) {
+               BZSceneEventData data;
+               data.integerValue = _RAW_PARAMETER(2);
+               bzGameScenePostEvent(_CONTEXT_PARAMETER(scene), _RAW_PARAMETER(1), &data);
+       }
+}
+
+static void native_eventCheck(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZSceneEventData data;
+       bool found = bzGameSceneQueryEvent(&data, _CONTEXT_PARAMETER(scene), _RAW_PARAMETER(1));
+       if (found && parameterCount >= 2) {
+               found = (data.integerValue == _RAW_PARAMETER(2));
+       }
+       stack[0].__raw = found ? 1 : 0;
+}
+
+/*static void native_agent(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZAgentSimulationID simulation = bzGameGetAgentSimulation(metadata->scene);
+
+       BZAgentDetails details = {
+               .angle = metadata->angle,
+               .mass = bzAgentDefaultMass,
+               .maxForce = bzAgentDefaultMaxForce,
+               .maxSpeed = bzAgentDefaultMaxSpeed,
+               .position = metadata->position,
+       };
+
+       if (parameterCount >= 1) {
+               details.position.x = _FLOAT_PARAMETER(1);
+       }
+       if (parameterCount >= 2) {
+               details.position.y = _FLOAT_PARAMETER(2);
+       }
+       if (parameterCount >= 3) {
+               details.angle = _FLOAT_PARAMETER(3);
+       }
+       if (parameterCount >= 4) {
+               details.mass = _FLOAT_PARAMETER(4);
+       }
+       if (parameterCount >= 5) {
+               details.maxForce = _FLOAT_PARAMETER(5);
+       }
+       if (parameterCount >= 6) {
+               details.maxSpeed = _FLOAT_PARAMETER(6);
+       }
+
+       metadata->agent = bzFXAgentSimulationAddAgent(simulation, &details);
+}
+
+static void native_agentSync(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZAgentSimulationID simulation = bzGameGetAgentSimulation(metadata->scene);
+
+       BZAgentDetails details;
+       bzFXAgentSimulationGetAgentDetails(&details, simulation, metadata->agent);
+
+       metadata->angle = details.angle;
+       metadata->position = details.position;
+
+       calculateTransformMatrix(metadata);
+}*/
+
+static void native_scene(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       BZSceneEventData data = {
+               .stringValue = _STRING_PARAMETER(1),
+       };
+       bzGameScenePostEvent(_CONTEXT_PARAMETER(scene), bzSceneChangeEventIdentifier, &data);
+
+//     BZActorID actor = bzGameSceneAddActor(_CONTEXT_PARAMETER(scene), "%s", svmGetString(instance, stack[1].pointerLiteral));
+//     for (size_t i = 2, j = 1; i <= parameterCount; ++i, ++j) {
+//             actor->parameters[j] = stack[i].__raw;
+//     }
+}
+
+static void native_id(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) {
+       BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance);
+       stack[0].__raw = metadata->uuid;
+}
+
+SVMFunctionCallback bzScriptingBindingsLookupNativeFunction(uint32_t nameCRC, void *userParam) {
+       switch (nameCRC) {
+               FUNCTION_BIND(log)
+               FUNCTION_BIND(logf)
+
+               FUNCTION_BIND(btn)
+               FUNCTION_BIND(btnp)
+
+               FUNCTION_BIND(min)
+               FUNCTION_BIND(max)
+               FUNCTION_BIND(mid)
+               FUNCTION_BIND(sgn)
+               FUNCTION_BIND(abs)
+               FUNCTION_BIND(floor)
+               FUNCTION_BIND(ceil)
+               FUNCTION_BIND(adelta)
+               FUNCTION_BIND(rnd)
+               FUNCTION_BIND(lerp)
+               FUNCTION_BIND(dot)
+
+               FUNCTION_BIND(time)
+
+               FUNCTION_BIND(sqrt)
+               FUNCTION_BIND(sin)
+               FUNCTION_BIND(cos)
+               FUNCTION_BIND(atan2)
+
+               FUNCTION_BIND(distance)
+               FUNCTION_BIND(distanceCheck)
+
+               FUNCTION_BIND(layer)
+               FUNCTION_BIND(depth)
+       //      FUNCTION_BIND(pset)
+       //      FUNCTION_BIND(sget)
+               FUNCTION_BIND(cls)
+               FUNCTION_BIND(camera)
+               FUNCTION_BIND(circ)
+               FUNCTION_BIND(circfill)
+               FUNCTION_BIND(line)
+               FUNCTION_BIND(rect)
+               FUNCTION_BIND(rectfill)
+               FUNCTION_BIND(triangle)
+               FUNCTION_BIND(spr)
+               FUNCTION_BIND(sspr)
+               FUNCTION_BIND(print)
+               FUNCTION_BIND(printf)
+               FUNCTION_BIND(fillp)
+               FUNCTION_BIND(palette)
+               FUNCTION_BIND(output)
+               FUNCTION_BIND(map)
+               FUNCTION_BIND(mget)
+               FUNCTION_BIND(mset)
+               FUNCTION_BIND(mfget)
+
+               FUNCTION_BIND(resetMatrix)
+               FUNCTION_BIND(translate)
+               FUNCTION_BIND(rotate)
+               FUNCTION_BIND(scale)
+
+               FUNCTION_BIND(particle)
+
+               FUNCTION_BIND(sound)
+               FUNCTION_BIND(speech)
+               FUNCTION_BIND(speechf)
+
+               FUNCTION_BIND(collision)
+               FUNCTION_BIND(collisionCheck)
+
+               FUNCTION_BIND(actor)
+               FUNCTION_BIND(find)
+               FUNCTION_BIND(parameter)
+               FUNCTION_BIND(public)
+
+               FUNCTION_BIND(event)
+               FUNCTION_BIND(eventCheck)
+
+//             FUNCTION_BIND(agent)
+//             FUNCTION_BIND(agentSync)
+
+               FUNCTION_BIND(scene)
+
+               FUNCTION_BIND(id)
+
+               default:
+                       bzLog("Warning: Could not find function %d", nameCRC);
+                       return NULL;
+       }
+}
diff --git a/src/bz/scripting/bindings.h b/src/bz/scripting/bindings.h
new file mode 100644 (file)
index 0000000..59fbafb
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef BZ_SCRIPTING_BINDINGS_H
+#define BZ_SCRIPTING_BINDINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/bindings_internal.h b/src/bz/scripting/bindings_internal.h
new file mode 100644 (file)
index 0000000..1165d0c
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BZ_SCRIPTING_BINDINGS_INTERNAL_H
+#define BZ_SCRIPTING_BINDINGS_INTERNAL_H
+
+#include <bz/scripting/bindings.h>
+
+#include <sun/vm/vm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern SVMFunctionCallback bzScriptingBindingsLookupNativeFunction(uint32_t nameCRC, void *userParam);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/environment.c b/src/bz/scripting/environment.c
new file mode 100644 (file)
index 0000000..53ebaa9
--- /dev/null
@@ -0,0 +1,48 @@
+#include <bz/scripting/environment_internal.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/scripting/bindings_internal.h>
+#include <bz/scripting/script.h>
+
+/*BZScriptingEnvironmentID bzScriptingMakeEnvironment(BZMemoryArenaID arena, void *lookupUserParam, const char *identifierFmt, ...) {
+       BZScriptingEnvironment *environment = bzMemoryAlloc(arena, sizeof(BZScriptingEnvironment));
+       environment->arena = arena;
+       bzInsertIdentifier(environment->identifier, identifierFmt);
+
+       SVMVMConfiguration config = {
+               .malloc = malloc,
+               .free = free, // fixme
+               .lookup = bzScriptingBindingsLookupNativeFunction,
+               .lookupUserParam = lookupUserParam,
+       };
+
+       environment->vm = svmCreateVM(&config);
+
+       return environment;
+}
+
+bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash) {
+       for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) {
+               if (environment->publics[i].identifierHash == identifierHash) {
+                       if (out != NULL) {
+                               out->__raw = environment->publics[i].value.__raw;
+                       }
+                       return true;
+               }
+       }
+       return false;
+}
+
+void bzScriptingSetEnvironmentPublic(BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash, SVMOperand value) {
+       for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) {
+               if (environment->publics[i].identifierHash == 0) {
+                       environment->publics[i].identifierHash = identifierHash;
+               }
+               if (environment->publics[i].identifierHash == identifierHash) {
+                       environment->publics[i].value.__raw = value.__raw;
+                       return;
+               } 
+       }
+       bzError("Out of globals!!");
+}*/
diff --git a/src/bz/scripting/environment.h b/src/bz/scripting/environment.h
new file mode 100644 (file)
index 0000000..1821363
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef BZ_SCRIPTING_ENVIRONMENT_H
+#define BZ_SCRIPTING_ENVIRONMENT_H
+
+#include <bz/memory/arena.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef struct BZScriptingEnvironment BZScriptingEnvironment;
+//typedef BZScriptingEnvironment * BZScriptingEnvironmentID;
+
+//extern BZScriptingEnvironmentID bzScriptingMakeEnvironment(BZMemoryArenaID arena, void *lookupUserParam, const char *identifierFmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/environment_internal.h b/src/bz/scripting/environment_internal.h
new file mode 100644 (file)
index 0000000..3d90b98
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef BZ_SCRIPTING_ENVIRONMENT_INTERNAL_H
+#define BZ_SCRIPTING_ENVIRONMENT_INTERNAL_H
+
+#include <bz/scripting/environment.h>
+
+#include <bz/scripting/script_internal.h>
+#include <bz/types/common.h>
+#include <bz/types/identifier.h>
+#include <sun/compiler/compiler.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*#define kBZScriptingEnvironmentScriptCount 256
+#define kBZScriptingEnvironmentScriptInstanceCount 256
+#define kBZScriptingEnvironmentPublicCount 256
+
+struct BZScriptingEnvironmentPublic {
+       BZIdentifierHash identifierHash;
+       SVMOperand value;
+};
+typedef struct BZScriptingEnvironmentPublic BZScriptingEnvironmentPublic;
+
+struct BZScriptingEnvironment {
+       BZMemoryArenaID arena;
+       char identifier[kBZMaxIdentifierLength];
+       SVMVM *vm;
+       BZScript scripts[kBZScriptingEnvironmentScriptCount];
+       BZScriptInstance instances[kBZScriptingEnvironmentScriptInstanceCount];
+       BZScriptingEnvironmentPublic publics[kBZScriptingEnvironmentPublicCount];
+};
+
+extern bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash);
+extern void bzScriptingSetEnvironmentPublic(BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash, SVMOperand value);*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/script.c b/src/bz/scripting/script.c
new file mode 100644 (file)
index 0000000..efff766
--- /dev/null
@@ -0,0 +1,134 @@
+#include <bz/scripting/script_internal.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+//#include <bz/scripting/bindings_internal.h>
+//#include <bz/scripting/environment_internal.h>
+#include <bz/types/identifier_internal.h>
+#include <sun/compiler/compiler.h>
+#include <string.h>
+
+#define kMaxIncludeDepth 8
+
+struct BZScriptReadContext {
+       size_t currentHandleIdx;
+       BZResourceID handles[kMaxIncludeDepth];
+       bool handlePreprocessor;
+       bool pastFirstCharacter;
+};
+typedef struct BZScriptReadContext BZScriptReadContext;
+
+static size_t readSource(char *out, size_t outSize, void *userParam) {
+       BZScriptReadContext *context = userParam;
+
+       BZResourceID currentHandle = context->handles[context->currentHandleIdx];
+       size_t readStartPosition = bzResourcesTell(currentHandle);
+
+process:
+       if (context->handlePreprocessor) {
+               // Extract the current line...
+
+               size_t preprocessorLength = 0;
+               while (bzResourcesReadBytes(currentHandle, out, 1) > 0) {
+                       if (*out == '\n') {
+                               break;
+                       }
+                       preprocessorLength += 1;
+               }
+
+               char *preprocessorInstruction = alloca(preprocessorLength + 1);
+               preprocessorInstruction[preprocessorLength] = '\0';
+               bzResourcesSeek(currentHandle, readStartPosition);
+               bzResourcesReadBytes(currentHandle, preprocessorInstruction, preprocessorLength);
+
+               if (strncmp(preprocessorInstruction, "#include ", 9) == 0) {
+                       context->currentHandleIdx++;
+                       bzAssert(context->currentHandleIdx < kMaxIncludeDepth);
+                       preprocessorInstruction += 9;
+                       context->handles[context->currentHandleIdx] = bzResourcesOpenResource("script", "assets/scripts/%s", preprocessorInstruction);
+                       bzAssertMessage(context->handles[context->currentHandleIdx] != NULL, "Invalid include '%s'", preprocessorInstruction);
+               }
+       }
+
+       size_t readSize = 0;
+       while (readSize == 0) {
+               currentHandle = context->handles[context->currentHandleIdx];
+               readStartPosition = bzResourcesTell(currentHandle);
+               readSize = bzResourcesReadBytes(currentHandle, out, outSize);
+
+               if (readSize == 0) {
+                       if (context->currentHandleIdx > 0) {
+                               bzResourcesCloseResource(currentHandle);
+                               --context->currentHandleIdx;
+                       } else {
+                               // 0th level is closed in main code
+                               break;
+                       }
+               }
+       }
+
+       for (size_t i = 0; i < readSize; ++i) {
+               switch (out[i]) {
+                       case '#':
+                               if (context->pastFirstCharacter == false) {
+                                       // need to handle preprocessor, set flag and return only the read characters
+                                       bzResourcesSeek(currentHandle, readStartPosition + i);
+                                       context->handlePreprocessor = true;
+                                       
+                                       if (i > 0) {
+                                               return i; // don't return the '#' character
+                                       } else {
+                                               goto process; // I know this is horrible, don't @ me.
+                                       }
+                               }
+                               break;
+                       
+                       case '\n':
+                               context->pastFirstCharacter = false;
+                               break;
+
+                       case ' ':
+                       case '\t':
+                               // These still count as 'first character' techically...
+                               break;
+
+                       default:
+                               context->pastFirstCharacter = true;
+                               break;
+               }
+       }
+
+       return readSize;
+}
+
+static void *sourceMalloc(void *memory, size_t size) {
+       return alloca(size);
+}
+
+SVMModule *bzScriptingLoadScriptModule(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZScriptReadContext context = {
+               .handles = { bzResourcesOpenResource("script", "assets/scripts/%s.sun", identifier), },
+               .currentHandleIdx = 0,
+               .handlePreprocessor = false,
+               .pastFirstCharacter = false,
+       };
+       bzAssertMessage(context.handles[0] != NULL, "Invalid script '%s'", identifier);
+
+       SLCCompileConfiguration config = {
+               .sourceReadCallback = readSource,
+               .sourceUserParam = &context,
+               .realloc = sourceMalloc, // fixme
+               .free = NULL, // fixme
+       };
+
+       SVMModule *module = slcCompileSource(&config);
+       bzResourcesCloseResource(context.handles[0]);
+
+       if (module == NULL) {
+               bzError("Could not load script %s", identifier);
+       }
+
+       return module;
+}
diff --git a/src/bz/scripting/script.h b/src/bz/scripting/script.h
new file mode 100644 (file)
index 0000000..4e52d04
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef BZ_SCRIPTING_SCRIPT_H
+#define BZ_SCRIPTING_SCRIPT_H
+
+#include <bz/memory/arena.h>
+//#include <bz/scripting/environment.h>
+#include <sun/vm/vm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef struct BZScript BZScript;
+//typedef BZScript * BZScriptID;
+
+//typedef struct BZScriptInstance BZScriptInstance;
+//typedef BZScriptInstance * BZScriptInstanceID;
+
+extern SVMModule *bzScriptingLoadScriptModule(BZMemoryArenaID arena, const char *identifierFmt, ...);
+
+
+
+//extern BZScriptInstanceID bzScriptingNewInstance(BZScriptID script, void *userParam, const char *identifierFmt, ...);
+
+//extern bool bzScriptingUpdateInstance(BZScriptInstanceID instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/script_internal.h b/src/bz/scripting/script_internal.h
new file mode 100644 (file)
index 0000000..d3f3c5c
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef BZ_SCRIPTING_SCRIPT_INTERNAL_H
+#define BZ_SCRIPTING_SCRIPT_INTERNAL_H
+
+#include <bz/scripting/script.h>
+
+//#include <sun/compiler/compiler.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*struct BZScriptInstance {
+       BZScriptingEnvironmentID environment;
+       //BZScriptState state;
+       SVMModuleInstance *instance;
+};
+
+struct BZScript {
+       BZScriptingEnvironmentID environment;
+       SVMModule *module;
+};*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/scripting/static_crc32.hpp b/src/bz/scripting/static_crc32.hpp
new file mode 100644 (file)
index 0000000..18703c4
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef BZ_SCRIPTING_STATIC_CRC32_HPP
+#define BZ_SCRIPTING_STATIC_CRC32_HPP
+
+#include <stdint.h>
+
+#ifndef __cplusplus
+       #error "C++ Only"
+#endif
+
+// CRC stuff taken from: https://stackoverflow.com/a/23683218
+
+static constexpr uint32_t crc_table[256] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+       0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+       0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+       0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+       0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+       0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+       0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+       0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+       0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+       0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+       0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+       0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+       0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+       0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+       0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+       0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+       0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+       0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+       0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+       0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+       0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+       0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+
+template <int size, int idx = 0, class dummy = void>
+struct MM {
+       static constexpr uint32_t crc32(const char * str, uint32_t prev_crc = 0xFFFFFFFF) {
+               return MM<size, idx+1>::crc32(str, (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF] );
+       }
+};
+
+template <int size, class dummy>
+struct MM <size, size, dummy> {
+       static constexpr uint32_t crc32(const char * str, uint32_t prev_crc = 0xFFFFFFFF) {
+               return prev_crc ^ 0xFFFFFFFF;
+       }
+};
+
+#define COMPILE_TIME_CRC32_STR(x) ((MM<sizeof(x) - 1>::crc32(x)))
+
+#endif
diff --git a/src/bz/types/common.h b/src/bz/types/common.h
new file mode 100644 (file)
index 0000000..d02a5fe
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BZ_TYPES_COMMON_H
+#define BZ_TYPES_COMMON_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/types/identifier.c b/src/bz/types/identifier.c
new file mode 100644 (file)
index 0000000..537a4e2
--- /dev/null
@@ -0,0 +1,9 @@
+#include <bz/types/identifier_internal.h>
+
+#include <crc.h>
+#include <string.h>
+
+BZIdentifierHash bzIdentifierHashFromIdentifier(BZIdentifier identifier) {
+       BZIdentifierHash hash = crc32buf(identifier, strlen(identifier));
+       return hash;
+}
diff --git a/src/bz/types/identifier.h b/src/bz/types/identifier.h
new file mode 100644 (file)
index 0000000..a7556d7
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef BZ_TYPES_IDENTIFIER_H
+#define BZ_TYPES_IDENTIFIER_H
+
+#include <bz/types/common.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define kBZMaxIdentifierLength 256
+
+typedef const char * BZIdentifier;
+typedef uint32_t BZIdentifierHash;
+
+extern BZIdentifierHash bzIdentifierHashFromIdentifier(BZIdentifier string);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/types/identifier_internal.h b/src/bz/types/identifier_internal.h
new file mode 100644 (file)
index 0000000..4b9bfb0
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef BZ_TYPES_IDENTIFIER_INTERNAL_H
+#define BZ_TYPES_IDENTIFIER_INTERNAL_H
+
+#include <bz/types/identifier.h>
+
+#include <stb_sprintf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define bzInsertIdentifier(dst, fmt) \
+       { \
+               va_list args; \
+               va_start(args, fmt); \
+               stbsp_vsnprintf(dst, kBZMaxIdentifierLength, fmt, args); \
+               va_end(args); \
+       }
+
+#define bzMakeIdentifier(out, fmt) \
+       static char out[kBZMaxIdentifierLength]; \
+       bzInsertIdentifier(out, fmt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/types/point.h b/src/bz/types/point.h
new file mode 100644 (file)
index 0000000..1763a91
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef BZ_TYPES_POINT_H
+#define BZ_TYPES_POINT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZPoint {
+       float x;
+       float y;
+};
+typedef struct BZPoint BZPoint;
+
+static inline BZPoint bzPointMake(float x, float y) {
+       return (BZPoint){ .x = x, .y = y };
+}
+
+//#define bzPointMake(x, y) ((BZPoint){ .x = x, .y = y })
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/types/rect.h b/src/bz/types/rect.h
new file mode 100644 (file)
index 0000000..3d53844
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef BZ_TYPES_RECT_H
+#define BZ_TYPES_RECT_H
+
+#include <bz/types/point.h>
+#include <bz/types/size.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZRect {
+       BZPoint origin;
+       BZSize size;
+};
+typedef struct BZRect BZRect;
+
+static inline BZRect bzRectMake(float x, float y, float w, float h) {
+       return (BZRect){ .origin = bzPointMake(x, y), .size = bzSizeMake(w, h) };
+}
+
+static inline float bzRectMinX(BZRect rect) {
+       return rect.origin.x - rect.size.width / 2.0f;
+}
+
+static inline float bzRectMidX(BZRect rect) {
+       return rect.origin.x;
+}
+
+static inline float bzRectMaxX(BZRect rect) {
+       return rect.origin.x + rect.size.width / 2.0f;
+}
+
+static inline float bzRectMinY(BZRect rect) {
+       return rect.origin.y - rect.size.height / 2.0f;
+}
+
+static inline float bzRectMidY(BZRect rect) {
+       return rect.origin.y;
+}
+
+static inline float bzRectMaxY(BZRect rect) {
+       return rect.origin.y + rect.size.height / 2.0f;
+}
+
+//#define bzRectMake(x, y, w, h) ((BZRect){ .origin = {.x = (x), .y = (y)}, .size = {.width = (w), .height = (h)} })
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/types/size.h b/src/bz/types/size.h
new file mode 100644 (file)
index 0000000..d7e3fb9
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef BZ_TYPES_SIZE_H
+#define BZ_TYPES_SIZE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BZSize {
+       float width;
+       float height;
+};
+typedef struct BZSize BZSize;
+
+static inline BZSize bzSizeMake(float w, float h) {
+       return (BZSize){ .width = w, .height = h };
+}
+
+//#define bzSizeMake(w, h) ((BZSize){ .width = w, .height = h })
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bz/types/user_parameter.h b/src/bz/types/user_parameter.h
new file mode 100644 (file)
index 0000000..abe5e80
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef BZ_TYPES_USER_PARAMETER_H
+#define BZ_TYPES_USER_PARAMETER_H
+
+#include <bz/types/common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*enum BZUserParameterType {
+       BZUserParameterTypeNone,
+
+       BZUserParameterTypeBoolean,
+       BZUserParameterTypeInteger,
+       BZUserParameterTypeFloat,
+       BZUserParameterTypePointer,
+};
+typedef enum BZUserParameterType BZUserParameterType;
+
+struct BZUserParameter {
+       BZUserParameterType type;
+       union {
+               bool booleanValue;
+               uint32_t integerValue;
+               float floatValue;
+               void *pointerValue;
+       };
+};
+typedef struct BZUserParameter BZUserParameter;
+
+#define bzEmptyUserParameter() ((BZUserParameter) { .type = BZUserParameterTypeNone })
+#define bzBooleanUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypeBoolean, .booleanValue = v })
+#define bzIntegerUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypeInteger, .integerValue = v })
+#define bzFloatUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypeFloat, .floatValue = v })
+#define bzPointerUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypePointer, .pointerValue = v })*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src_platform/playdate/bz/debug/assert.c b/src_platform/playdate/bz/debug/assert.c
new file mode 100644 (file)
index 0000000..e46aa23
--- /dev/null
@@ -0,0 +1,7 @@
+#include <bz/debug/assert.h>
+
+#include <stdlib.h>
+
+void bzAssertExit() {
+       //exit(1);
+}
diff --git a/src_platform/playdate/bz/debug/log.c b/src_platform/playdate/bz/debug/log.c
new file mode 100644 (file)
index 0000000..487fe11
--- /dev/null
@@ -0,0 +1,29 @@
+#include <bz/debug/log_internal.h>
+
+#include <playdate/entrypoint.h>
+#include <stb_sprintf.h>
+
+#define kSprintfBufferLength 1024
+static char buffer[kSprintfBufferLength];
+
+void _bzLog(const char *filename, unsigned long lineNumber, const char *fmt, ...) {
+       va_list args;
+       va_start(args, fmt);
+       stbsp_vsnprintf(buffer, kSprintfBufferLength, fmt, args);
+       va_end(args);
+       playdate->system->logToConsole("LOG: %s\n", buffer);
+}
+
+void _bzError(const char *filename, unsigned long lineNumber, const char *fmt, ...) {
+       va_list args;
+       va_start(args, fmt);
+       stbsp_vsnprintf(buffer, kSprintfBufferLength, fmt, args);
+       va_end(args);
+       playdate->system->error("LOG: %s\n", buffer);
+}
+
+void bzLogInit(const char *outputFilePath) {
+}
+
+void bzLogTeardown() {
+}
diff --git a/src_platform/playdate/bz/debug/perfgraph.c b/src_platform/playdate/bz/debug/perfgraph.c
new file mode 100644 (file)
index 0000000..e97680a
--- /dev/null
@@ -0,0 +1,9 @@
+#include <playdate/entrypoint.h>
+
+void resetSystemTimer(void) {
+       playdate->system->resetElapsedTime();
+}
+
+float getSystemTimer(void) {
+       return playdate->system->getElapsedTime();
+}
diff --git a/src_platform/playdate/bz/gfx/gfx_platform.c b/src_platform/playdate/bz/gfx/gfx_platform.c
new file mode 100644 (file)
index 0000000..e89e392
--- /dev/null
@@ -0,0 +1,852 @@
+#include <bz/gfx/gfx_platform.h>
+
+#include <bz/gfx/gfx_internal.h>
+#include <bz/math/math.h>
+#include <bz/renderer/render_pass_internal.h>
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight);
+
+static BZRenderPass renderPass = {
+       .softwareHook = gfxRender,
+};
+BZRenderPassID bzGFXRenderPass = &renderPass;
+
+
+
+
+
+
+#define bzGeneratePattern(p11, p12, p13, p14, p21, p22, p23, p24, p31, p32, p33, p34, p41, p42, p43, p44) { {p11, p12, p13, p14}, {p21, p22, p23, p24}, {p31, p32, p33, p34}, {p41, p42, p43, p44} }
+
+//(r&0xFF) << 0 | (g&0xFF) << 8 | (b&0xFF) << 16 | (0xFF) << 24
+
+#define pattern0 0b0000000000000000
+#define pattern1 0b1000000101000010
+#define pattern2 0b0000000000000000
+#define pattern3 0b0101111101011111
+#define pattern4 0b0000110000000000
+#define pattern5 0b0101101001011010
+#define pattern6 0b1111111111111111
+#define pattern7 0b0000000000000000
+
+uint16_t patterns[] = {
+       pattern0,
+       pattern0,
+       pattern3,
+       pattern1,
+       pattern5,
+       pattern5,
+       pattern3,
+       pattern6,
+       pattern2,
+       pattern5,
+       pattern2,
+       pattern2,
+       pattern2,
+       pattern5,
+       pattern2,
+       pattern2,
+       pattern1,
+
+       
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1,
+       pattern1
+};
+
+
+
+#define GFX_WIDTH 200
+#define GFX_HEIGHT 120
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight) {
+       bzGfxComposite();
+
+//extern uint8_t *bzGfxCompositedBuffer;
+
+
+       bzPerfTimerStart("game.blit.transfer");
+
+       uint32_t *pixels = output;
+       
+
+       //int idx = 0;
+       uint32_t value = 0;
+       for (size_t y = 0; y < GFX_HEIGHT * pixelHeight; ++y) {
+               //idx = y * 13;
+               int patY = y % 4;
+               size_t patYShift = 12-patY*4;
+               //int patX = 0;
+               uint8_t ly = y >> 1;
+               uint8_t lx = 0;
+               uint8_t *bufferLine = &bzGfxCompositedBuffer[(y >> 1) << bufferStrideShift]; // FIXME, bitshift
+               for (size_t x = 0; x < GFX_WIDTH << 1; ) {
+                       //value = 0;
+                       //int target = bzMin(GFX_WIDTH * pixelWidth - x, 32); // FIXME, slow
+                       for (int i = 0; i < 32; ++i, ++x) {
+                               uint8_t c = bufferLine[lx];
+                               //uint8_t patternIdx = patternLookup[c];
+                               //bzAssertMessage(patternIdx <= 7, "Pattern index %d at %d,%d %d,%d", patternIdx, x, y, x / pixelWidth, y / pixelHeight);
+                               uint8_t bit = (patterns[c] >> patYShift >> (3-(x & 0b11)))&1;
+                               
+                               //[patternIdx][patY][x & 0b11 /*% 4*/];
+                               value = (value << 1) | bit ;// | patterns[patternIdx][patY][x % 4]; //(patterns[patternIdx][patY][x % 4] * 0b10000000000000000000000000000000);//x % 4 -> patX FIXME
+                               lx += x&1;
+                       }
+                       __asm volatile ("rev %0, %1" : "=l" (*pixels) : "l" (value));
+                       pixels++;
+               }
+       }
+
+       bzPerfTimerStop("game.blit.transfer");
+}
+
+
+
+
+
+
+
+
+#if 0
+
+void bzGfxComposite(uint8_t *outputBuffer) {
+       bzPerfTimerStart("game.blit.composite");
+
+       size_t canvasMemorySize = bufferStride * height * sizeof(uint8_t);
+       
+       uint8_t *drawBufferReadHead = drawBuffer;
+       uint8_t *paletteBufferReadHead = paletteBuffer;
+       uint8_t *maskBufferReadHead = maskBuffer;
+       uint8_t *overlayBufferReadHead = overlayBuffer;
+       uint8_t *outputBufferWriteHead = outputBuffer;
+       for (size_t i = 0; i < bufferStride * height; ++i) {
+               *outputBufferWriteHead = palettes[*paletteBufferReadHead + 1][*drawBufferReadHead];
+
+               ++drawBufferReadHead;
+               ++paletteBufferReadHead;
+               ++maskBufferReadHead;
+               ++overlayBufferReadHead;
+               ++outputBufferWriteHead;
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// The real memory seems stupid slow??
+
+//#define kScreenMem 3120
+//static uint32_t *fakeScreen = NULL;//[3120];
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight) {
+//     if (fakeScreen == NULL) {
+//             fakeScreen = malloc(kScreenMem * sizeof(uint32_t));
+//     }
+
+       // FIXME, this one is very very slow.
+
+       bzPerfTimerStart("game.blit.composite");
+
+       uint8_t *p = &buffer[0][0][0];
+
+       for (size_t y = 0; y < canvasHeight; ++y) {
+               for (size_t x = 0; x < canvasWidth; ++x) {
+//                     bzAssertMessage(buffer[1][y][x] == 1, "Got: %d", buffer[1][y][x]);
+
+                       p[7] = palettes[p[1] + 1][p[0]];
+                       p+=8*sizeof(uint8_t);
+
+//                     uint8_t cIdx = palettes[buffer[y][x][1] + 1][buffer[y][x][0]]; ////////// (palettes[buffer[y][x][1] + 1][buffer[y][x][0]] & buffer[y][x][2]); //  PALETTE, return this    (buffer[1][y][x]) + 1
+//                     if (buffer[y][x][2] == 0) cIdx = 0;
+//                     if (buffer[y][x][3] != 0) cIdx = buffer[y][x][3];
+//                     bzBufferSet(7, x, y, cIdx);
+
+//                     if (buffer[2][y][x] == 0) {
+//                             cIdx = 0;
+//                     }
+//                     if (buffer[3][y][x] > 0) {
+//                             cIdx = buffer[3][y][x];
+//                     }
+//                     intermediateBuffer[y][x] = cIdx;
+                       //intermediateBuffer[y][x] = paletteColors[cIdx];
+               }
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+
+       bzPerfTimerStart("game.blit.transfer");
+
+//     uint32_t *pixels = output;
+       uint32_t *pixels = (uint32_t *)output;//(uint32_t *)output;
+       //int idx = 0;
+       uint32_t value = 0;
+       for (size_t y = 0; y < canvasHeight * pixelHeight; ++y) {
+               //idx = y * 13;
+               int patY = y % 4;
+               //int patX = 0;
+               uint8_t ly = y >> 1;
+               uint8_t lx = 0;
+               for (size_t x = 0; x < canvasWidth * pixelWidth; ) {
+                       //value = 0;
+                       int target = bzMin(canvasWidth * pixelWidth - x, 32);
+                       for (int i = 0; i < target; ++i, ++x) {
+                               uint8_t c = buffer[ly /*/ pixelHeight*/][lx /*/ pixelWidth*/][7];
+                               uint8_t patternIdx = patternLookup[c];
+                               //bzAssertMessage(patternIdx <= 7, "Pattern index %d at %d,%d %d,%d", patternIdx, x, y, x / pixelWidth, y / pixelHeight);
+                               uint8_t bit = patterns[patternIdx][patY][x & 0b11 /*% 4*/];
+                               value = (value << 1) | bit ;// | patterns[patternIdx][patY][x % 4]; //(patterns[patternIdx][patY][x % 4] * 0b10000000000000000000000000000000);//x % 4 -> patX FIXME
+                               lx += x&1;
+                       }
+//                     *pixels = swap(value);
+                       __asm volatile ("rev %0, %1" : "=l" (*pixels) : "l" (value));
+                       pixels++;// += 4;//sizeof(uint32_t);
+               }
+               //pixels += 2; // stride...
+       }
+
+       bzPerfTimerStop("game.blit.transfer");
+
+//     memcpy(output, fakeScreen, kScreenMem * sizeof(uint32_t));
+}
+
+
+
+
+
+
+#include <bz/gfx/gfx_internal.h>
+
+#include <bz/gfx/font_internal.h>
+#include <bz/math/math.h>
+#include <bz/renderer/render_pass_internal.h>
+
+#include <stdlib.h>
+
+// http://members.chello.at/~easyfilter/Bresenham.pdf
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight);
+
+static BZRenderPass renderPass = {
+       .softwareHook = gfxRender,
+};
+BZRenderPassID bzGFXRenderPass = &renderPass;
+
+#define GFX_WIDTH 200 //256 // 128 //320
+#define GFX_HEIGHT 120 //128 // 128 //180
+
+static int currentBuffer = 0;
+static uint8_t buffer[GFX_HEIGHT][GFX_WIDTH][8];
+static int cameraX = 0;
+static int cameraY = 0;
+static bool fillPattern[4][4];
+
+#define _bzBufferSet(idx, x, y, value) buffer[(y)][(x)][(idx)] = (value)
+#define _bzBufferSetSafe(idx, x, y, value) { bzAssert(idx>= 0 && idx<8);bzAssertMessage((x)>= 0 && (x)<GFX_WIDTH, "invalid x: %d   %s, %d", x, __FILE__, __LINE__);bzAssertMessage((y)>= 0 && (y)<GFX_HEIGHT, "invalid y: %d   %s, %d", y, __FILE__, __LINE__);_bzBufferSet(idx, x, y, value); }
+
+#define bzBufferSet _bzBufferSet
+
+//uint32_t intermediateBuffer[GFX_HEIGHT][GFX_WIDTH];
+
+#define SS_WIDTH 128
+#define SS_HEIGHT 128
+
+static uint8_t spritesheet[SS_HEIGHT][SS_WIDTH];
+
+static BZFont *currentFont;
+
+#define bzGenerateColor(r, g, b) (r&0xFF) << 0 | (g&0xFF) << 8 | (b&0xFF) << 16 | (0xFF) << 24
+uint32_t paletteColors[] = {
+       bzGenerateColor(0, 0, 0),
+       bzGenerateColor(29, 43, 83),
+       bzGenerateColor(126, 37, 83),
+       bzGenerateColor(0, 135, 81),
+       bzGenerateColor(171, 82, 54),
+       bzGenerateColor(95, 87, 79),
+       bzGenerateColor(194, 195, 199),
+       bzGenerateColor(255, 241, 232),
+       bzGenerateColor(255, 0, 77),
+       bzGenerateColor(255, 163, 0),
+       bzGenerateColor(255, 236, 39),
+       bzGenerateColor(0, 228, 54),
+       bzGenerateColor(41, 173, 255),
+       bzGenerateColor(131, 118, 156),
+       bzGenerateColor(255, 119, 168),
+       bzGenerateColor(255, 204, 170),
+
+       bzGenerateColor(41,24,20),
+       bzGenerateColor(17,29,53),
+       bzGenerateColor(66,33,54),
+       bzGenerateColor(18,83,89),
+       bzGenerateColor(116,47,41),
+       bzGenerateColor(73,51,59),
+       bzGenerateColor(162,136,121),
+       bzGenerateColor(243,239,125),
+       bzGenerateColor(190,18,80),
+       bzGenerateColor(255,108,36),
+       bzGenerateColor(168,231,46),
+       bzGenerateColor(0,181,67),
+       bzGenerateColor(6,90,181),
+       bzGenerateColor(117,70,101),
+       bzGenerateColor(255,110,89),
+       bzGenerateColor(255,157,129)
+};
+
+uint8_t palettes[16][16] = {
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+};
+
+//BZRect bzCalculateCullRect() {
+//     return bzRectMake(cameraX, cameraY, GFX_WIDTH, SS_HEIGHT);
+//}
+
+void bzGfxPrepareSpritesheet(size_t width, size_t height, void *data) {
+       uint8_t *imageData = (uint8_t *)data;
+       bzLog("Preparing spritesheet %dx%d", width, height);
+       //assert(width == 128 && height == 128);
+       for (size_t y = 0, i = 0; y < SS_HEIGHT; ++y) {
+               for (size_t x = 0; x < SS_WIDTH; ++x, ++i) {
+                       spritesheet[y][x] = imageData[i];
+               }
+       }
+}
+
+void bzGfxPrepareFont(void *font) {
+       currentFont = font;
+}
+
+static bool prepareFrame(int *minXOut, int *minYOut, int *maxXOut, int *maxYOut, int x0, int y0, int x1, int y1, int *clippedLeftX, int *clippedTopY, int *clippedRightX, int *clippedBottomY) {
+       int xMin = x0, xMax = x1;
+       int yMin = y0, yMax = y1;
+
+       if (x1 < x0) {
+               xMin = x1;
+               xMax = x0;
+       }
+
+       if (y1 < y0) {
+               yMin = y1;
+               yMax = y0;
+       }
+
+       int xMinOut = xMin - cameraX, xMaxOut = xMax - cameraX;
+       int yMinOut = yMin - cameraY, yMaxOut = yMax - cameraY;
+       
+       if (xMaxOut < 0 || yMaxOut < 0 || xMinOut >= GFX_WIDTH || yMinOut >= GFX_HEIGHT) {
+               return false;
+       }
+
+       int safeXMinOut = bzMax(0,              xMinOut);
+       int safeXMaxOut = bzMin(GFX_WIDTH - 1,  xMaxOut);
+       int safeYMinOut = bzMax(0,              yMinOut);
+       int safeYMaxOut = bzMin(GFX_HEIGHT - 1, yMaxOut);
+
+       *minXOut = safeXMinOut;
+       *minYOut = safeYMinOut;
+       *maxXOut = safeXMaxOut;
+       *maxYOut = safeYMaxOut;
+
+       if (clippedLeftX != NULL) {
+               *clippedLeftX = safeXMinOut - xMinOut;
+               *clippedTopY  = safeYMinOut - yMinOut;
+
+               *clippedRightX  = xMaxOut - safeXMaxOut;
+               *clippedBottomY = yMaxOut - safeYMaxOut;
+       }
+
+       return true;
+}
+
+static bool prepareColor(int *colorOut, int color) {
+       int c = (color <= 15) ? palettes[0][color] : color;
+       if (c > 0) {
+               *colorOut = c;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+void bzPSet(int x, int y, int c) {
+       if (c == 0) return;
+
+       int ox = x - cameraX;
+       int oy = y - cameraY;
+       if (ox >= 0 && ox < GFX_WIDTH && oy >=0 && oy < GFX_HEIGHT) {
+               if(fillPattern[y%4][x%4] == false) bzBufferSet(currentBuffer, ox, oy, (c <= 15) ? palettes[0][c] : c); // FIXME
+       }
+}
+
+int bzSGet(int x, int y) {
+       if (x >= 0 && x < SS_WIDTH && y >=0 && y < SS_HEIGHT) {
+               return spritesheet[y][x];
+       } else {
+               return 0;
+       }
+}
+
+//void bzSSet(int x, int y, int c);
+
+//bool bzFGet(int n, int f);
+//void bzFSet(int n, int f, bool v);
+
+void bzCls(uint8_t c) {
+
+       //memset(&buffer[currentBuffer], c, BUFFER_WIDTH * GFX_HEIGHT);
+
+//     for (size_t i = 0; i < )
+
+       for (size_t y = 0; y < GFX_HEIGHT; ++y) {
+               for (size_t x = 0; x < GFX_WIDTH; ++x) {
+                       bzBufferSet(currentBuffer, x, y, c);
+               }
+       }
+
+       //bzCamera(0, 0);
+       bzFillP(0x0000);
+}
+
+void bzGetCamera(int *x, int *y) {
+       if (x != NULL) *x = cameraX;
+       if (y != NULL) *y = cameraY;
+}
+
+void bzCamera(int x, int y) {
+       cameraX = x;
+       cameraY = y;
+}
+
+void bzCirc(int xm, int ym, int r, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, xm - r, ym - r, xm + r, ym + r, NULL, NULL, NULL, NULL)) {
+               xm -= cameraX;
+               ym -= cameraY;
+               
+               int x = -r;
+               int y = 0;
+               int err = 2 - 2 * r;
+
+               do {
+                       bzPSet(xm-x, ym+y, cOut);
+                       bzPSet(xm-y, ym-x, cOut);
+                       bzPSet(xm+x, ym-y, cOut);
+                       bzPSet(xm+y, ym+x, cOut);
+
+                       r = err;
+
+                       if (r <= y) err += (++y << 1) + 1;
+                       if (r > x || err > y) err += (++x << 1) + 1;
+               } while (x < 0);
+       }
+}
+
+void bzCircFill(int xm, int ym, int r, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, xm - r, ym - r, xm + r, ym + r, NULL, NULL, NULL, NULL)) {
+               xm -= cameraX;
+               ym -= cameraY;
+               
+               int x = -r;
+               int y = 0;
+               int err = 2 - 2 * r;
+
+               do {
+                       int fy1 = ym - bzAbs(y);
+                       int fy2 = ym + bzAbs(y);
+                       int fx  = bzMax(xm - bzAbs(x), 0);
+                       int tx  = bzMin(xm + bzAbs(x), GFX_WIDTH - 1); // FIXME, not as many abs
+
+                       bool validFy1 = fy1 >= 0 && fy1 < GFX_HEIGHT;
+                       bool validFy2 = fy2 >= 0 && fy2 < GFX_HEIGHT;
+
+                       if (validFy1 && validFy2) {
+                               for (int ox = fx; ox <= tx; ++ox) {
+                                       bzBufferSet(currentBuffer, ox, fy1, cOut);
+                                       bzBufferSet(currentBuffer, ox, fy2, cOut);
+                               }
+                       } else if (validFy1) {
+                               for (int ox = fx; ox <= tx; ++ox) {
+                                       bzBufferSet(currentBuffer, ox, fy1, cOut);
+                               }
+                       } else if (validFy2) {
+                               for (int ox = fx; ox <= tx; ++ox) {
+                                       bzBufferSet(currentBuffer, ox, fy2, cOut);
+                               }
+                       }
+
+                       r = err;
+
+                       if (r <= y) err += (++y << 1) + 1;
+                       if (r > x || err > y) err += (++x << 1) + 1;
+               } while (x < 0);
+       }
+}
+
+//void bzOval(int x0, int y0, int x1, int y1, int c);
+//void bzOvalFill(int x0, int y0, int x1, int y1, int c);
+
+void bzLine(int x0, int y0, int x1, int y1, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, x0, y0, x1, y1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               x0 = x0 - cameraX;
+               y0 = y0 - cameraY;
+               x1 = x1 - cameraX;
+               y1 = y1 - cameraY;
+
+               int deltaX = bzAbs(x1 - x0);
+               int deltaY = -bzAbs(y1 - y0);
+               int signX = bzSgn(x1 - x0);
+               int signY = bzSgn(y1 - y0);
+               int err = deltaX + deltaY;
+               int e2;
+
+               int x = x0, y = y0;
+
+               for (;;) {
+                       if (x >= 0 && x < GFX_WIDTH && y >=0 && y < GFX_HEIGHT) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, x, y, cOut);
+                       }
+
+                       e2 = err << 1;
+
+                       if (e2 >= deltaY) {
+                               if (x == x1) break;
+                               err += deltaY;
+                               x += signX;
+                       }
+
+                       if (e2 <= deltaX) {
+                               if (y == y1) break;
+                               err += deltaX;
+                               y += signY;
+                       }
+               }
+       }
+}
+
+void bzRect(int x0, int y0, int x1, int y1, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, x0, y0, x1, y1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               if (clipLeft == 0 && clipRight == 0) {
+                       for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                               bzBufferSet(currentBuffer, xMinOut, oy, cOut);
+                               bzBufferSet(currentBuffer, xMaxOut, oy, cOut);
+                       }
+               } else if (clipLeft == 0) {
+                       for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                               bzBufferSet(currentBuffer, xMinOut, oy, cOut);
+                       }
+               } else if (clipRight == 0) {
+                       for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                               bzBufferSet(currentBuffer, xMaxOut, oy, cOut);
+                       }
+               }
+
+               if (clipTop == 0 && clipBottom == 0) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, yMinOut, cOut);
+                               bzBufferSet(currentBuffer, ox, yMaxOut, cOut);
+                       }
+               } else if (clipTop == 0) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, yMinOut, cOut);
+                       }
+               } else if (clipBottom == 0) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, yMaxOut, cOut);
+                       }
+               }
+       }
+}
+
+void bzRectFill(int x0, int y0, int x1, int y1, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, x0, y0, x1, y1, NULL, NULL, NULL, NULL)) {
+               for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, oy, cOut);
+                       }
+               }
+       }
+}
+
+void bzSpr(int n, int x, int y) {
+       bzSprExt(n, x, y, 1, 1, false, false);
+}
+
+void bzSprExt(int n, int x, int y, int w, int h, bool flipX, bool flipY) {
+       int px = (n % 16) * 8;
+       int py = (n / 16) * 8;
+       int pw = w * 8;
+       int ph = h * 8;
+       bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY);
+}
+
+void bzSSpr(int sx, int sy, int sw, int sh, int dx, int dy) {
+       bzSSprExt(sx, sy, sw, sh, dx, dy, sw, sh, false, false);
+}
+
+void bzSSprExt(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flipX, bool flipY) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, dx, dy, dx+dw, dy+dh, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               
+               for (size_t y = 0; y < sh - (clipTop + clipBottom); ++y) {
+                       for (size_t x = 0; x < sw - (clipLeft + clipRight); ++x) {
+                               int color = spritesheet[clipTop+sy+y][clipLeft+sx+x];
+                               if (color > 0) bzBufferSet(currentBuffer, xMinOut+x, yMinOut+y, color); // FIXME, scaled up and 0 check removal??
+                       }
+               }
+       }
+       
+       /*int fromX = 0;
+       int toX = sw;
+       int dX = 1;
+
+       if (flipX) {
+               fromX = toX - 1;
+               toX = -1;
+               dX = -1;
+       }
+
+       int fromY = 0;
+       int toY = sh;
+       int dY = 1;
+
+       if (flipY) {
+               fromY = toY - 1;
+               toY = -1;
+               dY = -1;
+       }
+
+       for (int rY = fromY, wY = dy; rY != toY; rY += dY, ++wY) {
+               for (int rX = fromX, wX = dx; rX != toX; rX += dX, ++wX) {
+                       bzPSet(wX, wY, bzSGet(sx + rX, sy + rY));
+               }
+       }*/
+}
+
+void bzFillP(int p) {
+       int f = 1;
+       for (int y = 3; y >= 0; --y) {
+               for (int x = 3; x >= 0; --x, f<<=1) {
+                       fillPattern[y][x] = (p & f) > 0;
+               }
+       }
+}
+
+
+
+void bzPrint(int x, int y, int color, const char *text) {
+       int currentX = x, currentY = y;
+       bzGfxRenderFont(bzPSet, &currentX, &currentY, currentFont, color, text);
+}
+
+void bzSetPaletteColor(int palette, int colorIdx, int color) {
+       palettes[palette][colorIdx] = color;
+}
+
+#define bzGeneratePattern(p11, p12, p13, p14, p21, p22, p23, p24, p31, p32, p33, p34, p41, p42, p43, p44) { {p11, p12, p13, p14}, {p21, p22, p23, p24}, {p31, p32, p33, p34}, {p41, p42, p43, p44} }
+
+//(r&0xFF) << 0 | (g&0xFF) << 8 | (b&0xFF) << 16 | (0xFF) << 24
+
+int patterns[8][4][4] = {
+       bzGeneratePattern(0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0),
+       bzGeneratePattern(1, 0, 0, 0,
+                         0, 0, 0, 1,
+                         0, 1, 0, 0,
+                         0, 0, 1, 0),
+       bzGeneratePattern(0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0),
+       bzGeneratePattern(0, 1, 0, 1,
+                         1, 1, 1, 1,
+                         0, 1, 0, 1,
+                         1, 1, 1, 1),
+       bzGeneratePattern(0, 0, 0, 0,
+                         1, 1, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0),
+       bzGeneratePattern(0, 1, 0, 1,
+                         1, 0, 1, 0,
+                         0, 1, 0, 1,
+                         1, 0, 1, 0),
+       bzGeneratePattern(1, 1, 1, 1,
+                         1, 1, 1, 1,
+                         1, 1, 1, 1,
+                         1, 1, 1, 1),
+};
+
+uint8_t patternLookup[] = {
+       0,
+       0,
+       3,
+       1,
+       5,
+       5,
+       3,
+       6,
+       2,
+       5,
+       2,
+       2,
+       2,
+       5,
+       2,
+       2,
+       1,
+
+       
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1
+};
+
+
+
+
+// The real memory seems stupid slow??
+
+//#define kScreenMem 3120
+//static uint32_t *fakeScreen = NULL;//[3120];
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight) {
+//     if (fakeScreen == NULL) {
+//             fakeScreen = malloc(kScreenMem * sizeof(uint32_t));
+//     }
+
+       // FIXME, this one is very very slow.
+
+       bzPerfTimerStart("game.blit.composite");
+
+       uint8_t *p = &buffer[0][0][0];
+
+       for (size_t y = 0; y < GFX_HEIGHT; ++y) {
+               for (size_t x = 0; x < GFX_WIDTH; ++x) {
+//                     bzAssertMessage(buffer[1][y][x] == 1, "Got: %d", buffer[1][y][x]);
+
+                       p[7] = palettes[p[1] + 1][p[0]];
+                       p+=8*sizeof(uint8_t);
+
+//                     uint8_t cIdx = palettes[buffer[y][x][1] + 1][buffer[y][x][0]]; ////////// (palettes[buffer[y][x][1] + 1][buffer[y][x][0]] & buffer[y][x][2]); //  PALETTE, return this    (buffer[1][y][x]) + 1
+//                     if (buffer[y][x][2] == 0) cIdx = 0;
+//                     if (buffer[y][x][3] != 0) cIdx = buffer[y][x][3];
+//                     bzBufferSet(7, x, y, cIdx);
+
+//                     if (buffer[2][y][x] == 0) {
+//                             cIdx = 0;
+//                     }
+//                     if (buffer[3][y][x] > 0) {
+//                             cIdx = buffer[3][y][x];
+//                     }
+//                     intermediateBuffer[y][x] = cIdx;
+                       //intermediateBuffer[y][x] = paletteColors[cIdx];
+               }
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+
+       bzPerfTimerStart("game.blit.transfer");
+
+//     uint32_t *pixels = output;
+       uint32_t *pixels = (uint32_t *)output;//(uint32_t *)output;
+       //int idx = 0;
+       uint32_t value = 0;
+       for (size_t y = 0; y < GFX_HEIGHT * pixelHeight; ++y) {
+               //idx = y * 13;
+               int patY = y % 4;
+               //int patX = 0;
+               uint8_t ly = y >> 1;
+               uint8_t lx = 0;
+               for (size_t x = 0; x < GFX_WIDTH * pixelWidth; ) {
+                       //value = 0;
+                       int target = bzMin(GFX_WIDTH * pixelWidth - x, 32);
+                       for (int i = 0; i < target; ++i, ++x) {
+                               uint8_t c = buffer[ly /*/ pixelHeight*/][lx /*/ pixelWidth*/][7];
+                               uint8_t patternIdx = patternLookup[c];
+                               //bzAssertMessage(patternIdx <= 7, "Pattern index %d at %d,%d %d,%d", patternIdx, x, y, x / pixelWidth, y / pixelHeight);
+                               uint8_t bit = patterns[patternIdx][patY][x & 0b11 /*% 4*/];
+                               value = (value << 1) | bit ;// | patterns[patternIdx][patY][x % 4]; //(patterns[patternIdx][patY][x % 4] * 0b10000000000000000000000000000000);//x % 4 -> patX FIXME
+                               lx += x&1;
+                       }
+//                     *pixels = swap(value);
+                       __asm volatile ("rev %0, %1" : "=l" (*pixels) : "l" (value));
+                       pixels++;// += 4;//sizeof(uint32_t);
+               }
+               //pixels += 2; // stride...
+       }
+
+       bzPerfTimerStop("game.blit.transfer");
+
+//     memcpy(output, fakeScreen, kScreenMem * sizeof(uint32_t));
+}
+
+void bzSetOutputBuffer(int idx) {
+       currentBuffer = idx;
+}
+
+#endif
diff --git a/src_platform/playdate/bz/gfx/gfx_platform.h b/src_platform/playdate/bz/gfx/gfx_platform.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src_platform/playdate/bz/input/input.c b/src_platform/playdate/bz/input/input.c
new file mode 100644 (file)
index 0000000..ca21ae1
--- /dev/null
@@ -0,0 +1,138 @@
+#include <bz/input/input_internal.h>
+#include <bz/input/platform.h>
+
+#include <bz/input/input_id_internal.h>
+#include <playdate/entrypoint.h>
+
+typedef enum BZInputCode {
+       BZInputCodeQuit = 1,
+} BZInputCode;
+const BZInputCode BZInputCodeMax = BZInputCodeQuit;
+
+typedef union BZInputTableEntry {
+       bool booleanValue;
+       int integerValue;
+       float floatValue;
+} BZInputTableEntry;
+
+static BZInput bzQuitInput = { .id = BZInputCodeQuit, };
+BZInputID bzQuitInputID = &bzQuitInput;
+
+//const size_t inputTableSize = BZInputCodeMax + 1;
+#define inputTableSize 8
+static BZInputTableEntry inputTable[inputTableSize];
+
+typedef struct {
+       bool down;
+       bool pressed;
+} Button;
+
+static Button buttons[8];
+
+/*int buttonIdx(SDL_KeyCode keyCode) {
+       switch (keyCode) {
+               case SDLK_UP:
+                       return 1;
+
+               case SDLK_DOWN:
+                       return 2;
+
+               case SDLK_LEFT:
+                       return 3;
+
+               case SDLK_RIGHT:
+                       return 4;
+
+               case 'z':
+                       return 5;
+
+               case 'x':
+                       return 6;
+
+               default:
+                       return 0;
+       }
+}*/
+
+void bzInputInit() {
+       for (size_t i = 0; i < 8; ++i) {
+               buttons[i].down = false;
+       }
+}
+
+void bzInputTeardown() {
+
+}
+
+void bzInputProcessFrame() {
+       for (size_t i = 0; i < inputTableSize; ++i) {
+               inputTable[i].integerValue = 0; // FIXME, max size and whatever.
+       }
+
+//     for (size_t i = 0; i < 8; ++i) {
+//             buttons[i].down = buttons[i].down || buttons[i].pressed;
+//             buttons[i].pressed = false;
+//     }
+
+       PDButtons current;
+       PDButtons pushed;
+       PDButtons released;
+       playdate->system->getButtonState(&current, &pushed, &released);
+
+       buttons[1].down = (current & kButtonUp) > 0;
+       buttons[1].pressed = (pushed & kButtonUp) > 0;
+
+       buttons[2].down = (current & kButtonDown) > 0;
+       buttons[2].pressed = (pushed & kButtonDown) > 0;
+
+       buttons[3].down = (current & kButtonLeft) > 0;
+       buttons[3].pressed = (pushed & kButtonLeft) > 0;
+
+       buttons[4].down = (current & kButtonRight) > 0;
+       buttons[4].pressed = (pushed & kButtonRight) > 0;
+
+       buttons[5].down = (current & kButtonB) > 0;
+       buttons[5].pressed = (pushed & kButtonB) > 0;
+
+       buttons[6].down = (current & kButtonA) > 0;
+       buttons[6].pressed = (pushed & kButtonA) > 0;
+
+       /*SDL_Event event;
+       while (SDL_PollEvent(&event)) {
+               switch (event.type) {
+                       case SDL_QUIT:
+                               inputTable[BZInputCodeQuit].booleanValue = true;
+                               break;
+
+                       case SDL_KEYDOWN:
+                               buttons[buttonIdx(event.key.keysym.sym)].pressed = true;
+                               break;
+
+                       case SDL_KEYUP:
+                               buttons[buttonIdx(event.key.keysym.sym)].pressed = false;
+                               buttons[buttonIdx(event.key.keysym.sym)].down = false;
+                               break;
+               }
+       }*/
+}
+
+bool bzInputGetBooleanValue(BZInputID inputID) {
+       return inputTable[inputID->id].booleanValue;
+}
+
+int bzInputGetIntegerValue(BZInputID inputID) {
+       return inputTable[inputID->id].integerValue;
+}
+
+float bzInputGetFloatValue(BZInputID inputID) {
+       return inputTable[inputID->id].floatValue;
+}
+
+bool bzInputBtn(int id) {
+       return buttons[id].down || buttons[id].pressed;
+}
+
+bool bzInputBtnP(int id) {
+       return buttons[id].pressed;
+}
+
diff --git a/src_platform/playdate/bz/memory/arena.c b/src_platform/playdate/bz/memory/arena.c
new file mode 100644 (file)
index 0000000..9b0b191
--- /dev/null
@@ -0,0 +1,9 @@
+#include <bz/memory/arena_internal.h>
+
+#include <playdate/entrypoint.h>
+
+void *bzSystemAllocate(size_t size) {
+       void *mem = playdate->system->realloc(NULL, size);
+       bzAssertMessage(mem != NULL, "Allocation failed");
+       return mem;
+}
diff --git a/src_platform/playdate/bz/renderer/renderer.c b/src_platform/playdate/bz/renderer/renderer.c
new file mode 100644 (file)
index 0000000..cde9c61
--- /dev/null
@@ -0,0 +1,84 @@
+#include <bz/renderer/renderer_internal.h>
+
+#include <bz/gfx/gfx_internal.h>
+#include <bz/math/math.h>
+#include <bz/memory/arena.h>
+#include <bz/renderer/render_pass_internal.h>
+#include <playdate/entrypoint.h>
+
+struct BZRendererPass {
+       size_t idx;
+       BZRenderPassID pass;
+};
+
+/*static SDL_Window *window;
+static SDL_Renderer *renderer;
+static SDL_Texture *renderTexture;
+static SDL_Texture *renderTextureIntermediate;
+static size_t intermediateWidth, intermediateHeight;
+static int outputWidth, outputHeight;*/
+//static SDL_Surface *windowSurface;
+//static SDL_Surface *drawSurface;
+
+//const static size_t kMaxRenderPasses = 8;
+//static size_t numRenderPasses = 0;
+#define kMaxRenderPasses 1 // FIXME
+static BZRendererPass rendererPasses[kMaxRenderPasses];
+
+#define GAME_WIDTH 200 // 256 //128 //320 //128 // 400 // 128
+#define GAME_HEIGHT 120 // 128 //128 //180 //128 // 240 // 128
+
+//#define RENDERER_WIDTH 256 //128 //320 //128 // 400 // 128
+//#define RENDERER_HEIGHT 128 //128 //180 //128 // 240 // 128
+
+#define RENDERER_WIDTH 400 // 1280
+#define RENDERER_HEIGHT 240 // 720
+
+#define SCREEN_WIDTH 400 * 1 // 1280
+#define SCREEN_HEIGHT 240 * 1 // 720
+
+//#define SCREEN_SCALE 4
+
+void bzRendererInit() {//const BZRendererConfiguration *configuration, const char *title) {
+
+       for (size_t i = 0; i < kMaxRenderPasses; ++i) {
+               rendererPasses[i].pass = NULL;
+       }
+
+       prepare palettes
+       bzGfxPrepareCanvasBuffer(kBZSystemMemoryArena, GAME_WIDTH, GAME_HEIGHT);
+}
+
+void bzRendererTeardown() {
+}
+
+BZRendererPassID bzRendererRegisterPass(BZRenderPassID renderPass) {
+       for (size_t i = 0; i < kMaxRenderPasses; ++i) {
+               if (rendererPasses[i].pass == NULL) {
+                       rendererPasses[i].pass = renderPass;
+                       return &rendererPasses[i];
+               }
+       }
+       return NULL;
+}
+
+void bzRendererUnregisterPass(BZRendererPassID rendererPass) {
+       rendererPass->pass = NULL;
+}
+
+void bzRendererBlit() {
+       uint8_t *pixels = playdate->graphics->getFrame();
+       for (size_t i = 0; i < kMaxRenderPasses; ++i) {
+               BZRenderPass *renderPass = rendererPasses[i].pass;
+               if (renderPass != NULL) {
+                       if (renderPass->softwareHook != NULL) {
+                               renderPass->softwareHook((uint32_t *)pixels/*drawSurface->pixels*/, 2, 2);
+                       }
+               }
+       }
+}
+
+void bzRendererVBlank() {
+       playdate->system->drawFPS(0,0);
+       playdate->graphics->markUpdatedRows(0, 240); // FIXME
+}
diff --git a/src_platform/playdate/bz/resources/resource.c b/src_platform/playdate/bz/resources/resource.c
new file mode 100644 (file)
index 0000000..cc1e733
--- /dev/null
@@ -0,0 +1,49 @@
+#include <bz/resources/resource.h>
+
+#include <bz/memory/allocator.h>
+#include <bz/resources/identifier.h>
+#include <playdate/entrypoint.h>
+#include <stdarg.h>
+
+struct BZResource { SDFile *unused; };
+
+BZResourceID bzResourcesOpenResource(const char *type, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+       bzLog("Trying to load: %s", identifier);
+       
+       BZResourceID resource = (BZResourceID)playdate->file->open(identifier, kFileRead);
+       return resource;
+}
+
+extern void bzResourcesCloseResource(BZResourceID resource) {
+       SDFile *handle = (SDFile *)resource;
+       playdate->file->close(handle);
+}
+
+size_t bzResourcesFileLength(BZResourceID resource) {
+       SDFile *handle = (SDFile *)resource;
+
+       int reset, size;
+
+       reset = playdate->file->tell(handle);
+       playdate->file->seek(handle, 0, SEEK_END);
+       size = playdate->file->tell(handle);
+       playdate->file->seek(handle, reset, SEEK_SET);
+
+       return size;
+}
+
+size_t bzResourcesTell(BZResourceID resource) {
+       SDFile *handle = (SDFile *)resource;
+       return playdate->file->tell(handle);
+}
+
+size_t bzResourcesSeek(BZResourceID resource, size_t position) {
+       SDFile *handle = (SDFile *)resource;
+       return playdate->file->seek(handle, position, SEEK_SET);
+}
+
+size_t bzResourcesReadBytes(BZResourceID resource, void *outputBuffer, size_t numBytes) {
+       SDFile *handle = (SDFile *)resource;
+       return playdate->file->read(handle, outputBuffer, numBytes);
+}
diff --git a/src_platform/playdate/playdate/entrypoint.c b/src_platform/playdate/playdate/entrypoint.c
new file mode 100644 (file)
index 0000000..03e29b8
--- /dev/null
@@ -0,0 +1,64 @@
+//#include <stdio.h>
+//#include <stdlib.h>
+
+#include <playdate/entrypoint.h>
+
+#include <bz/debug/perfgraph_internal.h>
+#include <bz/input/input.h>
+#include <bz/input/platform.h>
+#include <bz/renderer/renderer.h>
+//#include <bz/resources/file_system_internal.h>
+
+extern void bzpGameInit(void);
+extern bool bzpGameTick(void);
+extern void bzpGameTeardown(void);
+
+static int update(void *userdata);
+
+PlaydateAPI *playdate;
+
+int eventHandler(PlaydateAPI *pd, PDSystemEvent event, uint32_t arg) {
+       playdate = pd;
+
+       bzLog("Booted to event handler.");
+
+       switch (event) {
+               case kEventInit:
+                       //bzFileSystemInit(argv[0], SDL_GetBasePath());
+                       bzpGameInit();
+                       pd->system->setUpdateCallback(update, pd);
+                       break;
+
+               default:
+                       // ...
+                       break;
+       }
+
+       return 0;
+}
+
+static int update(void *userdata) {
+       bzPerfReset();
+       bzpGameTick();
+       bzRendererVBlank(); // FIXME
+
+       return 1;
+}
+
+
+// This is some... interesting stuff from https://wiki.osdev.org/C%2B%2B#GCC
+
+void *__dso_handle;
+int __cxa_atexit(void (*destructor) (void *), void *arg, void *dso) {
+       // This is called to register a destructor to be called on program terminate. I'll probably just not bother.
+       return 0;
+}
+
+void __cxa_finalize(void *f) {
+       // This is called to actually call the things I just said I won't bother doing.
+}
+
+// Literally do not ask about these. Why do I need exception unwinding defines when exceptions are disabled????
+void *__exidx_start;
+void *__exidx_end;
diff --git a/src_platform/playdate/playdate/entrypoint.h b/src_platform/playdate/playdate/entrypoint.h
new file mode 100644 (file)
index 0000000..3cb3381
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef BTSPLY_PLAYDATE_ENTRYPOINT_H
+#define BTSPLY_PLAYDATE_ENTRYPOINT_H
+
+#include <pd_api.h>
+
+extern PlaydateAPI *playdate;
+
+#endif
diff --git a/src_platform/sdl/bz/audio/playback.c b/src_platform/sdl/bz/audio/playback.c
new file mode 100644 (file)
index 0000000..1a6c22c
--- /dev/null
@@ -0,0 +1,704 @@
+#include <bz/audio/playback_internal.h>
+
+#include <bz/audio/ogg.h>
+#include <bz/audio/speech.h>
+#include <bz/audio/wav.h>
+#include <bz/math/math.h>
+#include <bz/math/random.h>
+#include <bz/memory/allocator.h>
+#include <bz/resources/resource.h>
+#include <bz/types/identifier_internal.h>
+#include <parson.h>
+#include <SDL.h>
+
+// FIXME
+#define CHANNELS    2
+#define SAMPLE_RATE 48000
+#define SAMPLE_COUNT 512
+
+//#define STREAM_DATA_SIZE 16*4096 // FIXME
+
+enum BZAudioVariableType {
+       BZAudioVariableTypeConstant,
+       BZAudioVariableTypeParameter,
+};
+typedef enum BZAudioVariableType BZAudioVariableType;
+
+struct BZAudioVariable {
+       BZAudioVariableType type;
+       union {
+               float constantValue;
+               BZIdentifierHash parameterIdentifier;
+       };
+};
+typedef struct BZAudioVariable BZAudioVariable;
+
+struct BZAudioPlaybackEngineBus {
+       BZIdentifierHash identifier;
+       BZAudioVariable volume;
+       uint8_t *dataBuffer;
+       BZIdentifierHash targetBusIdentifier;
+};
+typedef struct BZAudioPlaybackEngineBus BZAudioPlaybackEngineBus;
+
+struct BZAudioPlaybackEngineParameter {
+       BZIdentifierHash identifier;
+       float value;
+};
+typedef struct BZAudioPlaybackEngineParameter BZAudioPlaybackEngineParameter;
+
+enum BZAudioPlaybackEngineSourceState {
+       BZAudioPlaybackEngineSourceStateFree,
+       BZAudioPlaybackEngineSourceStateEnqueued,
+       BZAudioPlaybackEngineSourceStatePlaying,
+};
+typedef enum BZAudioPlaybackEngineSourceState BZAudioPlaybackEngineSourceState;
+
+typedef void (*BZAudioPlaybackEngineSourceScheduleCallback)(BZAudioPlaybackEngineID engine, void *sourceData, va_list args);
+typedef size_t (*BZAudioPlaybackEngineSourceDataCallback)(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData);
+
+struct BZAudioPlaybackEngineSource {
+       BZIdentifierHash identifier;
+       BZAudioVariable volume;
+       BZIdentifierHash targetBusIdentifier;
+       
+       BZAudioPlaybackEngineSourceState state;
+       
+       BZAudioPlaybackEngineSourceScheduleCallback scheduleCallback;
+       BZAudioPlaybackEngineSourceDataCallback dataCallback;
+
+       void *sourceData;
+};
+typedef struct BZAudioPlaybackEngineSource BZAudioPlaybackEngineSource;
+
+//enum BZAudioSoundbankEventType {
+//     BZAudioSoundbankEventTypeStop,
+//     BZAudioSoundbankEventTypeSound,
+//     BZAudioSoundbankEventTypeStream,
+//     BZAudioSoundbankEventTypeVoice,
+//};
+//typedef enum BZAudioSoundbankEventType BZAudioSoundbankEventType;
+
+struct BZAudioPlaybackEngineEvent {
+       BZIdentifierHash identifier;
+       size_t sourceCount;
+       BZAudioPlaybackEngineSource **sources;
+};
+typedef struct BZAudioPlaybackEngineEvent BZAudioPlaybackEngineEvent;
+
+#define kParameterCount 32
+
+struct BZAudioPlaybackEngine {
+       SDL_AudioDeviceID audioDevice;
+       SDL_AudioFormat audioDeviceFormat;
+
+       uint8_t audioDeviceChannels;
+       uint32_t audioDeviceRate;
+       uint8_t silenceValue;
+
+       size_t dataBufferSize;
+       uint8_t *tmpDataBuffer;
+
+       size_t busCount;
+       BZAudioPlaybackEngineBus *buses;
+
+       SDL_mutex *schedulingMutex;
+
+       BZAudioPlaybackEngineParameter parameters[kParameterCount];
+
+       size_t sourceCount;
+       BZAudioPlaybackEngineSource *sources;
+
+       size_t eventCount;
+       BZAudioPlaybackEngineEvent *events;
+};
+
+static void audioVariableFromJSON(BZAudioVariable *variableOut, void *obj, const char *key, float defaultValue) {
+       JSON_Object *jsonObject = (JSON_Object *)obj;
+
+       if (json_object_has_value_of_type(jsonObject, key, JSONNumber)) {
+               variableOut->type = BZAudioVariableTypeConstant;
+               variableOut->constantValue = json_object_get_number(jsonObject, key);
+       } else if (json_object_has_value_of_type(jsonObject, key, JSONString)) {
+               variableOut->type = BZAudioVariableTypeParameter;
+               const char *parameterIdentifier = json_object_get_string(jsonObject, key);
+               variableOut->parameterIdentifier = bzIdentifierHashFromIdentifier(parameterIdentifier);
+       } else {
+               variableOut->type = BZAudioVariableTypeConstant;
+               variableOut->constantValue = defaultValue;
+       }
+}
+
+static float audioPlaybackResolveParameter(BZAudioPlaybackEngineID engine, const BZAudioVariable *variable) {
+       switch (variable->type) {
+               case BZAudioVariableTypeConstant:
+                       return variable->constantValue;
+
+               case BZAudioVariableTypeParameter: {
+                       for (size_t i = 0; i < kParameterCount; ++i) {
+                               if (engine->parameters[i].identifier == variable->parameterIdentifier) {
+                                       return engine->parameters[i].value;
+                               }
+                       }
+                       return 0.0f;
+               }
+       }
+}
+
+static void audioDataCallback(void *userdata, uint8_t *stream, int len) {
+       BZAudioPlaybackEngineID engine = (BZAudioPlaybackEngine *)userdata;
+       bzAssert(len <= engine->dataBufferSize);
+
+       SDL_LockMutex(engine->schedulingMutex);
+
+       SDL_memset(engine->buses[0].dataBuffer, engine->silenceValue, engine->dataBufferSize);
+
+       for (size_t i = engine->busCount - 1; i < engine->busCount; --i) {
+               BZAudioPlaybackEngineBus *bus = &engine->buses[i];
+
+               SDL_memset(bus->dataBuffer, engine->silenceValue, engine->dataBufferSize);
+
+               for (size_t j = 0; j < engine->sourceCount; ++j) {
+                       BZAudioPlaybackEngineSource *source = &engine->sources[j];
+                       if (source->state != BZAudioPlaybackEngineSourceStateFree && source->targetBusIdentifier == bus->identifier) {
+                               if (source->state == BZAudioPlaybackEngineSourceStateEnqueued) {
+                                       source->state = BZAudioPlaybackEngineSourceStatePlaying;
+                               }
+                               
+                               size_t dataOut = source->dataCallback(engine->tmpDataBuffer, engine->dataBufferSize, engine, source->sourceData);
+                               float volume = audioPlaybackResolveParameter(engine, &source->volume);
+                               SDL_MixAudioFormat(bus->dataBuffer, engine->tmpDataBuffer, engine->audioDeviceFormat, dataOut, bzLerp(0, 128, volume)); // FIXME, log2?
+
+                               if (dataOut < engine->dataBufferSize) {
+                                       source->state = BZAudioPlaybackEngineSourceStateFree;
+                               }
+                       }
+               }
+
+               for (size_t j = engine->busCount - 1; j > i; --j) {
+                       BZAudioPlaybackEngineBus *mixBus = &engine->buses[j];
+                       if (mixBus->targetBusIdentifier == bus->identifier) {
+                               float busVolume = audioPlaybackResolveParameter(engine, &mixBus->volume); // FIXME, log2?
+                               SDL_MixAudioFormat(bus->dataBuffer, mixBus->dataBuffer, engine->audioDeviceFormat, len, bzLerp(0, 128, busVolume));
+                       }
+               }
+       }
+
+       SDL_memset(stream, engine->silenceValue, engine->dataBufferSize);
+       SDL_MixAudioFormat(stream, engine->buses[0].dataBuffer, engine->audioDeviceFormat, len, bzLerp(0, 128, 1.0f)); // FIXME, volume...
+
+       SDL_UnlockMutex(engine->schedulingMutex);
+}
+
+BZAudioPlaybackEngineID bzAudioPlaybackInit(BZMemoryArenaID arena, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZAudioPlaybackEngineID engine = bzMemoryAlloc(arena, sizeof(BZAudioPlaybackEngine));
+
+       SDL_AudioSpec desiredSpec;
+       SDL_AudioSpec obtainedSpec;
+
+       SDL_memset(&desiredSpec, 0, sizeof(SDL_AudioSpec));
+       desiredSpec.freq = SAMPLE_RATE;
+       desiredSpec.format = AUDIO_F32;
+       desiredSpec.channels = CHANNELS;
+       desiredSpec.samples = SAMPLE_COUNT;
+       desiredSpec.callback = audioDataCallback;
+       desiredSpec.userdata = engine;
+
+       engine->audioDevice = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, SDL_AUDIO_ALLOW_ANY_CHANGE);
+       if (engine->audioDevice == 0) {
+               bzError("Failed to initialize audio device");
+       }
+
+       engine->audioDeviceFormat = obtainedSpec.format;
+       engine->audioDeviceChannels = obtainedSpec.channels;
+       engine->audioDeviceRate = obtainedSpec.freq;
+       engine->silenceValue = obtainedSpec.silence;
+
+       size_t byteSize = SDL_AUDIO_BITSIZE(obtainedSpec.format) >> 2;
+       engine->dataBufferSize = obtainedSpec.samples * byteSize * sizeof(uint8_t);
+
+       engine->tmpDataBuffer = bzMemoryAlloc(arena, engine->dataBufferSize);
+
+       BZResourceID handle = bzResourcesOpenResource("audio", "assets/audio/%s.config.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+
+       char *data = bzMemoryAllocTmp(arena, length);
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *configJson = json_parse_string(data);
+       JSON_Object *configJsonObject = json_object(configJson);
+
+       JSON_Array *busesArray = json_object_get_array(configJsonObject, "buses");
+       engine->busCount = json_array_get_count(busesArray) + 1;// + 1; // 0 is implicit and main bus
+       engine->buses = bzMemoryAlloc(arena, engine->busCount * sizeof(BZAudioPlaybackEngineBus));
+       for (size_t i = 0; i < engine->busCount; ++i) {
+               BZAudioPlaybackEngineBus *bus = &engine->buses[i];
+
+               size_t queueSize = 1;
+
+               if (i > 0) {
+                       JSON_Object *busObject = json_array_get_object(busesArray, i - 1);
+                       const char *identifier = json_object_get_string(busObject, "identifier");
+                       bus->identifier = bzIdentifierHashFromIdentifier(identifier);
+                       audioVariableFromJSON(&bus->volume, busObject, "volume", 1.0f);
+
+                       size_t configQueueSize = json_object_get_number(busObject, "queue_size");
+                       if (configQueueSize > 0) {
+                               queueSize = configQueueSize;
+                       }
+               }
+
+               bus->dataBuffer = bzMemoryAlloc(arena, engine->dataBufferSize);
+       }
+
+       engine->schedulingMutex = SDL_CreateMutex();
+
+       json_value_free(configJson);
+       bzMemoryArenaResetTmp(arena);
+
+       // Run
+       SDL_PauseAudioDevice(engine->audioDevice, 0);
+
+       return engine;
+}
+
+void bzAudioPlaybackTeardown(BZAudioPlaybackEngineID engine) {
+       SDL_CloseAudioDevice(engine->audioDevice);
+}
+
+void bzAudioPlaybackSetParameter(BZAudioPlaybackEngineID engine, BZIdentifierHash parameterIdentifier, float value) {
+       for (size_t i = 0; i < kParameterCount; ++i) {
+               BZAudioPlaybackEngineParameter *parameter = &engine->parameters[i];
+               if (parameter->identifier == parameterIdentifier || parameter->identifier == 0) {
+                       parameter->identifier = parameterIdentifier;
+                       parameter->value = value;
+                       return;
+               }
+       }
+}
+
+void bzAudioPlaybackPostEvent(BZAudioPlaybackEngineID engine, BZIdentifierHash eventIdentifier, ...) {
+       SDL_LockMutex(engine->schedulingMutex);
+       
+       va_list args;
+
+       for (size_t i = 0; i < engine->eventCount; ++i) {
+               BZAudioPlaybackEngineEvent *event = &engine->events[i];
+               if (event->identifier == eventIdentifier) {
+                       size_t idx = bzRandomInteger(event->sourceCount);
+                       BZAudioPlaybackEngineSource *source = event->sources[idx];
+                       source->state = BZAudioPlaybackEngineSourceStateEnqueued;
+
+                       va_start(args, eventIdentifier);
+                       source->scheduleCallback(engine, source->sourceData, args);
+                       va_end(args);
+               }
+       }
+       
+       SDL_UnlockMutex(engine->schedulingMutex);
+}
+
+size_t bzAudioCalculateMemorySizeForConversion(BZAudioPlaybackEngineID engine, uint32_t rate, uint8_t channels, uint16_t bps, bool isFloat, bool isSigned, size_t dataSize) {
+       SDL_AudioFormat format = 0;
+
+       switch (bps) {
+               case 8:
+                       format = isSigned ? AUDIO_S8 : AUDIO_U8;
+                       break;
+               
+               case 16:
+                       format = isSigned ? AUDIO_S16 : AUDIO_U16;
+                       break;
+               
+               case 32:
+                       format = isFloat ? AUDIO_F32 : AUDIO_S32;
+                       break;
+               
+               default:
+                       bzError("Unhandled sound format");
+                       break;
+       }
+       
+       SDL_AudioCVT cvt;
+       SDL_BuildAudioCVT(&cvt, format, channels, rate, engine->audioDeviceFormat, engine->audioDeviceChannels, engine->audioDeviceRate);
+
+       cvt.len = dataSize;
+       
+       return cvt.len * cvt.len_mult;
+}
+
+size_t bzAudioPlaybackConvertSample(BZAudioPlaybackEngineID engine, uint32_t rate, uint8_t channels, uint16_t bps, bool isFloat, bool isSigned, void *dst, size_t dataSize, void *src) {
+       SDL_AudioFormat format = 0;
+
+       switch (bps) {
+               case 8:
+                       format = isSigned ? AUDIO_S8 : AUDIO_U8;
+                       break;
+               
+               case 16:
+                       format = isSigned ? AUDIO_S16 : AUDIO_U16;
+                       break;
+               
+               case 32:
+                       format = isFloat ? AUDIO_F32 : AUDIO_S32;
+                       break;
+               
+               default:
+                       bzError("Unhandled sound format");
+                       break;
+       }
+
+       SDL_AudioCVT cvt;
+       SDL_BuildAudioCVT(&cvt, format, channels, rate, engine->audioDeviceFormat, engine->audioDeviceChannels, engine->audioDeviceRate);
+
+       cvt.len = dataSize;
+       cvt.buf = dst;//bzMemoryAlloc(arena, cvt.len * cvt.len_mult);
+
+       if (src != cvt.buf) {
+               SDL_memcpy(cvt.buf, src, dataSize);
+       }
+
+       int r = SDL_ConvertAudio(&cvt);
+       bzAssert(r == 0);
+
+       return cvt.len_cvt;
+}
+
+///////////
+
+struct BZAudioSoundSource {
+       size_t readPosition;
+       size_t soundSize;
+       uint8_t *soundData;
+};
+typedef struct BZAudioSoundSource BZAudioSoundSource;
+
+static void soundScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) {
+       BZAudioSoundSource *source = sourceData;
+       source->readPosition = 0;
+}
+
+static size_t soundDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
+       BZAudioSoundSource *source = sourceData;
+
+       size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition);
+       SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize);
+       source->readPosition += copySize;
+
+       return copySize;
+}
+
+static size_t audioPlaybackCountSounds(JSON_Array *soundsArray) {
+       size_t baseSoundCount = json_array_get_count(soundsArray);
+
+       size_t soundCount = 0;
+       for (size_t i = 0; i < baseSoundCount; ++i) {
+               ++soundCount; // FIXME, count the number of concurrent sounds
+       }
+
+       return soundCount;
+}
+
+// NB: Sounds.
+// You can have multiple sounds playing at each time.
+// We create a n sources for each sound, where n is the number of times that sound can be played
+// However, we only load the data once, and all sounds reference the same data.
+static void audioPlaybackLoadSounds(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *soundsArray) {
+       size_t soundCount = audioPlaybackCountSounds(soundsArray);
+       for (size_t i = 0; i < soundCount; ++i) {
+               BZAudioPlaybackEngineSource *source = &sources[i];
+               
+               JSON_Object *soundObject = json_array_get_object(soundsArray, i);
+
+               const char *identifier = json_object_get_string(soundObject, "identifier");
+               source->identifier = bzIdentifierHashFromIdentifier(identifier);
+
+               audioVariableFromJSON(&source->volume, soundObject, "volume", 1.0f);
+
+               const char *targetBusIdentifier = json_object_get_string(soundObject, "bus");
+               if (targetBusIdentifier != NULL) {
+                       source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier);
+               }
+
+               source->state = BZAudioPlaybackEngineSourceStateFree;
+
+               source->scheduleCallback = soundScheduleCallback;
+               source->dataCallback = soundDataCallback;
+               BZAudioSoundSource *soundSource = bzMemoryAlloc(arena, sizeof(BZAudioSoundSource));
+               source->sourceData = soundSource;
+
+               const char *filename = json_object_get_string(soundObject, "file");
+
+               size_t size;
+               uint32_t rate;
+               uint16_t channels;
+               uint16_t bps;
+               void *wavData = bzAudioLoadWAV(&size, arena, &rate, &channels, &bps, filename); // FIXME, temporary memory...
+
+               size_t bufferSize = bzAudioCalculateMemorySizeForConversion(engine, rate, channels, bps, false, bps > 8, size);
+               soundSource->soundData = bzMemoryAlloc(arena, bufferSize);
+               soundSource->soundSize = bzAudioPlaybackConvertSample(engine, rate, channels, bps, false, bps > 8, soundSource->soundData, size, wavData);
+
+               // FIXME, Wavedata shouldn't be used.
+       }
+}
+
+struct BZAudioStreamSource {
+       BZAudioOGGStream *oggStream;
+
+       size_t readPosition;
+
+       size_t channels;
+       size_t rate;
+       size_t oggFrameSize;
+
+       size_t soundSize;
+       uint8_t *soundData;
+};
+typedef struct BZAudioStreamSource BZAudioStreamSource;
+
+static void streamScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) {
+       BZAudioStreamSource *source = sourceData;
+       source->readPosition = 0;
+
+       size_t readPosition;
+       size_t soundSize;
+       uint8_t *soundData;
+}
+
+static size_t streamDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
+       BZAudioStreamSource *source = sourceData;
+
+       size_t copiedSize = 0;
+       for (;;) {
+               if (source->readPosition == source->soundSize) {
+                       size_t decodedDataSize = 0;
+                       
+                       while (decodedDataSize == 0) {
+                               decodedDataSize = bzAudioQueryOGGData(source->oggStream, source->oggFrameSize, (float *)source->soundData);
+                               if (decodedDataSize == 0) {
+                                       bzAudioResetOGGData(source->oggStream);
+                               }
+                       }
+                       source->soundSize = bzAudioPlaybackConvertSample(engine, source->rate, source->channels, 32, true, true, source->soundData, decodedDataSize, source->soundData);
+                       source->readPosition = 0;
+               }
+
+               size_t copySize = bzMin(dataOutSize - copiedSize, source->soundSize - source->readPosition);
+               SDL_memcpy(&dataOut[copiedSize], &source->soundData[source->readPosition], copySize);
+               copiedSize += copySize;
+               source->readPosition += copySize;
+
+               if (copiedSize == dataOutSize) {
+                       break;
+               }
+       }
+
+       return copiedSize;
+}
+
+static size_t audioPlaybackCountStreams(JSON_Array *streamsArray) {
+       return json_array_get_count(streamsArray);
+}
+
+// NB: Streams.
+// You can only have one instance of each stream playing at any time.
+// We create one source for each stream and then allocate data required to convert it.
+static void audioPlaybackLoadStreams(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *streamsArray) {
+       size_t streamCount = audioPlaybackCountStreams(streamsArray);
+       for (size_t i = 0; i < streamCount; ++i) {
+               BZAudioPlaybackEngineSource *source = &sources[i];
+               
+               JSON_Object *streamObject = json_array_get_object(streamsArray, i);
+
+               const char *identifier = json_object_get_string(streamObject, "identifier");
+               source->identifier = bzIdentifierHashFromIdentifier(identifier);
+
+               audioVariableFromJSON(&source->volume, streamObject, "volume", 1.0f);
+
+               const char *targetBusIdentifier = json_object_get_string(streamObject, "bus");
+               if (targetBusIdentifier != NULL) {
+                       source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier);
+               }
+
+               source->state = BZAudioPlaybackEngineSourceStateFree;
+
+               source->scheduleCallback = streamScheduleCallback;
+               source->dataCallback = streamDataCallback;
+               BZAudioStreamSource *streamSource = bzMemoryAlloc(arena, sizeof(BZAudioStreamSource));
+               source->sourceData = streamSource;
+
+               const char *filename = json_object_get_string(streamObject, "file");
+
+               BZAudioOGGStreamDetails oggStreamDetails;
+               streamSource->oggStream = bzAudioOpenOGGStream(&oggStreamDetails, arena, filename);
+
+               streamSource->channels = oggStreamDetails.channels;
+               streamSource->rate = oggStreamDetails.rate;
+               streamSource->oggFrameSize = oggStreamDetails.channels * oggStreamDetails.maxSamples * sizeof(float);
+               size_t soundMaxSize = bzAudioCalculateMemorySizeForConversion(engine, oggStreamDetails.rate, oggStreamDetails.channels, 32, true, true, streamSource->oggFrameSize);
+
+               size_t decodeMemorySize = bzMax(streamSource->oggFrameSize, soundMaxSize);
+               streamSource->soundData = bzMemoryAlloc(arena, decodeMemorySize);
+       }
+}
+
+struct BZAudioVoiceSource {
+       BZAudioSoundSource soundSource;
+       BZAudioVoice *voice;
+       size_t voiceMaxSize;
+       size_t channels;
+       size_t rate;
+};
+typedef struct BZAudioVoiceSource BZAudioVoiceSource;
+
+static void voiceScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) {
+       BZAudioVoiceSource *source = sourceData;
+
+       const char *speechText = va_arg(args, const char *);
+
+       size_t dataSize = bzAudioGenerateSpeech(source->voice, (short *)source->soundSource.soundData, source->voiceMaxSize, speechText);
+
+       source->soundSource.soundSize = bzAudioPlaybackConvertSample(engine, source->rate, source->channels, 16, false, true, source->soundSource.soundData, dataSize, source->soundSource.soundData);
+       source->soundSource.readPosition = 0;
+}
+
+/*static size_t voiceDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
+       BZAudioVoiceSource *source = sourceData;
+
+       size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition);
+       SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize);
+       source->readPosition += copySize;
+
+       return copySize;
+}*/
+
+static size_t audioPlaybackCountVoices(JSON_Array *voicesArray) {
+       return json_array_get_count(voicesArray);
+}
+
+// NB: Voices.
+// You can only have one instance of each voice playing at any time.
+// We create one source for each voice and then allocate data required to synthesize.
+// Events can be enqueued(??)
+static void audioPlaybackLoadVoices(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *voicesArray) {
+       size_t voiceCount = audioPlaybackCountVoices(voicesArray);
+       for (size_t i = 0; i < voiceCount; ++i) {
+               BZAudioPlaybackEngineSource *source = &sources[i];
+               
+               JSON_Object *voiceObject = json_array_get_object(voicesArray, i);
+
+               const char *identifier = json_object_get_string(voiceObject, "identifier");
+               source->identifier = bzIdentifierHashFromIdentifier(identifier);
+
+               audioVariableFromJSON(&source->volume, voiceObject, "volume", 1.0f);
+
+               const char *targetBusIdentifier = json_object_get_string(voiceObject, "bus");
+               if (targetBusIdentifier != NULL) {
+                       source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier);
+               }
+
+               source->state = BZAudioPlaybackEngineSourceStateFree;
+
+               source->scheduleCallback = voiceScheduleCallback;
+               source->dataCallback = soundDataCallback;
+               BZAudioVoiceSource *voiceSource = bzMemoryAlloc(arena, sizeof(BZAudioVoiceSource));
+               source->sourceData = voiceSource;
+
+               BZAudioVoiceDetails voiceDetails;
+               voiceSource->voice = bzAudioLoadSpeechVoice(&voiceDetails, arena, "%s", identifier);
+
+               voiceSource->channels = voiceDetails.channels;
+               voiceSource->rate = voiceDetails.rate;
+
+               voiceSource->voiceMaxSize = voiceDetails.channels * voiceDetails.maxSamples * sizeof(short);
+               size_t soundMaxSize = bzAudioCalculateMemorySizeForConversion(engine, voiceDetails.rate, voiceDetails.channels, 16, false, true, voiceSource->voiceMaxSize);
+
+               size_t decodeMemorySize = bzMax(voiceSource->voiceMaxSize, soundMaxSize);
+               voiceSource->soundSource.soundData = bzMemoryAlloc(arena, decodeMemorySize);
+       }
+}
+
+static void audioPlaybackLoadEvents(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *eventsArray) {
+       engine->eventCount = json_array_get_count(eventsArray);
+       engine->events = bzMemoryAlloc(arena, engine->eventCount * sizeof(BZAudioPlaybackEngineEvent));
+       for (size_t i = 0; i < engine->eventCount; ++i) {
+               BZAudioPlaybackEngineEvent *event = &engine->events[i];
+               
+               JSON_Object *eventObject = json_array_get_object(eventsArray, i);
+
+               const char *identifier = json_object_get_string(eventObject, "identifier");
+               event->identifier = bzIdentifierHashFromIdentifier(identifier);
+
+               const char *sourceIdentifier = NULL;
+               JSON_Array *sourcesArray = NULL;
+
+               if (json_object_has_value_of_type(eventObject, "source", JSONArray)) {
+                       sourcesArray = json_object_get_array(eventObject, "source");
+                       event->sourceCount = json_array_get_count(sourcesArray);
+               } else if (json_object_has_value_of_type(eventObject, "source", JSONString)) {
+                       event->sourceCount = 1;
+                       sourceIdentifier = json_object_get_string(eventObject, "source");
+               } else {
+                       // FIXME, stop event
+               }
+
+               event->sources = bzMemoryAlloc(arena, event->sourceCount * sizeof(BZAudioPlaybackEngineSource *));
+               for (size_t j = 0; j < event->sourceCount; ++j) {
+                       if (sourcesArray != NULL) {
+                               sourceIdentifier = json_array_get_string(sourcesArray, j);
+                       }
+
+                       BZIdentifierHash sourceIdentifierHash = bzIdentifierHashFromIdentifier(sourceIdentifier);
+                       for (size_t k = 0; k < engine->sourceCount; ++k) {
+                               BZAudioPlaybackEngineSource *source = &engine->sources[k];
+                               if (source->identifier == sourceIdentifierHash) {
+                                       event->sources[j] = source;
+                                       break;
+                               }
+                       }
+
+                       bzAssertMessage(event->sources[j] != NULL, "Could not find source for event '%s': %s", identifier, sourceIdentifier);
+               }
+       }
+}
+
+void bzAudioPlaybackUseSoundbank(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, const char *identifierFmt, ...) {
+//FIXME, reset
+
+       bzMakeIdentifier(identifier, identifierFmt);
+
+       BZResourceID handle = bzResourcesOpenResource("soundbanks", "assets/soundbanks/%s.soundbank.json", identifier);
+       size_t length = bzResourcesFileLength(handle);
+
+       char *data = bzMemoryAllocTmp(arena, length);
+       bzResourcesReadBytes(handle, data, length);
+       bzResourcesCloseResource(handle);
+
+       //json_set_allocation_functions
+       JSON_Value *soundbankJson = json_parse_string(data);
+       JSON_Object *soundbankJsonObject = json_object(soundbankJson);
+
+       JSON_Array *soundsArray = json_object_get_array(soundbankJsonObject, "sounds");
+       JSON_Array *streamsArray = json_object_get_array(soundbankJsonObject, "streams");
+       JSON_Array *voicesArray = json_object_get_array(soundbankJsonObject, "voices");
+
+       size_t soundsCount = audioPlaybackCountSounds(soundsArray);
+       size_t streamsCount = audioPlaybackCountStreams(streamsArray);
+       engine->sourceCount = soundsCount + streamsCount + audioPlaybackCountVoices(voicesArray);
+       engine->sources = bzMemoryAlloc(arena, engine->sourceCount * sizeof(BZAudioPlaybackEngineSource));
+
+       audioPlaybackLoadSounds(&engine->sources[0], engine, arena, soundsArray);
+       audioPlaybackLoadStreams(&engine->sources[soundsCount], engine, arena, streamsArray);
+       audioPlaybackLoadVoices(&engine->sources[soundsCount + streamsCount], engine, arena, voicesArray);
+
+       JSON_Array *eventsArray = json_object_get_array(soundbankJsonObject, "events");
+       audioPlaybackLoadEvents(engine, arena, eventsArray);
+
+       json_value_free(soundbankJson);
+       bzMemoryArenaResetTmp(arena);
+}
diff --git a/src_platform/sdl/bz/debug/assert.c b/src_platform/sdl/bz/debug/assert.c
new file mode 100644 (file)
index 0000000..30e8392
--- /dev/null
@@ -0,0 +1,7 @@
+#include <bz/debug/assert.h>
+
+#include <stdlib.h>
+
+void bzAssertExit() {
+       exit(1);
+}
diff --git a/src_platform/sdl/bz/debug/log.c b/src_platform/sdl/bz/debug/log.c
new file mode 100644 (file)
index 0000000..cfb2ff5
--- /dev/null
@@ -0,0 +1,62 @@
+#include <bz/debug/log_internal.h>
+
+#include <stb_sprintf.h>
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#define _OUTPUT_PRELUDE(dst, name, filename, lineNumber) { \
+               time_t t; \
+               time(&t); \
+               struct tm *timeinfo = localtime(&t); \
+               output(dst, name",%02d:%02d:%02d,\"%s\"@%d,", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, filename, lineNumber); \
+       }
+
+static char buffer[STB_SPRINTF_MIN];
+
+static int errorFD = STDERR_FILENO;
+static int outputFD = STDOUT_FILENO;
+
+static char *sprintfCallback(char const *buf, void *user, int len) {
+       int fd = (int)((long)user);
+       write(fd, buf, (size_t)len);
+       return buffer;
+}
+
+static void output(int fd, const char *fmt, ...) {
+       va_list args;
+       va_start(args, fmt);
+       stbsp_vsprintfcb(sprintfCallback, (void *)((long)fd), buffer, fmt, args);
+       va_end(args);
+}
+
+void _bzLog(const char *filename, unsigned long lineNumber, const char *fmt, ...) {
+       va_list args;
+       va_start(args, fmt);
+       _OUTPUT_PRELUDE(outputFD, "LOG", filename, lineNumber);
+       stbsp_vsprintfcb(sprintfCallback, (void *)((long)outputFD), buffer, fmt, args);
+       output(outputFD, "\n");
+       va_end(args);
+}
+
+void _bzError(const char *filename, unsigned long lineNumber, const char *fmt, ...) {
+       va_list args;
+       va_start(args, fmt);
+       _OUTPUT_PRELUDE(errorFD, "ERROR", filename, lineNumber);
+       stbsp_vsprintfcb(sprintfCallback, (void *)((long)errorFD), buffer, fmt, args);
+       output(errorFD, "\n");
+       va_end(args);
+}
+
+void bzLogInit(const char *outputFilePath) {
+       if (outputFilePath != NULL) {
+               outputFD = errorFD = open(outputFilePath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+       }
+}
+
+void bzLogTeardown() {
+       //close();
+}
diff --git a/src_platform/sdl/bz/debug/perfgraph.c b/src_platform/sdl/bz/debug/perfgraph.c
new file mode 100644 (file)
index 0000000..0075351
--- /dev/null
@@ -0,0 +1,12 @@
+#include <SDL.h>
+
+static Uint64 baseTime;
+
+void resetSystemTimer(void) {
+       baseTime = SDL_GetTicks64();
+}
+
+float getSystemTimer(void) {
+       Uint64 delta = SDL_GetTicks64() - baseTime;
+       return delta / 1000.0f;
+}
diff --git a/src_platform/sdl/bz/gfx/gfx_platform.c b/src_platform/sdl/bz/gfx/gfx_platform.c
new file mode 100644 (file)
index 0000000..2067c6c
--- /dev/null
@@ -0,0 +1,853 @@
+#include <bz/gfx/gfx_platform.h>
+
+#include <bz/gfx/gfx_internal.h>
+#include <bz/math/math.h>
+#include <bz/renderer/palette_internal.h>
+#include <bz/renderer/render_pass_internal.h>
+
+static void gfxRender(BZRendererPaletteID palette, uint32_t *output, size_t width, size_t height, size_t pixelWidth, size_t pixelHeight);
+
+static BZRenderPass renderPass = {
+       .softwareHook = gfxRender,
+};
+BZRenderPassID bzGFXRenderPass = &renderPass;
+
+#define bzGenerateColor(r, g, b) ((r)&0xFF) << 0 | ((g)&0xFF) << 8 | ((b)&0xFF) << 16 | (0xFF) << 24
+uint32_t paletteColors[] = {
+       /* 00: */ bzGenerateColor(13, 2, 33),       // black
+       /* 01: */ bzGenerateColor(36, 23, 52),    // grey
+       /* 02: */ bzGenerateColor(46, 33, 86), // grey
+       /* 03: */ bzGenerateColor(45, 226, 230), // white
+       /* 04: */ bzGenerateColor(253, 55, 119),   // red
+       /* 05: */ bzGenerateColor(255, 56, 100),   // red
+       /* 06: */ bzGenerateColor(255, 67, 101),   // orange
+       /* 07: */ bzGenerateColor(255, 108, 17),  // orange
+       /* 08: */ bzGenerateColor(181, 181, 40),  // yellow
+       /* 09: */ bzGenerateColor(249, 200, 14),  // yellow
+       /* 10: */ bzGenerateColor(31, 105, 75),   // green
+       /* 11: */ bzGenerateColor(106, 190, 48),  // green
+       /* 12: */ bzGenerateColor(48, 96, 130),   // blue
+       /* 13: */ bzGenerateColor(99, 155, 255),  // blue
+       /* 14: */ bzGenerateColor(118, 66, 138),  // purple
+       /* 15: */ bzGenerateColor(246, 1, 157), // purple
+
+#if 0 // Vector16
+       /* 00: */ bzGenerateColor(0, 0, 0),       // black
+       /* 01: */ bzGenerateColor(92, 92, 92),    // grey
+       /* 02: */ bzGenerateColor(150, 150, 150), // grey
+       /* 03: */ bzGenerateColor(255, 255, 255), // white
+       /* 04: */ bzGenerateColor(172, 50, 50),   // red
+       /* 05: */ bzGenerateColor(217, 87, 99),   // red
+       /* 06: */ bzGenerateColor(143, 78, 30),   // orange
+       /* 07: */ bzGenerateColor(223, 113, 38),  // orange
+       /* 08: */ bzGenerateColor(181, 181, 40),  // yellow
+       /* 09: */ bzGenerateColor(251, 242, 54),  // yellow
+       /* 10: */ bzGenerateColor(31, 105, 75),   // green
+       /* 11: */ bzGenerateColor(106, 190, 48),  // green
+       /* 12: */ bzGenerateColor(48, 96, 130),   // blue
+       /* 13: */ bzGenerateColor(99, 155, 255),  // blue
+       /* 14: */ bzGenerateColor(118, 66, 138),  // purple
+       /* 15: */ bzGenerateColor(215, 123, 186), // purple
+#endif
+
+#if 0 // Pico-8
+       /* 00: */ bzGenerateColor(0, 0, 0),
+       /* 01: */ bzGenerateColor(29, 43, 83),
+       /* 02: */ bzGenerateColor(126, 37, 83),
+       /* 03: */ bzGenerateColor(0, 135, 81),
+       /* 04: */ bzGenerateColor(171, 82, 54),
+       /* 05: */ bzGenerateColor(95, 87, 79),
+       /* 06: */ bzGenerateColor(194, 195, 199),
+       /* 07: */ bzGenerateColor(255, 241, 232),
+       /* 08: */ bzGenerateColor(255, 0, 77),
+       /* 09: */ bzGenerateColor(255, 163, 0),
+       /* 10: */ bzGenerateColor(255, 236, 39),
+       /* 11: */ bzGenerateColor(0, 228, 54),
+       /* 12: */ bzGenerateColor(41, 173, 255),
+       /* 13: */ bzGenerateColor(131, 118, 156),
+       /* 14: */ bzGenerateColor(255, 119, 168),
+       /* 15: */ bzGenerateColor(255, 204, 170),
+
+       /* 16: */ bzGenerateColor(41,24,20),
+       /* 17: */ bzGenerateColor(17,29,53),
+       /* 18: */ bzGenerateColor(66,33,54),
+       /* 19: */ bzGenerateColor(18,83,89),
+       /* 20: */ bzGenerateColor(116,47,41),
+       /* 21: */ bzGenerateColor(73,51,59),
+       /* 22: */ bzGenerateColor(162,136,121),
+       /* 23: */ bzGenerateColor(243,239,125),
+       /* 24: */ bzGenerateColor(190,18,80),
+       /* 25: */ bzGenerateColor(255,108,36),
+       /* 26: */ bzGenerateColor(168,231,46),
+       /* 27: */ bzGenerateColor(0,181,67),
+       /* 28: */ bzGenerateColor(6,90,181),
+       /* 29: */ bzGenerateColor(117,70,101),
+       /* 30: */ bzGenerateColor(255,110,89),
+       /* 31: */ bzGenerateColor(255,157,129)
+#endif
+};
+
+static void gfxRender(BZRendererPaletteID palette, uint32_t *output, size_t width, size_t height, size_t pixelWidth, size_t pixelHeight) {
+       bzGfxComposite();
+
+       bzPerfTimerStart("game.blit.transfer");
+
+       uint32_t *pixels = output;
+       
+       for (size_t y = 0; y < height; ++y) {
+               uint8_t *bufferLine = &bzGfxCompositedBuffer[y << bufferStrideShift]; // FIXME, bitshift
+               for (size_t x = 0; x < width; ++x) {
+                       uint8_t c = bufferLine[x];
+                       uint32_t out = palette->colors[c % palette->numColors];
+                       uint32_t *pixelsOut = pixels;
+                       for (size_t py = 0; py < pixelHeight; ++py) {
+                               for (size_t px = 0; px < pixelWidth; ++px) {
+                                       *pixelsOut = out;
+                                       pixelsOut++;
+                               }
+                               pixelsOut+=width * pixelWidth - pixelWidth;
+                       }
+                       
+                       pixels += pixelWidth;
+               }
+               pixels += (pixelHeight - 1) * width * pixelWidth;
+       }
+
+       bzPerfTimerStop("game.blit.transfer");
+}
+
+
+
+
+
+
+
+
+#if 0
+
+void bzGfxComposite(uint8_t *outputBuffer) {
+       bzPerfTimerStart("game.blit.composite");
+
+       size_t canvasMemorySize = bufferStride * height * sizeof(uint8_t);
+       
+       uint8_t *drawBufferReadHead = drawBuffer;
+       uint8_t *paletteBufferReadHead = paletteBuffer;
+       uint8_t *maskBufferReadHead = maskBuffer;
+       uint8_t *overlayBufferReadHead = overlayBuffer;
+       uint8_t *outputBufferWriteHead = outputBuffer;
+       for (size_t i = 0; i < bufferStride * height; ++i) {
+               *outputBufferWriteHead = palettes[*paletteBufferReadHead + 1][*drawBufferReadHead];
+
+               ++drawBufferReadHead;
+               ++paletteBufferReadHead;
+               ++maskBufferReadHead;
+               ++overlayBufferReadHead;
+               ++outputBufferWriteHead;
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// The real memory seems stupid slow??
+
+//#define kScreenMem 3120
+//static uint32_t *fakeScreen = NULL;//[3120];
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight) {
+//     if (fakeScreen == NULL) {
+//             fakeScreen = malloc(kScreenMem * sizeof(uint32_t));
+//     }
+
+       // FIXME, this one is very very slow.
+
+       bzPerfTimerStart("game.blit.composite");
+
+       uint8_t *p = &buffer[0][0][0];
+
+       for (size_t y = 0; y < canvasHeight; ++y) {
+               for (size_t x = 0; x < canvasWidth; ++x) {
+//                     bzAssertMessage(buffer[1][y][x] == 1, "Got: %d", buffer[1][y][x]);
+
+                       p[7] = palettes[p[1] + 1][p[0]];
+                       p+=8*sizeof(uint8_t);
+
+//                     uint8_t cIdx = palettes[buffer[y][x][1] + 1][buffer[y][x][0]]; ////////// (palettes[buffer[y][x][1] + 1][buffer[y][x][0]] & buffer[y][x][2]); //  PALETTE, return this    (buffer[1][y][x]) + 1
+//                     if (buffer[y][x][2] == 0) cIdx = 0;
+//                     if (buffer[y][x][3] != 0) cIdx = buffer[y][x][3];
+//                     bzBufferSet(7, x, y, cIdx);
+
+//                     if (buffer[2][y][x] == 0) {
+//                             cIdx = 0;
+//                     }
+//                     if (buffer[3][y][x] > 0) {
+//                             cIdx = buffer[3][y][x];
+//                     }
+//                     intermediateBuffer[y][x] = cIdx;
+                       //intermediateBuffer[y][x] = paletteColors[cIdx];
+               }
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+
+       bzPerfTimerStart("game.blit.transfer");
+
+//     uint32_t *pixels = output;
+       uint32_t *pixels = (uint32_t *)output;//(uint32_t *)output;
+       //int idx = 0;
+       uint32_t value = 0;
+       for (size_t y = 0; y < canvasHeight * pixelHeight; ++y) {
+               //idx = y * 13;
+               int patY = y % 4;
+               //int patX = 0;
+               uint8_t ly = y >> 1;
+               uint8_t lx = 0;
+               for (size_t x = 0; x < canvasWidth * pixelWidth; ) {
+                       //value = 0;
+                       int target = bzMin(canvasWidth * pixelWidth - x, 32);
+                       for (int i = 0; i < target; ++i, ++x) {
+                               uint8_t c = buffer[ly /*/ pixelHeight*/][lx /*/ pixelWidth*/][7];
+                               uint8_t patternIdx = patternLookup[c];
+                               //bzAssertMessage(patternIdx <= 7, "Pattern index %d at %d,%d %d,%d", patternIdx, x, y, x / pixelWidth, y / pixelHeight);
+                               uint8_t bit = patterns[patternIdx][patY][x & 0b11 /*% 4*/];
+                               value = (value << 1) | bit ;// | patterns[patternIdx][patY][x % 4]; //(patterns[patternIdx][patY][x % 4] * 0b10000000000000000000000000000000);//x % 4 -> patX FIXME
+                               lx += x&1;
+                       }
+//                     *pixels = swap(value);
+                       __asm volatile ("rev %0, %1" : "=l" (*pixels) : "l" (value));
+                       pixels++;// += 4;//sizeof(uint32_t);
+               }
+               //pixels += 2; // stride...
+       }
+
+       bzPerfTimerStop("game.blit.transfer");
+
+//     memcpy(output, fakeScreen, kScreenMem * sizeof(uint32_t));
+}
+
+
+
+
+
+
+#include <bz/gfx/gfx_internal.h>
+
+#include <bz/gfx/font_internal.h>
+#include <bz/math/math.h>
+#include <bz/renderer/render_pass_internal.h>
+
+#include <stdlib.h>
+
+// http://members.chello.at/~easyfilter/Bresenham.pdf
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight);
+
+static BZRenderPass renderPass = {
+       .softwareHook = gfxRender,
+};
+BZRenderPassID bzGFXRenderPass = &renderPass;
+
+#define GFX_WIDTH 200 //256 // 128 //320
+#define GFX_HEIGHT 120 //128 // 128 //180
+
+static int currentBuffer = 0;
+static uint8_t buffer[GFX_HEIGHT][GFX_WIDTH][8];
+static int cameraX = 0;
+static int cameraY = 0;
+static bool fillPattern[4][4];
+
+#define _bzBufferSet(idx, x, y, value) buffer[(y)][(x)][(idx)] = (value)
+#define _bzBufferSetSafe(idx, x, y, value) { bzAssert(idx>= 0 && idx<8);bzAssertMessage((x)>= 0 && (x)<GFX_WIDTH, "invalid x: %d   %s, %d", x, __FILE__, __LINE__);bzAssertMessage((y)>= 0 && (y)<GFX_HEIGHT, "invalid y: %d   %s, %d", y, __FILE__, __LINE__);_bzBufferSet(idx, x, y, value); }
+
+#define bzBufferSet _bzBufferSet
+
+//uint32_t intermediateBuffer[GFX_HEIGHT][GFX_WIDTH];
+
+#define SS_WIDTH 128
+#define SS_HEIGHT 128
+
+static uint8_t spritesheet[SS_HEIGHT][SS_WIDTH];
+
+static BZFont *currentFont;
+
+#define bzGenerateColor(r, g, b) (r&0xFF) << 0 | (g&0xFF) << 8 | (b&0xFF) << 16 | (0xFF) << 24
+uint32_t paletteColors[] = {
+       bzGenerateColor(0, 0, 0),
+       bzGenerateColor(29, 43, 83),
+       bzGenerateColor(126, 37, 83),
+       bzGenerateColor(0, 135, 81),
+       bzGenerateColor(171, 82, 54),
+       bzGenerateColor(95, 87, 79),
+       bzGenerateColor(194, 195, 199),
+       bzGenerateColor(255, 241, 232),
+       bzGenerateColor(255, 0, 77),
+       bzGenerateColor(255, 163, 0),
+       bzGenerateColor(255, 236, 39),
+       bzGenerateColor(0, 228, 54),
+       bzGenerateColor(41, 173, 255),
+       bzGenerateColor(131, 118, 156),
+       bzGenerateColor(255, 119, 168),
+       bzGenerateColor(255, 204, 170),
+
+       bzGenerateColor(41,24,20),
+       bzGenerateColor(17,29,53),
+       bzGenerateColor(66,33,54),
+       bzGenerateColor(18,83,89),
+       bzGenerateColor(116,47,41),
+       bzGenerateColor(73,51,59),
+       bzGenerateColor(162,136,121),
+       bzGenerateColor(243,239,125),
+       bzGenerateColor(190,18,80),
+       bzGenerateColor(255,108,36),
+       bzGenerateColor(168,231,46),
+       bzGenerateColor(0,181,67),
+       bzGenerateColor(6,90,181),
+       bzGenerateColor(117,70,101),
+       bzGenerateColor(255,110,89),
+       bzGenerateColor(255,157,129)
+};
+
+uint8_t palettes[16][16] = {
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+};
+
+//BZRect bzCalculateCullRect() {
+//     return bzRectMake(cameraX, cameraY, GFX_WIDTH, SS_HEIGHT);
+//}
+
+void bzGfxPrepareSpritesheet(size_t width, size_t height, void *data) {
+       uint8_t *imageData = (uint8_t *)data;
+       bzLog("Preparing spritesheet %dx%d", width, height);
+       //assert(width == 128 && height == 128);
+       for (size_t y = 0, i = 0; y < SS_HEIGHT; ++y) {
+               for (size_t x = 0; x < SS_WIDTH; ++x, ++i) {
+                       spritesheet[y][x] = imageData[i];
+               }
+       }
+}
+
+void bzGfxPrepareFont(void *font) {
+       currentFont = font;
+}
+
+static bool prepareFrame(int *minXOut, int *minYOut, int *maxXOut, int *maxYOut, int x0, int y0, int x1, int y1, int *clippedLeftX, int *clippedTopY, int *clippedRightX, int *clippedBottomY) {
+       int xMin = x0, xMax = x1;
+       int yMin = y0, yMax = y1;
+
+       if (x1 < x0) {
+               xMin = x1;
+               xMax = x0;
+       }
+
+       if (y1 < y0) {
+               yMin = y1;
+               yMax = y0;
+       }
+
+       int xMinOut = xMin - cameraX, xMaxOut = xMax - cameraX;
+       int yMinOut = yMin - cameraY, yMaxOut = yMax - cameraY;
+       
+       if (xMaxOut < 0 || yMaxOut < 0 || xMinOut >= GFX_WIDTH || yMinOut >= GFX_HEIGHT) {
+               return false;
+       }
+
+       int safeXMinOut = bzMax(0,              xMinOut);
+       int safeXMaxOut = bzMin(GFX_WIDTH - 1,  xMaxOut);
+       int safeYMinOut = bzMax(0,              yMinOut);
+       int safeYMaxOut = bzMin(GFX_HEIGHT - 1, yMaxOut);
+
+       *minXOut = safeXMinOut;
+       *minYOut = safeYMinOut;
+       *maxXOut = safeXMaxOut;
+       *maxYOut = safeYMaxOut;
+
+       if (clippedLeftX != NULL) {
+               *clippedLeftX = safeXMinOut - xMinOut;
+               *clippedTopY  = safeYMinOut - yMinOut;
+
+               *clippedRightX  = xMaxOut - safeXMaxOut;
+               *clippedBottomY = yMaxOut - safeYMaxOut;
+       }
+
+       return true;
+}
+
+static bool prepareColor(int *colorOut, int color) {
+       int c = (color <= 15) ? palettes[0][color] : color;
+       if (c > 0) {
+               *colorOut = c;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+void bzPSet(int x, int y, int c) {
+       if (c == 0) return;
+
+       int ox = x - cameraX;
+       int oy = y - cameraY;
+       if (ox >= 0 && ox < GFX_WIDTH && oy >=0 && oy < GFX_HEIGHT) {
+               if(fillPattern[y%4][x%4] == false) bzBufferSet(currentBuffer, ox, oy, (c <= 15) ? palettes[0][c] : c); // FIXME
+       }
+}
+
+int bzSGet(int x, int y) {
+       if (x >= 0 && x < SS_WIDTH && y >=0 && y < SS_HEIGHT) {
+               return spritesheet[y][x];
+       } else {
+               return 0;
+       }
+}
+
+//void bzSSet(int x, int y, int c);
+
+//bool bzFGet(int n, int f);
+//void bzFSet(int n, int f, bool v);
+
+void bzCls(uint8_t c) {
+
+       //memset(&buffer[currentBuffer], c, BUFFER_WIDTH * GFX_HEIGHT);
+
+//     for (size_t i = 0; i < )
+
+       for (size_t y = 0; y < GFX_HEIGHT; ++y) {
+               for (size_t x = 0; x < GFX_WIDTH; ++x) {
+                       bzBufferSet(currentBuffer, x, y, c);
+               }
+       }
+
+       //bzCamera(0, 0);
+       bzFillP(0x0000);
+}
+
+void bzGetCamera(int *x, int *y) {
+       if (x != NULL) *x = cameraX;
+       if (y != NULL) *y = cameraY;
+}
+
+void bzCamera(int x, int y) {
+       cameraX = x;
+       cameraY = y;
+}
+
+void bzCirc(int xm, int ym, int r, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, xm - r, ym - r, xm + r, ym + r, NULL, NULL, NULL, NULL)) {
+               xm -= cameraX;
+               ym -= cameraY;
+               
+               int x = -r;
+               int y = 0;
+               int err = 2 - 2 * r;
+
+               do {
+                       bzPSet(xm-x, ym+y, cOut);
+                       bzPSet(xm-y, ym-x, cOut);
+                       bzPSet(xm+x, ym-y, cOut);
+                       bzPSet(xm+y, ym+x, cOut);
+
+                       r = err;
+
+                       if (r <= y) err += (++y << 1) + 1;
+                       if (r > x || err > y) err += (++x << 1) + 1;
+               } while (x < 0);
+       }
+}
+
+void bzCircFill(int xm, int ym, int r, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, xm - r, ym - r, xm + r, ym + r, NULL, NULL, NULL, NULL)) {
+               xm -= cameraX;
+               ym -= cameraY;
+               
+               int x = -r;
+               int y = 0;
+               int err = 2 - 2 * r;
+
+               do {
+                       int fy1 = ym - bzAbs(y);
+                       int fy2 = ym + bzAbs(y);
+                       int fx  = bzMax(xm - bzAbs(x), 0);
+                       int tx  = bzMin(xm + bzAbs(x), GFX_WIDTH - 1); // FIXME, not as many abs
+
+                       bool validFy1 = fy1 >= 0 && fy1 < GFX_HEIGHT;
+                       bool validFy2 = fy2 >= 0 && fy2 < GFX_HEIGHT;
+
+                       if (validFy1 && validFy2) {
+                               for (int ox = fx; ox <= tx; ++ox) {
+                                       bzBufferSet(currentBuffer, ox, fy1, cOut);
+                                       bzBufferSet(currentBuffer, ox, fy2, cOut);
+                               }
+                       } else if (validFy1) {
+                               for (int ox = fx; ox <= tx; ++ox) {
+                                       bzBufferSet(currentBuffer, ox, fy1, cOut);
+                               }
+                       } else if (validFy2) {
+                               for (int ox = fx; ox <= tx; ++ox) {
+                                       bzBufferSet(currentBuffer, ox, fy2, cOut);
+                               }
+                       }
+
+                       r = err;
+
+                       if (r <= y) err += (++y << 1) + 1;
+                       if (r > x || err > y) err += (++x << 1) + 1;
+               } while (x < 0);
+       }
+}
+
+//void bzOval(int x0, int y0, int x1, int y1, int c);
+//void bzOvalFill(int x0, int y0, int x1, int y1, int c);
+
+void bzLine(int x0, int y0, int x1, int y1, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, x0, y0, x1, y1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               x0 = x0 - cameraX;
+               y0 = y0 - cameraY;
+               x1 = x1 - cameraX;
+               y1 = y1 - cameraY;
+
+               int deltaX = bzAbs(x1 - x0);
+               int deltaY = -bzAbs(y1 - y0);
+               int signX = bzSgn(x1 - x0);
+               int signY = bzSgn(y1 - y0);
+               int err = deltaX + deltaY;
+               int e2;
+
+               int x = x0, y = y0;
+
+               for (;;) {
+                       if (x >= 0 && x < GFX_WIDTH && y >=0 && y < GFX_HEIGHT) { // FIXME, easier way to offset this...
+                               bzBufferSet(currentBuffer, x, y, cOut);
+                       }
+
+                       e2 = err << 1;
+
+                       if (e2 >= deltaY) {
+                               if (x == x1) break;
+                               err += deltaY;
+                               x += signX;
+                       }
+
+                       if (e2 <= deltaX) {
+                               if (y == y1) break;
+                               err += deltaX;
+                               y += signY;
+                       }
+               }
+       }
+}
+
+void bzRect(int x0, int y0, int x1, int y1, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, x0, y0, x1, y1, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               if (clipLeft == 0 && clipRight == 0) {
+                       for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                               bzBufferSet(currentBuffer, xMinOut, oy, cOut);
+                               bzBufferSet(currentBuffer, xMaxOut, oy, cOut);
+                       }
+               } else if (clipLeft == 0) {
+                       for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                               bzBufferSet(currentBuffer, xMinOut, oy, cOut);
+                       }
+               } else if (clipRight == 0) {
+                       for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                               bzBufferSet(currentBuffer, xMaxOut, oy, cOut);
+                       }
+               }
+
+               if (clipTop == 0 && clipBottom == 0) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, yMinOut, cOut);
+                               bzBufferSet(currentBuffer, ox, yMaxOut, cOut);
+                       }
+               } else if (clipTop == 0) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, yMinOut, cOut);
+                       }
+               } else if (clipBottom == 0) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, yMaxOut, cOut);
+                       }
+               }
+       }
+}
+
+void bzRectFill(int x0, int y0, int x1, int y1, int c) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut;
+       if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, x0, y0, x1, y1, NULL, NULL, NULL, NULL)) {
+               for (int oy = yMinOut; oy <= yMaxOut; ++oy) {
+                       for (int ox = xMinOut; ox <= xMaxOut; ++ox) {
+                               bzBufferSet(currentBuffer, ox, oy, cOut);
+                       }
+               }
+       }
+}
+
+void bzSpr(int n, int x, int y) {
+       bzSprExt(n, x, y, 1, 1, false, false);
+}
+
+void bzSprExt(int n, int x, int y, int w, int h, bool flipX, bool flipY) {
+       int px = (n % 16) * 8;
+       int py = (n / 16) * 8;
+       int pw = w * 8;
+       int ph = h * 8;
+       bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY);
+}
+
+void bzSSpr(int sx, int sy, int sw, int sh, int dx, int dy) {
+       bzSSprExt(sx, sy, sw, sh, dx, dy, sw, sh, false, false);
+}
+
+void bzSSprExt(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flipX, bool flipY) {
+       int xMinOut, yMinOut, xMaxOut, yMaxOut, clipLeft, clipTop, clipRight, clipBottom;
+       if (prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, dx, dy, dx+dw, dy+dh, &clipLeft, &clipTop, &clipRight, &clipBottom)) {
+               
+               for (size_t y = 0; y < sh - (clipTop + clipBottom); ++y) {
+                       for (size_t x = 0; x < sw - (clipLeft + clipRight); ++x) {
+                               int color = spritesheet[clipTop+sy+y][clipLeft+sx+x];
+                               if (color > 0) bzBufferSet(currentBuffer, xMinOut+x, yMinOut+y, color); // FIXME, scaled up and 0 check removal??
+                       }
+               }
+       }
+       
+       /*int fromX = 0;
+       int toX = sw;
+       int dX = 1;
+
+       if (flipX) {
+               fromX = toX - 1;
+               toX = -1;
+               dX = -1;
+       }
+
+       int fromY = 0;
+       int toY = sh;
+       int dY = 1;
+
+       if (flipY) {
+               fromY = toY - 1;
+               toY = -1;
+               dY = -1;
+       }
+
+       for (int rY = fromY, wY = dy; rY != toY; rY += dY, ++wY) {
+               for (int rX = fromX, wX = dx; rX != toX; rX += dX, ++wX) {
+                       bzPSet(wX, wY, bzSGet(sx + rX, sy + rY));
+               }
+       }*/
+}
+
+void bzFillP(int p) {
+       int f = 1;
+       for (int y = 3; y >= 0; --y) {
+               for (int x = 3; x >= 0; --x, f<<=1) {
+                       fillPattern[y][x] = (p & f) > 0;
+               }
+       }
+}
+
+
+
+void bzPrint(int x, int y, int color, const char *text) {
+       int currentX = x, currentY = y;
+       bzGfxRenderFont(bzPSet, &currentX, &currentY, currentFont, color, text);
+}
+
+void bzSetPaletteColor(int palette, int colorIdx, int color) {
+       palettes[palette][colorIdx] = color;
+}
+
+#define bzGeneratePattern(p11, p12, p13, p14, p21, p22, p23, p24, p31, p32, p33, p34, p41, p42, p43, p44) { {p11, p12, p13, p14}, {p21, p22, p23, p24}, {p31, p32, p33, p34}, {p41, p42, p43, p44} }
+
+//(r&0xFF) << 0 | (g&0xFF) << 8 | (b&0xFF) << 16 | (0xFF) << 24
+
+int patterns[8][4][4] = {
+       bzGeneratePattern(0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0),
+       bzGeneratePattern(1, 0, 0, 0,
+                         0, 0, 0, 1,
+                         0, 1, 0, 0,
+                         0, 0, 1, 0),
+       bzGeneratePattern(0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0),
+       bzGeneratePattern(0, 1, 0, 1,
+                         1, 1, 1, 1,
+                         0, 1, 0, 1,
+                         1, 1, 1, 1),
+       bzGeneratePattern(0, 0, 0, 0,
+                         1, 1, 0, 0,
+                         0, 0, 0, 0,
+                         0, 0, 0, 0),
+       bzGeneratePattern(0, 1, 0, 1,
+                         1, 0, 1, 0,
+                         0, 1, 0, 1,
+                         1, 0, 1, 0),
+       bzGeneratePattern(1, 1, 1, 1,
+                         1, 1, 1, 1,
+                         1, 1, 1, 1,
+                         1, 1, 1, 1),
+};
+
+uint8_t patternLookup[] = {
+       0,
+       0,
+       3,
+       1,
+       5,
+       5,
+       3,
+       6,
+       2,
+       5,
+       2,
+       2,
+       2,
+       5,
+       2,
+       2,
+       1,
+
+       
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1,
+       1
+};
+
+
+
+
+// The real memory seems stupid slow??
+
+//#define kScreenMem 3120
+//static uint32_t *fakeScreen = NULL;//[3120];
+
+static void gfxRender(uint32_t *output, size_t pixelWidth, size_t pixelHeight) {
+//     if (fakeScreen == NULL) {
+//             fakeScreen = malloc(kScreenMem * sizeof(uint32_t));
+//     }
+
+       // FIXME, this one is very very slow.
+
+       bzPerfTimerStart("game.blit.composite");
+
+       uint8_t *p = &buffer[0][0][0];
+
+       for (size_t y = 0; y < GFX_HEIGHT; ++y) {
+               for (size_t x = 0; x < GFX_WIDTH; ++x) {
+//                     bzAssertMessage(buffer[1][y][x] == 1, "Got: %d", buffer[1][y][x]);
+
+                       p[7] = palettes[p[1] + 1][p[0]];
+                       p+=8*sizeof(uint8_t);
+
+//                     uint8_t cIdx = palettes[buffer[y][x][1] + 1][buffer[y][x][0]]; ////////// (palettes[buffer[y][x][1] + 1][buffer[y][x][0]] & buffer[y][x][2]); //  PALETTE, return this    (buffer[1][y][x]) + 1
+//                     if (buffer[y][x][2] == 0) cIdx = 0;
+//                     if (buffer[y][x][3] != 0) cIdx = buffer[y][x][3];
+//                     bzBufferSet(7, x, y, cIdx);
+
+//                     if (buffer[2][y][x] == 0) {
+//                             cIdx = 0;
+//                     }
+//                     if (buffer[3][y][x] > 0) {
+//                             cIdx = buffer[3][y][x];
+//                     }
+//                     intermediateBuffer[y][x] = cIdx;
+                       //intermediateBuffer[y][x] = paletteColors[cIdx];
+               }
+       }
+
+       bzPerfTimerStop("game.blit.composite");
+
+       bzPerfTimerStart("game.blit.transfer");
+
+//     uint32_t *pixels = output;
+       uint32_t *pixels = (uint32_t *)output;//(uint32_t *)output;
+       //int idx = 0;
+       uint32_t value = 0;
+       for (size_t y = 0; y < GFX_HEIGHT * pixelHeight; ++y) {
+               //idx = y * 13;
+               int patY = y % 4;
+               //int patX = 0;
+               uint8_t ly = y >> 1;
+               uint8_t lx = 0;
+               for (size_t x = 0; x < GFX_WIDTH * pixelWidth; ) {
+                       //value = 0;
+                       int target = bzMin(GFX_WIDTH * pixelWidth - x, 32);
+                       for (int i = 0; i < target; ++i, ++x) {
+                               uint8_t c = buffer[ly /*/ pixelHeight*/][lx /*/ pixelWidth*/][7];
+                               uint8_t patternIdx = patternLookup[c];
+                               //bzAssertMessage(patternIdx <= 7, "Pattern index %d at %d,%d %d,%d", patternIdx, x, y, x / pixelWidth, y / pixelHeight);
+                               uint8_t bit = patterns[patternIdx][patY][x & 0b11 /*% 4*/];
+                               value = (value << 1) | bit ;// | patterns[patternIdx][patY][x % 4]; //(patterns[patternIdx][patY][x % 4] * 0b10000000000000000000000000000000);//x % 4 -> patX FIXME
+                               lx += x&1;
+                       }
+//                     *pixels = swap(value);
+                       __asm volatile ("rev %0, %1" : "=l" (*pixels) : "l" (value));
+                       pixels++;// += 4;//sizeof(uint32_t);
+               }
+               //pixels += 2; // stride...
+       }
+
+       bzPerfTimerStop("game.blit.transfer");
+
+//     memcpy(output, fakeScreen, kScreenMem * sizeof(uint32_t));
+}
+
+void bzSetOutputBuffer(int idx) {
+       currentBuffer = idx;
+}
+
+#endif
diff --git a/src_platform/sdl/bz/gfx/gfx_platform.h b/src_platform/sdl/bz/gfx/gfx_platform.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src_platform/sdl/bz/input/input.c b/src_platform/sdl/bz/input/input.c
new file mode 100644 (file)
index 0000000..24186ef
--- /dev/null
@@ -0,0 +1,139 @@
+#include <bz/input/input_internal.h>
+#include <bz/input/platform.h>
+
+#include <bz/input/input_id_internal.h>
+#include <SDL.h>
+
+typedef enum BZInputCode {
+       BZInputCodeQuit = 1,
+} BZInputCode;
+const BZInputCode BZInputCodeMax = BZInputCodeQuit;
+
+typedef union BZInputTableEntry {
+       bool booleanValue;
+       int integerValue;
+       float floatValue;
+} BZInputTableEntry;
+
+static BZInput bzQuitInput = { .id = BZInputCodeQuit, };
+BZInputID bzQuitInputID = &bzQuitInput;
+
+const size_t inputTableSize = BZInputCodeMax + 1;
+static BZInputTableEntry inputTable[inputTableSize];
+
+typedef struct {
+       bool down;
+       bool pressed;
+} Button;
+
+#define kButtonCount 12
+
+static Button buttons[kButtonCount];
+
+static SDL_Joystick *joy;
+
+int buttonIdx(SDL_KeyCode keyCode) {
+       switch (keyCode) {
+               case SDLK_UP:
+                       return 1;
+
+               case SDLK_DOWN:
+                       return 2;
+
+               case SDLK_LEFT:
+                       return 3;
+
+               case SDLK_RIGHT:
+                       return 4;
+
+               case 'z':
+                       return 5;
+
+               case 'x':
+                       return 6;
+
+               case 'w':
+                       return 7;
+
+               case 's':
+                       return 8;
+
+               case 'a':
+                       return 9;
+
+               case 'd':
+                       return 10;
+
+               default:
+                       return 0;
+       }
+}
+
+void bzInputInit() {
+       for (size_t i = 0; i < kButtonCount; ++i) {
+               buttons[i].down = false;
+       }
+
+       for (size_t i = 0, c = SDL_NumJoysticks(); i < c; ++i) {
+               bzLog("Joystick: %s", SDL_JoystickName(i));
+
+       }
+
+       if (SDL_NumJoysticks() > 0) {
+               joy = SDL_JoystickOpen(0);
+       }
+}
+
+void bzInputTeardown() {
+
+}
+
+void bzInputProcessFrame() {
+       for (size_t i = 0; i < inputTableSize; ++i) {
+               inputTable[i].integerValue = 0; // FIXME, max size and whatever.
+       }
+
+       for (size_t i = 0; i < kButtonCount; ++i) {
+               buttons[i].down = buttons[i].down || buttons[i].pressed;
+               buttons[i].pressed = false;
+       }
+
+       SDL_Event event;
+       while (SDL_PollEvent(&event)) {
+               switch (event.type) {
+                       case SDL_QUIT:
+                               inputTable[BZInputCodeQuit].booleanValue = true;
+                               break;
+
+                       case SDL_KEYDOWN:
+                               buttons[buttonIdx(event.key.keysym.sym)].pressed = true;
+                               break;
+
+                       case SDL_KEYUP:
+                               buttons[buttonIdx(event.key.keysym.sym)].pressed = false;
+                               buttons[buttonIdx(event.key.keysym.sym)].down = false;
+                               break;
+               }
+       }
+}
+
+bool bzInputGetBooleanValue(BZInputID inputID) {
+       return inputTable[inputID->id].booleanValue;
+}
+
+int bzInputGetIntegerValue(BZInputID inputID) {
+       return inputTable[inputID->id].integerValue;
+}
+
+float bzInputGetFloatValue(BZInputID inputID) {
+       return inputTable[inputID->id].floatValue;
+}
+
+bool bzInputBtn(int id) {
+       return buttons[id].down || buttons[id].pressed;
+}
+
+bool bzInputBtnP(int id) {
+       return buttons[id].pressed;
+}
+
diff --git a/src_platform/sdl/bz/memory/arena.c b/src_platform/sdl/bz/memory/arena.c
new file mode 100644 (file)
index 0000000..c129c0c
--- /dev/null
@@ -0,0 +1,11 @@
+#include <bz/memory/arena_internal.h>
+
+#include <stdlib.h>
+
+void *bzSystemAllocate(size_t size) {
+       return malloc(size);
+}
+
+void *bzSystemAllocateStack(size_t size) {
+       return alloca(size);
+}
diff --git a/src_platform/sdl/bz/renderer/renderer.c b/src_platform/sdl/bz/renderer/renderer.c
new file mode 100644 (file)
index 0000000..916e73d
--- /dev/null
@@ -0,0 +1,140 @@
+#include <bz/renderer/renderer_internal.h>
+
+#include <bz/gfx/gfx_internal.h>
+#include <bz/math/math.h>
+#include <bz/memory/allocator.h>
+#include <bz/renderer/palette_internal.h>
+#include <bz/renderer/render_pass_internal.h>
+#include <SDL.h>
+
+struct BZRendererPass {
+       size_t idx;
+       BZRenderPassID pass;
+};
+
+const static size_t kMaxRenderPasses = 8;
+
+struct BZRenderer {
+       BZRendererConfiguration configuration;
+       SDL_Window *window;
+       SDL_Renderer *renderer;
+       SDL_Texture *renderTexture;
+       SDL_Texture *renderTextureIntermediate;
+       size_t intermediateWidth, intermediateHeight;
+       int outputWidth, outputHeight;
+       //static size_t numRenderPasses = 0;
+       BZRendererPass rendererPasses[kMaxRenderPasses];
+};
+
+BZRendererID bzRendererInit(BZMemoryArenaID arena, const BZRendererConfiguration *configuration, const char *title) {
+       BZRendererID renderer = (BZRenderer *)bzMemoryAlloc(arena, sizeof(BZRenderer));
+
+       renderer->configuration = *configuration;
+
+       renderer->window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, configuration->outputWidth, configuration->outputHeight, SDL_WINDOW_ALLOW_HIGHDPI);
+       if (renderer->window == NULL) {
+               bzError("Could not make window: SDL_Error='%s'", SDL_GetError());
+               return NULL;
+       }
+
+       renderer->renderer = SDL_CreateRenderer(renderer->window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED/*SDL_RENDERER_ACCELERATED*/);
+       
+       //SDL_Surface *windowSurface =  SDL_GetWindowSurface(window);
+       SDL_GetWindowSizeInPixels(renderer->window, &renderer->outputWidth, &renderer->outputHeight);
+
+       renderer->renderTexture = SDL_CreateTexture(renderer->renderer, SDL_PIXELFORMAT_ABGR8888/*SDL_PIXELFORMAT_RBZA8888*/, SDL_TEXTUREACCESS_STREAMING, configuration->rendererWidth, configuration->rendererHeight);
+
+       renderer->intermediateWidth = configuration->rendererWidth * bzFloor((renderer->outputWidth / configuration->rendererWidth) + 1);
+       renderer->intermediateHeight = configuration->rendererHeight * bzFloor((renderer->outputHeight / configuration->rendererHeight) + 1);
+       renderer->renderTextureIntermediate = SDL_CreateTexture(renderer->renderer, SDL_PIXELFORMAT_ABGR8888/*SDL_PIXELFORMAT_RBZA8888*/, SDL_TEXTUREACCESS_TARGET, renderer->intermediateWidth, renderer->intermediateHeight);
+       SDL_SetTextureScaleMode(renderer->renderTextureIntermediate, SDL_ScaleModeBest);
+
+//     drawSurface/*tmpSurface*/ = SDL_CreateRBZSurface(0, RENDERER_WIDTH, RENDERER_HEIGHT, windowSurface->format->BytesPerPixel * 8, windowSurface->format->Rmask, windowSurface->format->Gmask, windowSurface->format->Bmask, windowSurface->format->Amask);
+       //drawSurface = SDL_CreateRBZSurface(0, RENDERER_WIDTH, RENDERER_HEIGHT, 8, 0, 0, 0, 0);
+
+       for (size_t i = 0; i < kMaxRenderPasses; ++i) {
+               renderer->rendererPasses[i].pass = NULL;
+       }
+
+       SDL_ShowCursor(SDL_DISABLE);
+
+       bzGfxPreparePalettes(arena, 8, configuration->palette->numColors);
+       bzGfxPrepareCanvasBuffer(arena, configuration->gameWidth, configuration->gameHeight);
+
+       return renderer;
+}
+
+void bzRendererTeardown(BZRendererID renderer) {
+       SDL_DestroyWindow(renderer->window);
+}
+
+BZRendererPassID bzRendererRegisterPass(BZRendererID renderer, BZRenderPassID renderPass) {
+       for (size_t i = 0; i < kMaxRenderPasses; ++i) {
+               if (renderer->rendererPasses[i].pass == NULL) {
+                       renderer->rendererPasses[i].pass = renderPass;
+                       return &renderer->rendererPasses[i];
+               }
+       }
+       return NULL;
+}
+
+void bzRendererUnregisterPass(BZRendererPassID rendererPass) {
+       rendererPass->pass = NULL;
+}
+
+void bzRendererBlit(BZRendererID renderer) {
+       //SDL_LockSurface(drawSurface);
+
+       //SDL_Rect srcRect = { .x = 0, .y = 0, .w = drawSurface->w,   .h = drawSurface->h };
+       void *pixels;
+       int pitch;
+
+       SDL_LockTexture(renderer->renderTexture, NULL, &pixels, &pitch);
+
+       for (size_t i = 0; i < kMaxRenderPasses; ++i) {
+               BZRenderPass *renderPass = renderer->rendererPasses[i].pass;
+               if (renderPass != NULL) {
+                       if (renderPass->softwareHook != NULL) {
+                               renderPass->softwareHook(renderer->configuration.palette, (uint32_t *)pixels/*drawSurface->pixels*/, renderer->configuration.gameWidth, renderer->configuration.gameHeight, renderer->configuration.rendererWidth / renderer->configuration.gameWidth, renderer->configuration.rendererHeight / renderer->configuration.gameHeight);
+                       }
+               }
+       }
+
+       //SDL_UnlockSurface(drawSurface);
+
+       SDL_UnlockTexture(renderer->renderTexture);
+
+       //SDL_Rect srcRect = { .x = 0, .y = 0, .w = drawSurface->w,   .h = drawSurface->h };
+       //SDL_Rect dstRect = { .x = 0, .y = 0, .w = windowSurface->w, .h = windowSurface->h};
+       //SDL_FillRect(windowSurface, &dstRect, 100);
+//     int zis = SDL_BlitSurface(drawSurface, &srcRect, tmpSurface, &srcRect);
+//     int zi = SDL_BlitScaled(drawSurface, &srcRect, windowSurface, &dstRect);
+
+       //SDL_SetRenderTarget(renderer, NULL);
+       //SDL_RenderClear(renderer);
+
+       SDL_SetRenderTarget(renderer->renderer, renderer->renderTextureIntermediate);
+       //renderTextureIntermediate
+       SDL_RenderCopyEx(renderer->renderer, renderer->renderTexture, NULL, NULL, 0, NULL, 0);
+       SDL_SetRenderTarget(renderer->renderer, NULL);
+
+
+       //SDL_RenderCopyEx(renderer, renderTextureIntermediate, NULL, NULL, 0, NULL, 0);
+
+
+//     SDL_RenderPresent(renderer);
+       //SDL_RenderPresent(renderer);
+       //SDL_SetRenderTarget(renderer, renderTexture);
+
+       //SDL_UpdateWindowSurface(window);
+       //SDL_UpdateWindowSurface(window);
+       //SDL_UpdateWindowSurface(window);
+}
+
+void bzRendererVBlank(BZRendererID renderer) {
+       SDL_RenderClear(renderer->renderer);
+       size_t height = (size_t)((float)renderer->outputWidth * (float)renderer->configuration.rendererHeight / (float)renderer->configuration.rendererWidth);
+       SDL_Rect dstRect = { .x = 0, .y = (renderer->outputHeight - height) / 2, .w = renderer->outputWidth, .h = height };
+       SDL_RenderCopyEx(renderer->renderer, renderer->renderTextureIntermediate, NULL, &dstRect, 0, NULL, 0);
+       SDL_RenderPresent(renderer->renderer);
+}
diff --git a/src_platform/sdl/bz/resources/file_system_internal.h b/src_platform/sdl/bz/resources/file_system_internal.h
new file mode 100644 (file)
index 0000000..8ccf7d8
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef BZ_RESOURCES_FILE_SYSTEM_INTERNAL_H
+#define BZ_RESOURCES_FILE_SYSTEM_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void bzFileSystemInit(const char *argv0, const char *dataPath);
+extern void bzFileSystemTeardown(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src_platform/sdl/bz/resources/resource.c b/src_platform/sdl/bz/resources/resource.c
new file mode 100644 (file)
index 0000000..d5a46fb
--- /dev/null
@@ -0,0 +1,74 @@
+#include <bz/resources/resource.h>
+
+#include <bz/types/identifier_internal.h>
+#include <stb_sprintf.h>
+#include <physfs.h>
+#include <stdlib.h>
+
+struct BZResource { PHYSFS_File unused; };
+
+// This is FOR DEBUG ONLY!
+char *gBZDataDebugDirectory = NULL;
+
+void bzFileSystemInit(const char *argv0, const char *dataPath) {
+       PHYSFS_init(argv0);
+       
+       if (gBZDataDebugDirectory != NULL) {
+               bzLog("!!! A debug data directory was found !!!");
+               bzLog("!!! DO NOT SHIP IF THIS IS DISPLAYED !!!");
+               int fsr3 = PHYSFS_mount(gBZDataDebugDirectory, NULL, 0);
+               bzAssert(fsr3 != 0);
+               bzLog("Trying to load: %s", gBZDataDebugDirectory);
+       } else {
+               char tmp[1024];
+               stbsp_snprintf(tmp, 1024, "%s%s", dataPath, "assets.pak");
+               int fsr2 = PHYSFS_mount(tmp, NULL, 0);
+               bzAssert(fsr2 != 0);
+               bzLog("Trying to load: %s", tmp);
+       }
+}
+
+void bzFileSystemTeardown() {
+       PHYSFS_deinit();
+}
+
+BZResourceID bzResourcesOpenResource(const char *type, const char *identifierFmt, ...) {
+       bzMakeIdentifier(identifier, identifierFmt);
+       bzLog("Trying to load: %s", identifier);
+
+       BZResourceID resource = (BZResourceID)PHYSFS_openRead(identifier);
+       bzAssertMessage(resource != NULL, "Error opening resource '%s': %s", identifier, PHYSFS_getLastError());
+
+       return resource;
+}
+
+extern void bzResourcesCloseResource(BZResourceID resource) {
+       PHYSFS_File *handle = (PHYSFS_File *)resource;
+       PHYSFS_close(handle);
+}
+
+size_t bzResourcesFileLength(BZResourceID resource) {
+       PHYSFS_File *handle = (PHYSFS_File *)resource;
+       return PHYSFS_fileLength(handle);
+}
+
+size_t bzResourcesTell(BZResourceID resource) {
+       PHYSFS_File *handle = (PHYSFS_File *)resource;
+       return PHYSFS_tell(handle);
+}
+
+size_t bzResourcesSeek(BZResourceID resource, size_t position) {
+       PHYSFS_File *handle = (PHYSFS_File *)resource;
+       return PHYSFS_seek(handle, position);
+}
+
+size_t bzResourcesReadBytes(BZResourceID resource, void *outputBuffer, size_t numBytes) {
+       PHYSFS_File *handle = (PHYSFS_File *)resource;
+       PHYSFS_sint64 r = PHYSFS_readBytes(handle, outputBuffer, numBytes);
+       if (r == -1) {
+               // FIXME, handle error?
+               return 0;
+       } else {
+               return (size_t)r;
+       }
+}
diff --git a/src_platform/sdl/main.c b/src_platform/sdl/main.c
new file mode 100644 (file)
index 0000000..b821d97
--- /dev/null
@@ -0,0 +1,55 @@
+#include <SDL.h>
+
+#include <bz/debug/log_internal.h>
+#include <bz/debug/perfgraph_internal.h>
+#include <bz/game/scene_internal.h>
+#include <bz/input/input_internal.h>
+#include <bz/input/platform.h>
+#include <bz/renderer/renderer.h>
+#include <bz/resources/file_system_internal.h>
+
+extern void bzpGameLaunch(void);
+extern void bzpGameInit(void);
+extern bool bzpGameTick(void);
+extern void bzpGameTeardown(void);
+
+int main(int argc, char *argv[]) {
+       int returnCode = 0;
+
+       bzpGameLaunch();
+
+       gSystemTicksPerSecond = 60;
+
+       SDL_Init(SDL_INIT_EVERYTHING);
+       bzFileSystemInit(argv[0], SDL_GetBasePath());
+       bzInputInit();
+
+       bzpGameInit();
+
+       //uint64_t targetUpdateTime = 1000 / 60;
+       //uint64_t nextUpdateTicks = 0;
+
+       for (;;) {
+//             bzInputProcessFrame();
+
+               // FIXME, input stuff...
+
+               //uint64_t ticks = SDL_GetTicks64();
+
+               //if (ticks >= nextUpdateTicks) {
+               //      nextUpdateTicks = ticks + targetUpdateTime;
+                       bzPerfReset();
+                       if (bzpGameTick() == false) goto game_loop_exit; // FIXME
+                       if (bzInputGetBooleanValue(bzQuitInputID)) goto game_loop_exit; // FIXME
+               //}
+       }
+game_loop_exit:
+
+       bzpGameTeardown();
+       bzInputTeardown();
+       bzFileSystemTeardown();
+       bzLogTeardown();
+       SDL_Quit();
+       
+       return returnCode;
+}
diff --git a/third_party/parson b/third_party/parson
new file mode 160000 (submodule)
index 0000000..ba29f4e
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit ba29f4eda9ea7703a9f6a9cf2b0532a2605723c3
diff --git a/third_party/pcg-c-basic b/third_party/pcg-c-basic
new file mode 160000 (submodule)
index 0000000..bc39cd7
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit bc39cd76ac3d541e618606bcc6e1e5ba5e5e6aa3
diff --git a/third_party/physfs b/third_party/physfs
new file mode 160000 (submodule)
index 0000000..d1ca6e1
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit d1ca6e14aa904363706a0f5712d8b8e6e13c5fa6
diff --git a/third_party/sdl b/third_party/sdl
new file mode 160000 (submodule)
index 0000000..efaa587
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit efaa58732abb3faf3900b2d93a166b955fbd8ed0
diff --git a/third_party/snippets_org_crc/crc.c b/third_party/snippets_org_crc/crc.c
new file mode 100644 (file)
index 0000000..531e837
--- /dev/null
@@ -0,0 +1,126 @@
+/* Crc - 32 BIT ANSI X3.66 CRC checksum files */
+
+#include "crc.h"
+
+/**********************************************************************\
+|* Demonstration program to compute the 32-bit CRC used as the frame  *|
+|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71     *|
+|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level     *|
+|* protocol).  The 32-bit FCS was added via the Federal Register,     *|
+|* 1 June 1982, p.23798.  I presume but don't know for certain that   *|
+|* this polynomial is or will be included in CCITT V.41, which        *|
+|* defines the 16-bit CRC (often called CRC-CCITT) polynomial.  FIPS  *|
+|* PUB 78 says that the 32-bit FCS reduces otherwise undetected       *|
+|* errors by a factor of 10^-5 over 16-bit FCS.                       *|
+\**********************************************************************/
+
+/* Copyright (C) 1986 Gary S. Brown.  You may use this program, or
+   code or tables extracted from it, as desired without restriction.*/
+
+/* First, the polynomial itself and its table of feedback terms.  The  */
+/* polynomial is                                                       */
+/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+/* Note that we take it "backwards" and put the highest-order term in  */
+/* the lowest-order bit.  The X^32 term is "implied"; the LSB is the   */
+/* X^31 term, etc.  The X^0 term (usually shown as "+1") results in    */
+/* the MSB being 1.                                                    */
+
+/* Note that the usual hardware shift register implementation, which   */
+/* is what we're using (we're merely optimizing it by doing eight-bit  */
+/* chunks at a time) shifts bits into the lowest-order term.  In our   */
+/* implementation, that means shifting towards the right.  Why do we   */
+/* do it this way?  Because the calculated CRC must be transmitted in  */
+/* order from highest-order term to lowest-order term.  UARTs transmit */
+/* characters in order from LSB to MSB.  By storing the CRC this way,  */
+/* we hand it to the UART in the order low-byte to high-byte; the UART */
+/* sends each low-bit to hight-bit; and the result is transmission bit */
+/* by bit from highest- to lowest-order term without requiring any bit */
+/* shuffling on our part.  Reception works similarly.                  */
+
+/* The feedback terms table consists of 256, 32-bit entries.  Notes:   */
+/*                                                                     */
+/*  1. The table can be generated at runtime if desired; code to do so */
+/*     is shown later.  It might not be obvious, but the feedback      */
+/*     terms simply represent the results of eight shift/xor opera-    */
+/*     tions for all combinations of data and CRC register values.     */
+/*                                                                     */
+/*  2. The CRC accumulation logic is the same for all CRC polynomials, */
+/*     be they sixteen or thirty-two bits wide.  You simply choose the */
+/*     appropriate table.  Alternatively, because the table can be     */
+/*     generated at runtime, you can start by generating the table for */
+/*     the polynomial in question and use exactly the same "updcrc",   */
+/*     if your application needn't simultaneously handle two CRC       */
+/*     polynomials.  (Note, however, that XMODEM is strange.)          */
+/*                                                                     */
+/*  3. For 16-bit CRCs, the table entries need be only 16 bits wide;   */
+/*     of course, 32-bit entries work OK if the high 16 bits are zero. */
+/*                                                                     */
+/*  4. The values must be right-shifted by eight bits by the "updcrc"  */
+/*     logic; the shift must be unsigned (bring in zeroes).  On some   */
+/*     hardware you could probably optimize the shift in assembler by  */
+/*     using byte-swap instructions.                                   */
+
+static uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
+0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8))
+
+uint32_t crc32buf(const char *buf, size_t len)
+{
+      register uint32_t oldcrc32;
+
+      oldcrc32 = 0xFFFFFFFF;
+
+      for ( ; len; --len, ++buf)
+      {
+            oldcrc32 = UPDC32(*buf, oldcrc32);
+      }
+
+      return ~oldcrc32;
+      
+}
+
+// Adapted from http://web.archive.org/web/20080303102530/http://c.snippets.org/snip_lister.php?fname=crc_32.c
diff --git a/third_party/snippets_org_crc/crc.h b/third_party/snippets_org_crc/crc.h
new file mode 100644 (file)
index 0000000..f818654
--- /dev/null
@@ -0,0 +1,22 @@
+
+/*
+**  CRC.H - header file for SNIPPETS CRC and checksum functions
+*/
+
+#ifndef CRC__H
+#define CRC__H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint32_t crc32buf(const char *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CRC__H */
\ No newline at end of file
diff --git a/third_party/soloud_speech/Elements.def b/third_party/soloud_speech/Elements.def
new file mode 100644 (file)
index 0000000..cdeabbd
--- /dev/null
@@ -0,0 +1,1659 @@
+/*mName, mRK, mDU, mUD, mFont, mDict, mIpa, mFeat, interpolators*/
+/* (mSteady, mFixed, mProportion, mExtDelay, mIntDelay) */
+{"END", 31, 5, 5,0x00,NULL,NULL,0,
+ {
+  {   270,    135,  50,  3,  3}, /* ELM_FN       0 */
+  {   490,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {  1480,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2500,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_AN   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A1   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A2   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A3   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A4   -10.5 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"Q",   29, 6, 6,0x00,NULL,NULL,0,
+ {
+  {   270,    135,  50,  3,  3}, /* ELM_FN       0 */
+  {   490,      0, 100,  3,  3}, /* ELM_F1       0 */
+  {  1480,      0, 100,  3,  3}, /* ELM_F2       0 */
+  {  2500,      0, 100,  3,  3}, /* ELM_F3       0 */
+  {    60,      0, 100,  3,  3}, /* ELM_B1       0 */
+  {    90,      0, 100,  3,  3}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  3}, /* ELM_B3       0 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_AN   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A1   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A2   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A3   -10.5 */
+  {   -30,  -10.5, 100,  3,  0}, /* ELM_A4   -10.5 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"P",   23, 8, 8,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {   760,    350,  50,  2,  2}, /* ELM_F2     -30 */
+  {  2500,      0, 100,  0,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"PY",  29, 1, 1,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {   760,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2500,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {    49,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 43.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"PZ",  23, 2, 2,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {   760,    350,  50,  2,  2}, /* ELM_F2     -30 */
+  {  2500,      0, 100,  2,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  2,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 33.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"T",   23, 6, 6,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {  1780,    950,  50,  2,  2}, /* ELM_F2      60 */
+  {  2680,   2680,   0,  0,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,    150,   0,  0,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"TY",  29, 1, 1,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {  1780,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2680,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 50.75,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"TZ",  23, 2, 2,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  1}, /* ELM_F1      15 */
+  {  1780,    950,  50,  2,  1}, /* ELM_F2      60 */
+  {  2680,   2680,   0,  2,  0}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  1}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  1}, /* ELM_B2       0 */
+  {   150,    150,   0,  2,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"K",   23, 8, 8,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  3,  3}, /* ELM_F1      15 */
+  {  1480,   1550,  50,  3,  3}, /* ELM_F2     810 */
+  {  2620,   1580,  50,  3,  3}, /* ELM_F3     270 */
+  {    60,     30,  50,  3,  3}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  3}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  3}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"KY",  29, 1, 1,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {  1480,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2620,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 50.75,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 50.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"KZ",  23, 4, 4,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  3,  3}, /* ELM_F1      15 */
+  {  1480,   1550,  50,  3,  3}, /* ELM_F2     810 */
+  {  2620,   1580,  50,  3,  3}, /* ELM_F3     270 */
+  {    60,     30,  50,  3,  3}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  3}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  3}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 19.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"B",   26,12,12,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {   760,    350,  50,  2,  2}, /* ELM_F2     -30 */
+  {  2500,      0, 100,  0,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"BY",  29, 1, 1,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {   760,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2500,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {    49,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 43.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"BZ",  26, 0, 0,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  0}, /* ELM_F1      15 */
+  {   760,    350,  50,  2,  0}, /* ELM_F2     -30 */
+  {  2500,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  0}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"D",   26, 8, 8,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {  1780,    950,  50,  2,  2}, /* ELM_F2      60 */
+  {  2680,   2680,   0,  2,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,    150,   0,  2,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"DY",  29, 1, 1,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {  1780,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2680,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {    35,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {  45.5,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"DZ",  26, 1, 1,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  0}, /* ELM_F1      15 */
+  {  1780,    950,  50,  2,  0}, /* ELM_F2      60 */
+  {  2680,   2680,   0,  2,  0}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  0}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  0}, /* ELM_B2       0 */
+  {   150,    150,   0,  2,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  38.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    35,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+
+{"G",   26,12,12,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  3,  3}, /* ELM_F1      15 */
+  {  1480,   1550,  50,  3,  3}, /* ELM_F2     810 */
+  {  2620,   1580,  50,  3,  3}, /* ELM_F3     270 */
+  {    60,     30,  50,  3,  3}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  3}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  3}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {    35,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"GY",  29, 1, 1,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {  1480,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2620,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {    35,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  45.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"GZ",  26, 2, 2,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  3,  2}, /* ELM_F1      15 */
+  {  1480,   1550,  50,  3,  2}, /* ELM_F2     810 */
+  {  2620,   1580,  50,  3,  2}, /* ELM_F3     270 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {    35,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {    35,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    14,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"M",   15, 8, 8,0x6D,"m","m",ELM_FEATURE_BLB|ELM_FEATURE_NAS,
+ {
+  {   360,    360,   0,  3,  0}, /* ELM_FN       0 */
+  {   480,    480,   0,  3,  0}, /* ELM_F1       0 */
+  {  1000,    350,  50,  3,  0}, /* ELM_F2    -150 */
+  {  2200,      0, 100,  5,  0}, /* ELM_F3       0 */
+  {    40,     20,  50,  3,  0}, /* ELM_B1       0 */
+  {   175,     87,  50,  3,  0}, /* ELM_B2    -0.5 */
+  {   120,      0, 100,  5,  0}, /* ELM_B3       0 */
+  {    42,     21,  50,  3,  0}, /* ELM_AN       0 */
+  {    26,    -10, 100,  3,  0}, /* ELM_A1     -10 */
+  {    30,    -10, 100,  3,  0}, /* ELM_A2     -10 */
+  {    33,    -10, 100,  3,  0}, /* ELM_A3     -10 */
+  {   -30,    -10, 100,  3,  0}, /* ELM_A4     -10 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  2,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  2,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  2,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  2,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"N",   15, 8, 8,0x6E,"n","n",ELM_FEATURE_ALV|ELM_FEATURE_NAS,
+ {
+  {   450,    450,   0,  3,  0}, /* ELM_FN       0 */
+  {   480,    480,   0,  3,  0}, /* ELM_F1       0 */
+  {  1780,    950,  50,  3,  3}, /* ELM_F2      60 */
+  {  2620,   2680,   0,  3,  0}, /* ELM_F3      60 */
+  {    40,     20,  50,  3,  0}, /* ELM_B1       0 */
+  {   300,    150,  50,  3,  3}, /* ELM_B2       0 */
+  {   260,    130,  50,  3,  0}, /* ELM_B3       0 */
+  {    42,     21,  50,  3,  0}, /* ELM_AN       0 */
+  {    35,    -10, 100,  3,  0}, /* ELM_A1     -10 */
+  {    35,    -10, 100,  3,  0}, /* ELM_A2     -10 */
+  {    35,    -10, 100,  3,  0}, /* ELM_A3     -10 */
+  {    20,    -10, 100,  3,  0}, /* ELM_A4     -10 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  2,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  2,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  2,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  2,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"NG",  15, 8, 8,0x4E,"N","N",ELM_FEATURE_NAS|ELM_FEATURE_VEL,
+ {
+  {   360,    360,   0,  3,  0}, /* ELM_FN       0 */
+  {   480,    480,   0,  3,  0}, /* ELM_F1       0 */
+  {   820,   1550,  50,  5,  3}, /* ELM_F2    1140 */
+  {  2800,   1580,  50,  3,  3}, /* ELM_F3     180 */
+  {   160,     80,   0,  5,  0}, /* ELM_B1     -80 */
+  {   150,     75,  50,  5,  3}, /* ELM_B2       0 */
+  {   100,     50,  50,  3,  0}, /* ELM_B3       0 */
+  {    42,     21,  50,  3,  3}, /* ELM_AN       0 */
+  {    20,      0, 100,  3,  0}, /* ELM_A1       0 */
+  {    30,      0, 100,  3,  0}, /* ELM_A2       0 */
+  {    35,      0, 100,  3,  0}, /* ELM_A3       0 */
+  {     0,      0, 100,  3,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  3,  0}, /* ELM_AB       0 */
+  {    52,     26,  50,  2,  0}, /* ELM_AV       0 */
+  {    56,     28,  50,  2,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  2,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  2,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"F",   18,12,12,0x66,"f","f",ELM_FEATURE_FRC|ELM_FEATURE_LBD|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   400,    170,  50,  3,  2}, /* ELM_F1     -30 */
+  {  1420,    350,  50,  3,  2}, /* ELM_F2    -360 */
+  {  2560,    980,  50,  3,  2}, /* ELM_F3    -300 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {     0,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {     0,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {    54,     27,  50,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    32,     16,  50,  0,  0}, /* ELM_ASP      0 */
+  {    54,     30,  50,  0,  0}  /* ELM_AF       3 */
+ }
+},
+
+{"TH",  18,15,15,0x54,"T","T",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   400,    170,  50,  3,  2}, /* ELM_F1     -30 */
+  {  1780,   1190,  50,  3,  2}, /* ELM_F2     300 */
+  {  2680,   2680,   0,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,    150,   0,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 22.75,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"S",   18,12,12,0x73,"s","s",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   400,    170,  50,  3,  2}, /* ELM_F1     -30 */
+  {  1720,    950,  50,  3,  2}, /* ELM_F2      90 */
+  {  2620,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {   200,    100,  50,  3,  2}, /* ELM_B1       0 */
+  {    96,     48,  50,  3,  2}, /* ELM_B2       0 */
+  {   220,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    32,     16,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"SH",  18,12,12,0x53,"S","S",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   400,    170,  50,  3,  2}, /* ELM_F1     -30 */
+  {  2200,   1190,  50,  3,  2}, /* ELM_F2      90 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {    42,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"X",   18,12,12,0x78,"x","x",ELM_FEATURE_FRC|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  3,  3}, /* ELM_F1      15 */
+  {  1480,   1550,  50,  3,  3}, /* ELM_F2     810 */
+  {  2620,   1580,  50,  3,  3}, /* ELM_F3     270 */
+  {    60,     30,  50,  3,  3}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  3}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  3}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 19.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"H",    9,10,10,0x68,"h","h",ELM_FEATURE_APR|ELM_FEATURE_GLT,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,      0, 100,  0,  7}, /* ELM_F1       0 */
+  {  1480,      0, 100,  0,  7}, /* ELM_F2       0 */
+  {  2500,      0, 100,  0,  7}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  7}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  7}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  7}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  7}, /* ELM_AN       0 */
+  {    35,    -14, 100,  0,  7}, /* ELM_A1     -14 */
+  { 36.75,    -14, 100,  0,  7}, /* ELM_A2     -14 */
+  { 26.25,     -7, 100,  0,  7}, /* ELM_A3      -7 */
+  { 22.75,   -3.5, 100,  0,  7}, /* ELM_A4    -3.5 */
+  {   -30,      0, 100,  0,  7}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  7}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  7}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"V",   20, 4, 4,0x76,"v","v",ELM_FEATURE_FRC|ELM_FEATURE_LBD|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  1420,    350,  50,  3,  2}, /* ELM_F2    -360 */
+  {  2560,    980,  50,  3,  2}, /* ELM_F3    -300 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,     75,  50,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 33.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"QQ",  30, 0, 0,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,      0, 100,  0,  0}, /* ELM_F1       0 */
+  {  1420,      0, 100,  0,  0}, /* ELM_F2       0 */
+  {  2560,      0, 100,  0,  0}, /* ELM_F3       0 */
+  {    60,      0, 100,  0,  0}, /* ELM_B1       0 */
+  {    90,      0, 100,  0,  0}, /* ELM_B2       0 */
+  {   150,      0, 100,  0,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 40.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 33.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"DH",  20, 4, 4,0x54,"D","D",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  1600,   1190,  50,  3,  2}, /* ELM_F2     390 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {    54,     27,  50,  0,  0}, /* ELM_AB       0 */
+  {    36,     18,  50,  0,  0}, /* ELM_AV       0 */
+  {    54,     27,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"DI",  20, 4, 4,0x54,"D","D",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  1600,   1190,  50,  3,  2}, /* ELM_F2     390 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    28,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"Z",   20, 4, 4,0x7A,"z","z",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  1720,    950,  50,  3,  2}, /* ELM_F2      90 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    40,     20,  50,  0,  0}, /* ELM_AV       0 */
+  {    54,     27,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"ZZ",  20, 4, 4,0x7A,"z","z",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  1720,    950,  50,  3,  2}, /* ELM_F2      90 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {  24.5,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"ZH",  20, 4, 4,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  2020,   1190,  50,  3,  2}, /* ELM_F2     180 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"CH",  23, 4, 4,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {  1780,    950,  50,  2,  2}, /* ELM_F2      60 */
+  {  2680,   2680,   0,  2,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,    150,   0,  2,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"CI",  18, 8, 8,0x53,"S","S",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VLS,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   400,    170,  50,  3,  2}, /* ELM_F1     -30 */
+  {  2020,   1190,  50,  3,  2}, /* ELM_F2     180 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {    42,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AV       0 */
+  {     0,      0,  50,  0,  0}, /* ELM_AVC      0 */
+  {    60,     30,  50,  0,  0}, /* ELM_ASP      0 */
+  {    60,     30,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"J",   26, 4, 4,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,    110,  50,  2,  2}, /* ELM_F1      15 */
+  {  1780,    950,  50,  2,  2}, /* ELM_F2      60 */
+  {  2680,   2680,   0,  2,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  2,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  2,  2}, /* ELM_B2       0 */
+  {   150,    150,   0,  2,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  {  31.5,      0, 100,  0,  0}, /* ELM_A1       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A2       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"JY",  20, 3, 3,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   280,    170,  50,  3,  2}, /* ELM_F1      30 */
+  {  2020,   1190,  50,  3,  2}, /* ELM_F2     180 */
+  {  2560,      0, 100,  3,  2}, /* ELM_F3       0 */
+  {    60,     30,  50,  3,  2}, /* ELM_B1       0 */
+  {    90,     45,  50,  3,  2}, /* ELM_B2       0 */
+  {   150,      0, 100,  3,  2}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 29.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A3       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"L",   11, 8, 8,0x6C,"l","l",ELM_FEATURE_ALV|ELM_FEATURE_LAT|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   460,    230,  50,  6,  0}, /* ELM_F1       0 */
+  {  1480,    710,  50,  6,  0}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  6,  0}, /* ELM_F3     -30 */
+  {    60,     30,  50,  6,  0}, /* ELM_B1       0 */
+  {    90,     45,  50,  6,  0}, /* ELM_B2       0 */
+  {   150,     75,  50,  6,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    21,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"LL",  11, 8, 8,0x6C,"l","l",ELM_FEATURE_ALV|ELM_FEATURE_LAT|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   460,    230,  50,  6,  0}, /* ELM_F1       0 */
+  {   940,    470,  50,  6,  0}, /* ELM_F2       0 */
+  {  2500,   1220,  50,  6,  0}, /* ELM_F3     -30 */
+  {    60,     30,  50,  6,  0}, /* ELM_B1       0 */
+  {    90,     45,  50,  6,  0}, /* ELM_B2       0 */
+  {   150,     75,  50,  6,  0}, /* ELM_B3       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AN       0 */
+  { 36.75,      0, 100,  0,  0}, /* ELM_A1       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A2       0 */
+  { 26.25,      0, 100,  0,  0}, /* ELM_A3       0 */
+  {    21,      0, 100,  0,  0}, /* ELM_A4       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A5       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_A6       0 */
+  {   -30,      0, 100,  0,  0}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"RX",  10,10,10,0xD5,"R","<ELM_FEATURE_RZD>",ELM_FEATURE_RZD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,      0, 100,  0,  5}, /* ELM_F1       0 */
+  {  1180,      0, 100,  0,  5}, /* ELM_F2       0 */
+  {  1600,   1600,   0,  5,  5}, /* ELM_F3       0 */
+  {    60,     30,  50,  0,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {    70,     35,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  {    42,     21,  50,  5,  5}, /* ELM_A1       0 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A2       0 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A3       0 */
+  {   -30,      0,  50,  5,  5}, /* ELM_A4      15 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    50,     25,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"R",   10,11,11,0xA8,"r","r",ELM_FEATURE_ALV|ELM_FEATURE_APR,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,      0, 100,  0,  5}, /* ELM_F1       0 */
+  {  1180,    590,  50,  5,  5}, /* ELM_F2       0 */
+  {  1600,    740,  50,  5,  5}, /* ELM_F3     -60 */
+  {    60,      0, 100,  0,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  {    42,     21,  50,  5,  5}, /* ELM_A1       0 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A2       0 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A3       0 */
+  {   -30,      0,  50,  5,  5}, /* ELM_A4      15 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"W",   10, 8, 8,0x77,"w","w",ELM_FEATURE_APR|ELM_FEATURE_LBV|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   190,     50,  50,  4,  4}, /* ELM_F1     -45 */
+  {   760,    350,  50,  4,  4}, /* ELM_F2     -30 */
+  {  2020,    980,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 43.75,     21,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {    28,     14,  50,  4,  4}, /* ELM_A2       0 */
+  {    21,   10.5,  50,  4,  4}, /* ELM_A3       0 */
+  {   -30,      0,  50,  4,  4}, /* ELM_A4      15 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"Y",   10, 7, 7,0x6A,"j","j",ELM_FEATURE_APR|ELM_FEATURE_PAL|ELM_FEATURE_VCD,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   250,    110,  50,  4,  4}, /* ELM_F1     -15 */
+  {  2500,   1190,  50,  4,  4}, /* ELM_F2     -60 */
+  {  2980,   1460,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 33.25,   17.5,  50,  4,  4}, /* ELM_A2   0.875 */
+  {  38.5,   17.5,  50,  4,  4}, /* ELM_A3   -1.75 */
+  {  31.5,     14,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"I",    2, 8, 6,0x49,"I","I",ELM_FEATURE_FNT|ELM_FEATURE_SMH|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   400,    170,  50,  4,  4}, /* ELM_F1     -30 */
+  {  2080,   1070,  50,  4,  4}, /* ELM_F2      30 */
+  {  2560,   1340,  50,  4,  4}, /* ELM_F3      60 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 36.75,   17.5,  50,  4,  4}, /* ELM_A2  -0.875 */
+  {    35,   17.5,  50,  4,  4}, /* ELM_A3       0 */
+  { 29.75,     14,  50,  4,  4}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"E",    2, 8, 4,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   640,    350,  50,  4,  4}, /* ELM_F1      30 */
+  {  2020,   1070,  50,  4,  4}, /* ELM_F2      60 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {    42,     21,  50,  4,  4}, /* ELM_A2       0 */
+  {  38.5,   17.5,  50,  4,  4}, /* ELM_A3   -1.75 */
+  {  31.5,     14,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"AA",   2,10, 5,0x51,"&","&",ELM_FEATURE_FNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   790,    410,  50,  4,  4}, /* ELM_F1      15 */
+  {  1780,    950,  50,  4,  4}, /* ELM_F2      60 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {   130,     65,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 47.25,   24.5,  50,  4,  4}, /* ELM_A2   0.875 */
+  {  38.5,   17.5,  50,  4,  4}, /* ELM_A3   -1.75 */
+  {  31.5,     14,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"U",    2, 9, 6,0xC3,"V","V",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   700,    350,  50,  4,  4}, /* ELM_F1       0 */
+  {  1360,    710,  50,  4,  4}, /* ELM_F2      30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 43.75,     21,  50,  4,  4}, /* ELM_A2  -0.875 */
+  {  31.5,     14,  50,  4,  4}, /* ELM_A3   -1.75 */
+  {  24.5,   10.5,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"O",    2, 9, 6,0x81,"0","A.",ELM_FEATURE_BCK|ELM_FEATURE_LOW|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   610,    290,  50,  4,  4}, /* ELM_F1     -15 */
+  {   880,    470,  50,  4,  4}, /* ELM_F2      30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 47.25,   24.5,  50,  4,  4}, /* ELM_A2   0.875 */
+  { 22.75,   10.5,  50,  4,  4}, /* ELM_A3  -0.875 */
+  { 15.75,      7,  50,  4,  4}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OO",   2, 6, 4,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   370,    170,  50,  4,  4}, /* ELM_F1     -15 */
+  {  1000,    470,  50,  4,  4}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {    42,     21,  50,  4,  4}, /* ELM_A2       0 */
+  {    28,     14,  50,  4,  4}, /* ELM_A3       0 */
+  { 22.75,   10.5,  50,  4,  4}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"A",    2, 4, 4,0xAB,"@","@",ELM_FEATURE_CNT|ELM_FEATURE_MDL|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,    230,  50,  4,  4}, /* ELM_F1     -15 */
+  {  1480,    710,  50,  4,  4}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A2  -0.875 */
+  { 33.25,   17.5,  50,  4,  4}, /* ELM_A3   0.875 */
+  { 26.25,     14,  50,  4,  4}, /* ELM_A4   0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"EE",   2,11, 7,0x69,"i","i",ELM_FEATURE_FNT|ELM_FEATURE_HGH|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   250,    110,  50,  4,  4}, /* ELM_F1     -15 */
+  {  2320,   1190,  50,  4,  4}, /* ELM_F2      30 */
+  {  3200,   1580,  50,  4,  4}, /* ELM_F3     -20 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 33.25,   17.5,  50,  4,  4}, /* ELM_A2   0.875 */
+  { 36.75,   17.5,  50,  4,  4}, /* ELM_A3  -0.875 */
+  {  31.5,     14,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"ER",   2,16,16,0xCE,"3","V\"",ELM_FEATURE_CNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   580,    290,  50,  4,  4}, /* ELM_F1       0 */
+  {  1420,    710,  50,  4,  4}, /* ELM_F2       0 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {  45.5,     21,  50,  4,  4}, /* ELM_A2   -1.75 */
+  { 33.25,   17.5,  50,  4,  4}, /* ELM_A3   0.875 */
+  { 26.25,     14,  50,  4,  4}, /* ELM_A4   0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"AR",   2,15,15,0x41,"A","A",ELM_FEATURE_BCK|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   790,    410,  50,  4,  4}, /* ELM_F1      15 */
+  {   880,    470,  50,  4,  4}, /* ELM_F2      30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {    49,   24.5,  50,  4,  4}, /* ELM_A2       0 */
+  { 29.75,     14,  50,  4,  4}, /* ELM_A3  -0.875 */
+  { 22.75,   10.5,  50,  4,  4}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"AW",   2,16,10,0x8D,"O","O",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,    230,  50,  4,  4}, /* ELM_F1     -15 */
+  {   820,    470,  50,  4,  4}, /* ELM_F2      60 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {  45.5,     21,  50,  4,  4}, /* ELM_A2   -1.75 */
+  { 22.75,   10.5,  50,  4,  4}, /* ELM_A3  -0.875 */
+  {  17.5,      7,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"UU",   2,14, 9,0x75,"u","u",ELM_FEATURE_BCK|ELM_FEATURE_HGH|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   250,    110,  50,  4,  4}, /* ELM_F1     -15 */
+  {   880,    470,  50,  4,  4}, /* ELM_F2      30 */
+  {  2200,   1100,  50,  4,  4}, /* ELM_F3       0 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {  38.5,   17.5,  50,  4,  4}, /* ELM_A2   -1.75 */
+  {  17.5,      7,  50,  4,  4}, /* ELM_A3   -1.75 */
+  {  10.5,    3.5,  50,  4,  4}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"AI",   2, 9, 6,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   640,    290,  50,  5,  5}, /* ELM_F1     -30 */
+  {  1600,    830,  50,  5,  5}, /* ELM_F2      30 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {  45.5,     21,  50,  5,  5}, /* ELM_A2   -1.75 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A3       0 */
+  { 29.75,     14,  50,  5,  5}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"IE",   2, 9, 6,0x61,"a","a",ELM_FEATURE_CNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   790,    410,  50,  5,  5}, /* ELM_F1      15 */
+  {   880,    470,  50,  5,  5}, /* ELM_F2      30 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {    49,   24.5,  50,  5,  5}, /* ELM_A2       0 */
+  { 29.75,     14,  50,  5,  5}, /* ELM_A3  -0.875 */
+  { 22.75,   10.5,  50,  5,  5}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OI",   2, 9, 6,0x6F,"o","o",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_UMD|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,    230,  50,  5,  5}, /* ELM_F1     -15 */
+  {   820,    350,  50,  5,  5}, /* ELM_F2     -60 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {  45.5,     21,  50,  5,  5}, /* ELM_A2   -1.75 */
+  { 22.75,   10.5,  50,  5,  5}, /* ELM_A3  -0.875 */
+  {  17.5,      7,  50,  5,  5}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OU",   2, 9, 6,0x61,"a","a",ELM_FEATURE_CNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   790,    410,  50,  5,  5}, /* ELM_F1      15 */
+  {  1300,    590,  50,  5,  5}, /* ELM_F2     -60 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  { 47.25,   24.5,  50,  5,  5}, /* ELM_A2   0.875 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A3       0 */
+  {    28,     14,  50,  5,  5}, /* ELM_A4       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OV",   2, 8, 6,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   370,    170,  50,  4,  4}, /* ELM_F1     -15 */
+  {  1000,    470,  50,  4,  4}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  {    42,     21,  50,  4,  4}, /* ELM_A2       0 */
+  {    28,     14,  50,  4,  4}, /* ELM_A3       0 */
+  { 22.75,   10.5,  50,  4,  4}, /* ELM_A4  -0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OA",   2, 9, 6,0xAB,"@","@",ELM_FEATURE_CNT|ELM_FEATURE_MDL|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,    230,  50,  5,  5}, /* ELM_F1     -15 */
+  {  1480,    710,  50,  5,  5}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A2  -0.875 */
+  { 33.25,   17.5,  50,  5,  5}, /* ELM_A3   0.875 */
+  { 26.25,     14,  50,  5,  5}, /* ELM_A4   0.875 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"IA",   2, 9, 6,0x49,"I","I",ELM_FEATURE_FNT|ELM_FEATURE_SMH|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   310,    170,  50,  5,  5}, /* ELM_F1      15 */
+  {  2200,   1070,  50,  5,  5}, /* ELM_F2     -30 */
+  {  2920,   1460,  50,  5,  5}, /* ELM_F3       0 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {    35,   17.5,  50,  5,  5}, /* ELM_A2       0 */
+  { 36.75,   17.5,  50,  5,  5}, /* ELM_A3  -0.875 */
+  {  31.5,     14,  50,  5,  5}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"IB",   2, 8, 6,0x51,"@","@",ELM_FEATURE_FNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,    230,  50,  4,  4}, /* ELM_F1     -15 */
+  {  1480,    710,  50,  4,  4}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  4,  4}, /* ELM_F3     -30 */
+  {    60,     30,  50,  4,  4}, /* ELM_B1       0 */
+  {    90,     45,  50,  4,  4}, /* ELM_B2       0 */
+  {   150,     75,  50,  4,  4}, /* ELM_B3       0 */
+  {   -30,      0, 100,  4,  4}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A1  -0.875 */
+  { 50.75,   24.5,  50,  4,  4}, /* ELM_A2  -0.875 */
+  { 33.25,   17.5,  50,  4,  4}, /* ELM_A3   0.875 */
+  { 26.25,     14,  50,  4,  4}, /* ELM_A4   0.875 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  4,  4}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"AIR",  2, 9, 6,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   640,    350,  50,  5,  5}, /* ELM_F1      30 */
+  {  2020,   1070,  50,  5,  5}, /* ELM_F2      60 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {    42,     21,  50,  5,  5}, /* ELM_A2       0 */
+  {  38.5,   17.5,  50,  5,  5}, /* ELM_A3   -1.75 */
+  {  31.5,     14,  50,  5,  5}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OOR",  2, 9, 6,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   370,    170,  50,  5,  5}, /* ELM_F1     -15 */
+  {  1000,    470,  50,  5,  5}, /* ELM_F2     -30 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {    42,     21,  50,  5,  5}, /* ELM_A2       0 */
+  {    28,     14,  50,  5,  5}, /* ELM_A3       0 */
+  { 22.75,      7,  50,  5,  5}, /* ELM_A4  -4.375 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+},
+
+{"OR",   2, 9, 6,0x8D,"O","O",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+  {   270,    135,  50,  0,  0}, /* ELM_FN       0 */
+  {   490,    230,  50,  5,  5}, /* ELM_F1     -15 */
+  {   820,    470,  50,  5,  5}, /* ELM_F2      60 */
+  {  2500,   1220,  50,  5,  5}, /* ELM_F3     -30 */
+  {    60,     30,  50,  5,  5}, /* ELM_B1       0 */
+  {    90,     45,  50,  5,  5}, /* ELM_B2       0 */
+  {   150,     75,  50,  5,  5}, /* ELM_B3       0 */
+  {   -30,      0, 100,  5,  5}, /* ELM_AN       0 */
+  { 50.75,   24.5,  50,  5,  5}, /* ELM_A1  -0.875 */
+  {  45.5,     21,  50,  5,  5}, /* ELM_A2   -1.75 */
+  { 22.75,   10.5,  50,  5,  5}, /* ELM_A3  -0.875 */
+  {  17.5,      7,  50,  5,  5}, /* ELM_A4   -1.75 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A5       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_A6       0 */
+  {   -30,    -15,  50,  5,  5}, /* ELM_AB       0 */
+  {    62,     31,  50,  0,  0}, /* ELM_AV       0 */
+  {    16,      8,  50,  0,  0}, /* ELM_AVC      0 */
+  {     0,      0,  50,  0,  0}, /* ELM_ASP      0 */
+  {     0,      0,  50,  0,  0}  /* ELM_AF       0 */
+ }
+} 
+
diff --git a/third_party/soloud_speech/darray.cpp b/third_party/soloud_speech/darray.cpp
new file mode 100644 (file)
index 0000000..cd816fa
--- /dev/null
@@ -0,0 +1,70 @@
+#include <stdlib.h>
+#include <string.h>
+#include "darray.h"
+
+darray::darray()
+{
+       mAllocChunk = 128;
+       mAllocated = mUsed = 0;
+       mData = NULL;
+}
+
+void darray::clear()
+{
+       free(mData);
+       mAllocChunk = 128;
+       mAllocated = mUsed = 0;
+       mData = NULL;
+}
+
+darray::~darray()
+{
+       clear();
+}
+
+char * darray::getDataInPos(int aPosition)
+{
+       if (aPosition < mAllocated && aPosition < mUsed)
+               return mData + aPosition;
+
+       if (aPosition >= mAllocated)
+       {
+               int newsize = mAllocated;
+
+               while (newsize <= aPosition)
+               {
+                       newsize += mAllocChunk;
+                       mAllocChunk *= 2;
+               }
+
+               char *newdata = (char*)realloc(mData, newsize);
+               if (!newdata)
+               {
+                       free(mData);
+                       mData = NULL;
+                       mAllocated = mUsed = 0;
+                       return NULL;
+               }
+               else
+               {
+                       memset(newdata + mAllocated, 0, newsize - mAllocated);
+               }
+
+               mData = newdata;
+               mAllocated = newsize;                   
+       }
+
+       if (aPosition >= mUsed)
+       {
+               mUsed = aPosition + 1;
+       }
+
+       return mData + aPosition;
+}
+
+void darray::put(int aData)
+{
+       char *s = getDataInPos(mUsed);
+
+       *s = (char)aData;
+}
diff --git a/third_party/soloud_speech/darray.h b/third_party/soloud_speech/darray.h
new file mode 100644 (file)
index 0000000..d3251fb
--- /dev/null
@@ -0,0 +1,22 @@
+#if !defined(DARRAY_H)
+#define DARRAY_H
+
+class darray
+{
+protected:
+       char *mData;
+       int mUsed;
+       int mAllocated;
+       int mAllocChunk;
+public:
+       darray();
+       ~darray();
+       void clear();
+       char *getDataInPos(int aPosition);
+       void put(int aData);
+       int getSize() const { return mUsed; }
+       char *getData() { return mData; } 
+};
+
+#endif
+
diff --git a/third_party/soloud_speech/klatt.cpp b/third_party/soloud_speech/klatt.cpp
new file mode 100644 (file)
index 0000000..c9572f0
--- /dev/null
@@ -0,0 +1,1074 @@
+#include <math.h>
+#include <stdlib.h>
+#include "klatt.h"
+#include "darray.h"
+#include "resonator.h"
+
+#ifndef PI
+#define PI 3.1415926535897932384626433832795f
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+class Interp
+{
+public:
+       float mSteady;
+       float mFixed;
+       char  mProportion;
+       char  mExtDelay;
+       char  mIntDelay;
+};
+
+
+enum Eparm_e
+{
+  ELM_FN, ELM_F1, ELM_F2, ELM_F3, 
+  ELM_B1, ELM_B2, ELM_B3, ELM_AN, 
+  ELM_A1, ELM_A2, ELM_A3, ELM_A4, 
+  ELM_A5, ELM_A6, ELM_AB, ELM_AV, 
+  ELM_AVC, ELM_ASP, ELM_AF, 
+  ELM_COUNT
+};
+class Element
+{
+public:
+         const char *mName; // unused
+         const char mRK;
+         const char mDU;
+         const char mUD;
+         unsigned char mFont; // unused
+         const char  *mDict; // unused
+         const char  *mIpa; // unused
+         int   mFeat; // only ELM_FEATURE_VWL
+         Interp mInterpolator[ELM_COUNT];
+ };
+
+enum ELEMENT_FEATURES
+{
+       ELM_FEATURE_ALV = 0x00000001,
+       ELM_FEATURE_APR = 0x00000002,
+       ELM_FEATURE_BCK = 0x00000004,
+       ELM_FEATURE_BLB = 0x00000008,
+       ELM_FEATURE_CNT = 0x00000010,
+       ELM_FEATURE_DNT = 0x00000020,
+       ELM_FEATURE_FNT = 0x00000040,
+       ELM_FEATURE_FRC = 0x00000080,
+       ELM_FEATURE_GLT = 0x00000100,
+       ELM_FEATURE_HGH = 0x00000200,
+       ELM_FEATURE_LAT = 0x00000400,
+       ELM_FEATURE_LBD = 0x00000800,
+       ELM_FEATURE_LBV = 0x00001000,
+       ELM_FEATURE_LMD = 0x00002000,
+       ELM_FEATURE_LOW = 0x00004000,
+       ELM_FEATURE_MDL = 0x00008000,
+       ELM_FEATURE_NAS = 0x00010000,
+       ELM_FEATURE_PAL = 0x00020000,
+       ELM_FEATURE_PLA = 0x00040000,
+       ELM_FEATURE_RND = 0x00080000,
+       ELM_FEATURE_RZD = 0x00100000,
+       ELM_FEATURE_SMH = 0x00200000,
+       ELM_FEATURE_STP = 0x00400000,
+       ELM_FEATURE_UMD = 0x00800000,
+       ELM_FEATURE_UNR = 0x01000000,
+       ELM_FEATURE_VCD = 0x02000000,
+       ELM_FEATURE_VEL = 0x04000000,
+       ELM_FEATURE_VLS = 0x08000000,
+       ELM_FEATURE_VWL = 0x10000000
+};
+
+enum ELEMENTS 
+{
+       ELM_END = 0,    
+       ELM_Q,  ELM_P,  ELM_PY, ELM_PZ, ELM_T,  ELM_TY, 
+       ELM_TZ, ELM_K,  ELM_KY, ELM_KZ, ELM_B,  ELM_BY, ELM_BZ, 
+       ELM_D,  ELM_DY, ELM_DZ, ELM_G,  ELM_GY, ELM_GZ, ELM_M,  
+       ELM_N,  ELM_NG, ELM_F,  ELM_TH, ELM_S,  ELM_SH, ELM_X,
+       ELM_H,  ELM_V,  ELM_QQ, ELM_DH, ELM_DI, ELM_Z,  ELM_ZZ,
+       ELM_ZH, ELM_CH, ELM_CI, ELM_J,  ELM_JY, ELM_L,  ELM_LL,
+       ELM_RX, ELM_R,  ELM_W,  ELM_Y,  ELM_I,  ELM_E,  ELM_AA,
+       ELM_U,  ELM_O,  ELM_OO, ELM_A,  ELM_EE, ELM_ER, ELM_AR,
+       ELM_AW, ELM_UU, ELM_AI, ELM_IE, ELM_OI, ELM_OU, ELM_OV,
+       ELM_OA, ELM_IA, ELM_IB, ELM_AIR,ELM_OOR,ELM_OR
+};
+
+#define PHONEME_COUNT 53
+#define AMP_ADJ 14
+#define StressDur(e,s) (s,((e->mDU + e->mUD)/2))
+
+
+
+
+class PhonemeToElements 
+{
+public:
+       int mKey;
+       char mData[8];
+};
+
+/* Order is important - 2 byte phonemes first, otherwise
+   the search function will fail*/
+static PhonemeToElements phoneme_to_elements[PHONEME_COUNT] = 
+{
+       /* mKey, count, 0-7 elements */
+/* tS */ 0x5374, 2, ELM_CH, ELM_CI, 0, 0, 0, 0, 0,
+/* dZ */ 0x5a64, 4, ELM_J, ELM_JY, ELM_QQ, ELM_JY, 0, 0, 0,
+/* rr */ 0x7272, 3, ELM_R, ELM_QQ, ELM_R, 0, 0, 0, 0,
+/* eI */ 0x4965, 2, ELM_AI, ELM_I, 0, 0, 0, 0, 0,
+/* aI */ 0x4961, 2, ELM_IE, ELM_I, 0, 0, 0, 0, 0,
+/* oI */ 0x496f, 2, ELM_OI, ELM_I, 0, 0, 0, 0, 0,
+/* aU */ 0x5561, 2, ELM_OU, ELM_OV, 0, 0, 0, 0, 0,
+/* @U */ 0x5540, 2, ELM_OA, ELM_OV, 0, 0, 0, 0, 0,
+/* I@ */ 0x4049, 2, ELM_IA, ELM_IB, 0, 0, 0, 0, 0,
+/* e@ */ 0x4065, 2, ELM_AIR, ELM_IB, 0, 0, 0, 0, 0,
+/* U@ */ 0x4055, 2, ELM_OOR, ELM_IB, 0, 0, 0, 0, 0,
+/* O@ */ 0x404f, 2, ELM_OR, ELM_IB, 0, 0, 0, 0, 0,
+/* oU */ 0x556f, 2, ELM_OI, ELM_OV, 0, 0, 0, 0, 0,
+/*    */ 0x0020, 1, ELM_Q, 0, 0, 0, 0, 0, 0,
+/* p  */ 0x0070, 3, ELM_P, ELM_PY, ELM_PZ, 0, 0, 0, 0,
+/* t  */ 0x0074, 3, ELM_T, ELM_TY, ELM_TZ, 0, 0, 0, 0,
+/* k  */ 0x006b, 3, ELM_K, ELM_KY, ELM_KZ, 0, 0, 0, 0,
+/* b  */ 0x0062, 3, ELM_B, ELM_BY, ELM_BZ, 0, 0, 0, 0,
+/* d  */ 0x0064, 3, ELM_D, ELM_DY, ELM_DZ, 0, 0, 0, 0,
+/* g  */ 0x0067, 3, ELM_G, ELM_GY, ELM_GZ, 0, 0, 0, 0,
+/* m  */ 0x006d, 1, ELM_M, 0, 0, 0, 0, 0, 0,
+/* n  */ 0x006e, 1, ELM_N, 0, 0, 0, 0, 0, 0,
+/* N  */ 0x004e, 1, ELM_NG, 0, 0, 0, 0, 0, 0,
+/* f  */ 0x0066, 1, ELM_F, 0, 0, 0, 0, 0, 0,
+/* T  */ 0x0054, 1, ELM_TH, 0, 0, 0, 0, 0, 0,
+/* s  */ 0x0073, 1, ELM_S, 0, 0, 0, 0, 0, 0,
+/* S  */ 0x0053, 1, ELM_SH, 0, 0, 0, 0, 0, 0,
+/* h  */ 0x0068, 1, ELM_H, 0, 0, 0, 0, 0, 0,
+/* v  */ 0x0076, 3, ELM_V, ELM_QQ, ELM_V, 0, 0, 0, 0,
+/* D  */ 0x0044, 3, ELM_DH, ELM_QQ, ELM_DI, 0, 0, 0, 0,
+/* z  */ 0x007a, 3, ELM_Z, ELM_QQ, ELM_ZZ, 0, 0, 0, 0,
+/* Z  */ 0x005a, 3, ELM_ZH, ELM_QQ, ELM_ZH, 0, 0, 0, 0,
+/* l  */ 0x006c, 1, ELM_L, 0, 0, 0, 0, 0, 0,
+/* r  */ 0x0072, 1, ELM_R, 0, 0, 0, 0, 0, 0,
+/* R  */ 0x0052, 1, ELM_RX, 0, 0, 0, 0, 0, 0,
+/* w  */ 0x0077, 1, ELM_W, 0, 0, 0, 0, 0, 0,
+/* x  */ 0x0078, 1, ELM_X, 0, 0, 0, 0, 0, 0,
+/* %  */ 0x0025, 1, ELM_QQ, 0, 0, 0, 0, 0, 0,
+/* j  */ 0x006a, 1, ELM_Y, 0, 0, 0, 0, 0, 0,
+/* I  */ 0x0049, 1, ELM_I, 0, 0, 0, 0, 0, 0,
+/* e  */ 0x0065, 1, ELM_E, 0, 0, 0, 0, 0, 0,
+/* &  */ 0x0026, 1, ELM_AA, 0, 0, 0, 0, 0, 0,
+/* V  */ 0x0056, 1, ELM_U, 0, 0, 0, 0, 0, 0,
+/* 0  */ 0x0030, 1, ELM_O, 0, 0, 0, 0, 0, 0,
+/* U  */ 0x0055, 1, ELM_OO, 0, 0, 0, 0, 0, 0,
+/* @  */ 0x0040, 1, ELM_A, 0, 0, 0, 0, 0, 0,
+/* i  */ 0x0069, 1, ELM_EE, 0, 0, 0, 0, 0, 0,
+/* 3  */ 0x0033, 1, ELM_ER, 0, 0, 0, 0, 0, 0,
+/* A  */ 0x0041, 1, ELM_AR, 0, 0, 0, 0, 0, 0,
+/* O  */ 0x004f, 1, ELM_AW, 0, 0, 0, 0, 0, 0,
+/* u  */ 0x0075, 1, ELM_UU, 0, 0, 0, 0, 0, 0,
+/* o  */ 0x006f, 1, ELM_OI, 0, 0, 0, 0, 0, 0,
+/* .  */ 0x002e, 1, ELM_END,0, 0, 0, 0, 0, 0,
+};
+
+static Element gElement[] =
+{
+#include "Elements.def"
+};
+
+static short clip(float input)
+{
+       int temp = (int)input;
+       /* clip on boundaries of 16-bit word */
+
+       if (temp < -32767)
+       {
+               //assert?
+               temp = -32767;
+       }
+       else
+       if (temp > 32767)
+       {
+               //assert?
+               temp = 32767;
+       }
+
+       return (short)(temp);
+}
+
+/* Convert from decibels to a linear scale factor */
+static float DBtoLIN(int dB)
+{
+       /*
+       * Convertion table, db to linear, 87 dB --> 32767
+       *                                 86 dB --> 29491 (1 dB down = 0.5**1/6)
+       *                                 ...
+       *                                 81 dB --> 16384 (6 dB down = 0.5)
+       *                                 ...
+       *                                  0 dB -->     0
+       *
+       * The just noticeable difference for a change in intensity of a vowel
+       *   is approximately 1 dB.  Thus all amplitudes are quantized to 1 dB
+       *   steps.
+       */
+
+       static const float amptable[88] =
+       {
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 0.0, 0.0,
+               0.0, 0.0, 0.0, 6.0, 7.0,
+               8.0, 9.0, 10.0, 11.0, 13.0,
+               14.0, 16.0, 18.0, 20.0, 22.0,
+               25.0, 28.0, 32.0, 35.0, 40.0,
+               45.0, 51.0, 57.0, 64.0, 71.0,
+               80.0, 90.0, 101.0, 114.0, 128.0,
+               142.0, 159.0, 179.0, 202.0, 227.0,
+               256.0, 284.0, 318.0, 359.0, 405.0,
+               455.0, 512.0, 568.0, 638.0, 719.0,
+               811.0, 911.0, 1024.0, 1137.0, 1276.0,
+               1438.0, 1622.0, 1823.0, 2048.0, 2273.0,
+               2552.0, 2875.0, 3244.0, 3645.0, 4096.0,
+               4547.0, 5104.0, 5751.0, 6488.0, 7291.0,
+               8192.0, 9093.0, 10207.0, 11502.0, 12976.0,
+               14582.0, 16384.0, 18350.0, 20644.0, 23429.0,
+               26214.0, 29491.0, 32767.0
+       };
+
+       // Check limits or argument (can be removed in final product)
+       if (dB < 0)
+       {
+               dB = 0;
+       }
+       else
+       if (dB >= 88)
+       {
+               dB = 87;
+       }
+
+       return amptable[dB] * 0.001f;
+}
+
+
+
+klatt_frame::klatt_frame() :
+       mF0FundamentalFreq(1330),       mVoicingAmpdb(60),                              mFormant1Freq(500),  
+       mFormant1Bandwidth(60),         mFormant2Freq(1500),                    mFormant2Bandwidth(90),
+       mFormant3Freq(2800),            mFormant3Bandwidth(150),                mFormant4Freq(3250), 
+       mFormant4Bandwidth(200),        mFormant5Freq(3700),                    mFormant5Bandwidth(200),  
+       mFormant6Freq(4990),            mFormant6Bandwidth(500),                mNasalZeroFreq(270),  
+       mNasalZeroBandwidth(100),       mNasalPoleFreq(270),                    mNasalPoleBandwidth(100), 
+       mAspirationAmpdb(0),            mNoSamplesInOpenPeriod(30),             mVoicingBreathiness(0),      
+       mVoicingSpectralTiltdb(10), mFricationAmpdb(0),                         mSkewnessOfAlternatePeriods(0),   
+       mFormant1Ampdb(0),                      mFormant1ParallelBandwidth(80), mFormant2Ampdb(0),      
+       mFormant2ParallelBandwidth(200), mFormant3Ampdb(0),                     mFormant3ParallelBandwidth(350),
+       mFormant4Ampdb(0),                      mFormant4ParallelBandwidth(500), mFormant5Ampdb(0),      
+       mFormant5ParallelBandwidth(600), mFormant6Ampdb(0),                     mFormant6ParallelBandwidth(800),    
+       mParallelNasalPoleAmpdb(0), mBypassFricationAmpdb(0),       mPalallelVoicingAmpdb(0),   
+       mOverallGaindb(62)  
+{
+};
+
+
+klatt::klatt() : 
+       mBaseF0(1330),
+       mBaseSpeed(10.0f),
+       mBaseDeclination(0.5f),
+       mBaseWaveform(KW_SAW),
+       mF0Flutter(0), 
+       mSampleRate(0), 
+       mNspFr(0),
+       mF0FundamentalFreq(0),
+       mVoicingAmpdb(0),
+       mSkewnessOfAlternatePeriods(0),
+       mTimeCount(0), 
+       mNPer(0),
+       mT0(0),
+       mNOpen(0),
+       mNMod(0),
+       mAmpVoice(0),
+       mAmpBypas(0),
+       mAmpAspir(0),
+       mAmpFrica(0),
+       mAmpBreth(0),
+       mSkew(0),
+       mVLast(0),
+       mNLast(0),
+       mGlotLast(0),
+       mDecay(0),
+       mOneMd(0),
+       mSeed(5),
+       mElementCount(0),
+       mElement(0),
+       mElementIndex(0),
+       mLastElement(0),
+       mTStress(0),
+       mNTStress(0),
+       mTop(0)
+{
+}
+
+/*
+function FLUTTER
+
+This function adds F0 flutter, as specified in:
+
+"Analysis, synthesis and perception of voice quality variations among
+female and male talkers" D.H. Klatt and L.C. Klatt JASA 87(2) February 1990.
+Flutter is added by applying a quasi-random element constructed from three
+slowly varying sine waves.
+*/
+void klatt::flutter()
+{
+       int original_f0 = mFrame.mF0FundamentalFreq / 10;
+       float fla = (float) mF0Flutter / 50;
+       float flb = (float) original_f0 / 100;
+       float flc = (float)sin(2 * PI * 12.7 * mTimeCount);
+       float fld = (float)sin(2 * PI * 7.1 * mTimeCount);
+       float fle = (float)sin(2 * PI * 4.7 * mTimeCount);
+       float delta_f0 = fla * flb * (flc + fld + fle) * 10;
+       mF0FundamentalFreq += (int) delta_f0;
+}
+
+/* Vwave is the differentiated glottal flow waveform, there is a weak
+spectral zero around 800 Hz, magic constants a,b reset pitch-synch
+*/
+
+float klatt::natural_source(int aNper)
+{
+       // See if glottis open 
+       if (aNper < mNOpen)
+       {
+               switch (mBaseWaveform)
+               {
+               case KW_TRIANGLE:
+                       return ((aNper % 200) - 100) * 81.92f; // triangle
+               case KW_SIN:
+                       return (float)(sin(aNper * 0.0314) * 8192); // sin
+               case KW_SQUARE:
+                       return ((aNper % 200) - 100) > 0 ? 8192.0f : -8192.0f; // square
+               case KW_PULSE:
+                       return ((aNper % 200) - 100) > 50 ? 8192.0f : -8192.0f; // pulse
+               case KW_NOISE:
+                       return (int)mNLast & 1 ? -8192.0f : 8192.0f;
+               case KW_WARBLE:
+                               return (int)mNLast & 7 ? -8192.0f : 8192.0f;
+               case KW_SAW: // fallthrough
+               default:
+                       return (abs((aNper % 200) - 100) - 50) * 163.84f; // saw
+               }       
+       }
+       else
+       {
+               // Glottis closed 
+               return (0.0);
+       }
+       
+}
+
+/* Reset selected parameters pitch-synchronously */
+
+void klatt::pitch_synch_par_reset(int ns)
+{      
+       if (mF0FundamentalFreq > 0)
+       {
+               mT0 = (40 * mSampleRate) / mF0FundamentalFreq;
+
+               /* Period in samp*4 */
+               mAmpVoice = DBtoLIN(mVoicingAmpdb);
+
+               /* Duration of period before amplitude modulation */
+               mNMod = mT0;
+
+               if (mVoicingAmpdb > 0)
+               {
+                       mNMod >>= 1;
+               }
+
+               /* Breathiness of voicing waveform */
+
+               mAmpBreth = DBtoLIN(mFrame.mVoicingBreathiness) * 0.1f;
+
+               /* Set open phase of glottal period */
+               /* where  40 <= open phase <= 263 */
+
+               mNOpen = 4 * mFrame.mNoSamplesInOpenPeriod;
+
+               if (mNOpen >= (mT0 - 1))
+               {
+                       mNOpen = mT0 - 2;
+               }
+
+               if (mNOpen < 40)
+               {
+                       mNOpen = 40;                  /* F0 max = 1000 Hz */
+               }
+
+               int temp;
+               float temp1;
+
+               temp = mSampleRate / mNOpen;
+               mCritDampedGlotLowPassFilter.initResonator(0L, temp, mSampleRate);
+
+               /* Make gain at F1 about constant */
+
+               temp1 = mNOpen * .00833f;
+               mCritDampedGlotLowPassFilter.setGain(temp1 * temp1);
+
+               /* Truncate skewness so as not to exceed duration of closed phase
+               of glottal period */
+
+               temp = mT0 - mNOpen;
+
+               if (mSkewnessOfAlternatePeriods > temp)
+               {
+                       mSkewnessOfAlternatePeriods = temp;
+               }
+
+               if (mSkew >= 0)
+               {
+                       mSkew = mSkewnessOfAlternatePeriods;                /* Reset mSkew to requested mSkewnessOfAlternatePeriods */
+               }
+               else
+               {
+                       mSkew = -mSkewnessOfAlternatePeriods;
+               }
+
+               /* Add skewness to closed portion of voicing period */
+
+               mT0 = mT0 + mSkew;
+               mSkew = -mSkew;
+       }
+       else
+       {
+               mT0 = 4;                        /* Default for f0 undefined */
+               mAmpVoice = 0.0;
+               mNMod = mT0;
+               mAmpBreth = 0.0;
+       }
+
+       /* Reset these pars pitch synchronously or at update rate if f0=0 */
+
+       if ((mT0 != 4) || (ns == 0))
+       {
+               /* Set one-pole ELM_FEATURE_LOW-pass filter that tilts glottal source */
+               mDecay = (0.033f * mFrame.mVoicingSpectralTiltdb);      /* Function of samp_rate ? */
+
+               if (mDecay > 0.0f)
+               {
+                       mOneMd = 1.0f - mDecay;
+               }
+               else
+               {
+                       mOneMd = 1.0f;
+               }
+       }
+}
+
+
+/* Get variable parameters from host computer,
+initially also get definition of fixed pars
+*/
+
+void klatt::frame_init()
+{
+       int mOverallGaindb;                       /* Overall gain, 60 dB is unity  0 to   60  */
+       float amp_parF1;                 /* mFormant1Ampdb converted to linear gain  */
+       float amp_parFN;                 /* mParallelNasalPoleAmpdb converted to linear gain  */
+       float amp_parF2;                 /* mFormant2Ampdb converted to linear gain  */
+       float amp_parF3;                 /* mFormant3Ampdb converted to linear gain  */
+       float amp_parF4;                 /* mFormant4Ampdb converted to linear gain  */
+       float amp_parF5;                 /* mFormant5Ampdb converted to linear gain  */
+       float amp_parF6;                 /* mFormant6Ampdb converted to linear gain  */
+
+       /* Read  speech frame definition into temp store
+       and move some parameters into active use immediately
+       (voice-excited ones are updated pitch synchronously
+       to avoid waveform glitches).
+        */
+
+       mF0FundamentalFreq = mFrame.mF0FundamentalFreq;
+       mVoicingAmpdb = mFrame.mVoicingAmpdb - 7;
+
+       if (mVoicingAmpdb < 0) mVoicingAmpdb = 0;
+
+       mAmpAspir = DBtoLIN(mFrame.mAspirationAmpdb) * .05f;
+       mAmpFrica = DBtoLIN(mFrame.mFricationAmpdb) * 0.25f;
+       mSkewnessOfAlternatePeriods = mFrame.mSkewnessOfAlternatePeriods;
+
+       /* Fudge factors (which comprehend affects of formants on each other?)
+       with these in place ALL_PARALLEL should sound as close as
+          possible to CASCADE_PARALLEL.
+          Possible problem feeding in Holmes's amplitudes given this.
+       */
+       amp_parF1 = DBtoLIN(mFrame.mFormant1Ampdb) * 0.4f;      /* -7.96 dB */
+       amp_parF2 = DBtoLIN(mFrame.mFormant2Ampdb) * 0.15f;     /* -16.5 dB */
+       amp_parF3 = DBtoLIN(mFrame.mFormant3Ampdb) * 0.06f;     /* -24.4 dB */
+       amp_parF4 = DBtoLIN(mFrame.mFormant4Ampdb) * 0.04f;     /* -28.0 dB */
+       amp_parF5 = DBtoLIN(mFrame.mFormant5Ampdb) * 0.022f;    /* -33.2 dB */
+       amp_parF6 = DBtoLIN(mFrame.mFormant6Ampdb) * 0.03f;     /* -30.5 dB */
+       amp_parFN = DBtoLIN(mFrame.mParallelNasalPoleAmpdb) * 0.6f;     /* -4.44 dB */
+       mAmpBypas = DBtoLIN(mFrame.mBypassFricationAmpdb) * 0.05f;      /* -26.0 db */
+
+       // Set coeficients of nasal resonator and zero antiresonator 
+       mNasalPole.initResonator(mFrame.mNasalPoleFreq, mFrame.mNasalPoleBandwidth, mSampleRate);
+
+       mNasalZero.initAntiresonator(mFrame.mNasalZeroFreq, mFrame.mNasalZeroBandwidth, mSampleRate);
+
+       // Set coefficients of parallel resonators, and amplitude of outputs 
+       mParallelFormant1.initResonator(mFrame.mFormant1Freq, mFrame.mFormant1ParallelBandwidth, mSampleRate);
+       mParallelFormant1.setGain(amp_parF1);
+
+       mParallelResoNasalPole.initResonator(mFrame.mNasalPoleFreq, mFrame.mNasalPoleBandwidth, mSampleRate);
+       mParallelResoNasalPole.setGain(amp_parFN);
+
+       mParallelFormant2.initResonator(mFrame.mFormant2Freq, mFrame.mFormant2ParallelBandwidth, mSampleRate);
+       mParallelFormant2.setGain(amp_parF2);
+
+       mParallelFormant3.initResonator(mFrame.mFormant3Freq, mFrame.mFormant3ParallelBandwidth, mSampleRate);
+       mParallelFormant3.setGain(amp_parF3);
+
+       mParallelFormant4.initResonator(mFrame.mFormant4Freq, mFrame.mFormant4ParallelBandwidth, mSampleRate);
+       mParallelFormant4.setGain(amp_parF4);
+
+       mParallelFormant5.initResonator(mFrame.mFormant5Freq, mFrame.mFormant5ParallelBandwidth, mSampleRate);
+       mParallelFormant5.setGain(amp_parF5);
+
+       mParallelFormant6.initResonator(mFrame.mFormant6Freq, mFrame.mFormant6ParallelBandwidth, mSampleRate);
+       mParallelFormant6.setGain(amp_parF6);
+
+
+       /* fold overall gain into output resonator */
+       mOverallGaindb = mFrame.mOverallGaindb - 3;
+
+       if (mOverallGaindb <= 0)
+               mOverallGaindb = 57;
+
+       /* output ELM_FEATURE_LOW-pass filter - resonator with freq 0 and BW = globals->mSampleRate
+       Thus 3db point is globals->mSampleRate/2 i.e. Nyquist limit.
+       Only 3db down seems rather mild...
+       */
+       mOutputLowPassFilter.initResonator(0L, (int)mSampleRate, mSampleRate);
+       mOutputLowPassFilter.setGain(DBtoLIN(mOverallGaindb));
+}
+
+/*
+function PARWAV
+
+CONVERT FRAME OF PARAMETER DATA TO A WAVEFORM CHUNK
+Synthesize globals->mNspFr samples of waveform and store in jwave[].
+*/
+
+void klatt::parwave(short int *jwave)
+{
+       /* Output of cascade branch, also final output  */
+
+       /* Initialize synthesizer and get specification for current speech
+       frame from host microcomputer */
+
+       frame_init();
+
+       if (mF0Flutter != 0)
+       {
+               mTimeCount++;                  /* used for f0 flutter */
+               flutter();       /* add f0 flutter */
+       }
+
+       /* MAIN LOOP, for each output sample of current frame: */
+
+       int ns;
+       for (ns = 0; ns < mNspFr; ns++)
+       {
+               float noise;
+               int n4;
+               float sourc;                   /* Sound source if all-parallel config used  */
+               float glotout;                 /* Output of glottal sound source  */
+               float par_glotout;             /* Output of parallelglottal sound sourc  */
+               float voice = 0;               /* Current sample of voicing waveform  */
+               float frics;                   /* Frication sound source  */
+               float aspiration;              /* Aspiration sound source  */
+               int nrand;                    /* Varible used by random number generator  */
+
+               /* Our own code like rand(), but portable
+               whole upper 31 bits of seed random
+               assumes 32-bit unsigned arithmetic
+               with untested code to handle larger.
+               */
+               mSeed = mSeed * 1664525 + 1;
+
+               mSeed &= 0xFFFFFFFF;
+
+               /* Shift top bits of seed up to top of int then back down to LS 14 bits */
+               /* Assumes 8 bits per sizeof unit i.e. a "byte" */
+               nrand = (((int) mSeed) << (8 * sizeof(int) - 32)) >> (8 * sizeof(int) - 14);
+
+               /* Tilt down noise spectrum by soft ELM_FEATURE_LOW-pass filter having
+               *    a pole near the origin in the z-plane, i.e.
+               *    output = input + (0.75 * lastoutput) */
+
+               noise = nrand + (0.75f * mNLast);       /* Function of samp_rate ? */
+
+               mNLast = noise;
+
+               /* Amplitude modulate noise (reduce noise amplitude during
+               second half of glottal period) if voicing simultaneously present
+               */
+
+               if (mNPer > mNMod)
+               {
+                       noise *= 0.5f;
+               }
+
+               /* Compute frication noise */
+               sourc = frics = mAmpFrica * noise;
+
+               /* Compute voicing waveform : (run glottal source simulation at
+               4 times normal sample rate to minimize quantization noise in
+               period of female voice)
+               */
+
+               for (n4 = 0; n4 < 4; n4++)
+               {
+                       /* use a more-natural-shaped source waveform with excitation
+                       occurring both upon opening and upon closure, stronest at closure */
+                       voice = natural_source(mNPer);
+
+                       /* Reset period when counter 'mNPer' reaches mT0 */
+
+                       if (mNPer >= mT0)
+                       {
+                               mNPer = 0;
+                               pitch_synch_par_reset(ns);
+                       }
+
+                       /* Low-pass filter voicing waveform before downsampling from 4*globals->mSampleRate */
+                       /* to globals->mSampleRate samples/sec.  Resonator f=.09*globals->mSampleRate, bw=.06*globals->mSampleRate  */
+
+                       voice = mDownSampLowPassFilter.resonate(voice); /* in=voice, out=voice */
+
+                       /* Increment counter that keeps track of 4*globals->mSampleRate samples/sec */
+                       mNPer++;
+               }
+
+               /* Tilt spectrum of voicing source down by soft ELM_FEATURE_LOW-pass filtering, amount
+               of tilt determined by mVoicingSpectralTiltdb
+               */
+               voice = (voice * mOneMd) + (mVLast * mDecay);
+
+               mVLast = voice;
+
+               /* Add breathiness during glottal open phase */
+               if (mNPer < mNOpen)
+               {
+                       /* Amount of breathiness determined by parameter mVoicingBreathiness */
+                       /* Use nrand rather than noise because noise is ELM_FEATURE_LOW-passed */
+                       voice += mAmpBreth * nrand;
+               }
+
+               /* Set amplitude of voicing */
+               glotout = mAmpVoice * voice;
+
+               /* Compute aspiration amplitude and add to voicing source */
+               aspiration = mAmpAspir * noise;
+
+               glotout += aspiration;
+
+               par_glotout = glotout;
+
+               /* NIS - rsynth "hack"
+               As Holmes' scheme is weak at nasals and (physically) nasal cavity
+               is "back near glottis" feed glottal source through nasal resonators
+               Don't think this is quite right, but improves things a bit
+               */
+               par_glotout = mNasalZero.antiresonate(par_glotout);
+               par_glotout = mNasalPole.resonate(par_glotout);
+               /* And just use mParallelFormant1 NOT mParallelResoNasalPole */         
+               float out = mParallelFormant1.resonate(par_glotout);
+               /* Sound sourc for other parallel resonators is frication
+               plus first difference of voicing waveform.
+               */
+               sourc += (par_glotout - mGlotLast);
+               mGlotLast = par_glotout;
+
+               /* Standard parallel vocal tract
+               Formants F6,F5,F4,F3,F2, outputs added with alternating sign
+               */
+               out = mParallelFormant6.resonate(sourc) - out;
+               out = mParallelFormant5.resonate(sourc) - out;
+               out = mParallelFormant4.resonate(sourc) - out;
+               out = mParallelFormant3.resonate(sourc) - out;
+               out = mParallelFormant2.resonate(sourc) - out;
+
+               out = mAmpBypas * sourc - out;
+               out = mOutputLowPassFilter.resonate(out);
+
+               *jwave++ = clip(out); /* Convert back to integer */
+       }
+}
+
+
+
+static char * phoneme_to_element_lookup(char *s, void ** data)
+{
+       int key8 = *s;
+       int key16 = key8 + (s[1] << 8);
+       if (s[1] == 0) key16 = -1; // avoid key8==key16
+       int i;
+       for (i = 0; i < PHONEME_COUNT; i++)
+       {
+               if (phoneme_to_elements[i].mKey == key16)
+               {
+                       *data = &phoneme_to_elements[i].mData;
+                       return s+2;
+               }
+               if (phoneme_to_elements[i].mKey == key8)
+               {
+                       *data = &phoneme_to_elements[i].mData;
+                       return s+1;
+               }
+       }
+       // should never happen
+       *data = NULL;
+       return s+1;
+}
+
+
+
+int klatt::phone_to_elm(char *aPhoneme, int aCount, darray *aElement)
+{
+       int stress = 0;
+       char *s = aPhoneme;
+       int t = 0;
+       char *limit = s + aCount;
+
+       while (s < limit && *s)
+       {
+               char *e = NULL;
+               s = phoneme_to_element_lookup(s, (void**)&e);
+
+               if (e)
+               {
+                       int n = *e++;
+
+                       while (n-- > 0)
+                       {
+                               int x = *e++;
+                               Element * p = &gElement[x];
+                               /* This works because only vowels have mUD != mDU,
+                               and we set stress just before a vowel
+                               */
+                               aElement->put(x);
+
+                               if (!(p->mFeat & ELM_FEATURE_VWL))
+                                       stress = 0;
+
+                               int stressdur = StressDur(p,stress);
+
+                               t += stressdur;
+
+                               aElement->put(stressdur);
+                               aElement->put(stress);
+                       }
+               }
+
+               else
+               {
+                       char ch = *s++;
+
+                       switch (ch)
+                       {
+
+                       case '\'':                /* Primary stress */
+                               stress = 3;
+                               break;
+
+                       case ',':                 /* Secondary stress */
+                               stress = 2;
+                               break;
+
+                       case '+':                 /* Tertiary stress */
+                               stress = 1;
+                               break;
+
+                       case '-':                 /* hyphen in input */
+                               break;
+
+                       default:
+//                             fprintf(stderr, "Ignoring %c in '%.*s'\n", ch, aCount, aPhoneme);
+                               break;
+                       }
+               }
+       }
+
+       return t;
+}
+
+
+
+/* 'a' is dominant element, 'b' is dominated
+    ext is flag to say to use external times from 'a' rather
+    than internal i.e. ext != 0 if 'a' is NOT current element.
+ */
+
+static void set_trans(Slope *t, Element * a, Element * b,int ext, int /* e */)
+{
+       int i;
+
+       for (i = 0; i < ELM_COUNT; i++)
+       {
+               t[i].mTime = ((ext) ? a->mInterpolator[i].mExtDelay : a->mInterpolator[i].mIntDelay);
+
+               if (t[i].mTime)
+               {
+                       t[i].mValue = a->mInterpolator[i].mFixed + (a->mInterpolator[i].mProportion * b->mInterpolator[i].mSteady) * 0.01f; // mProportion is in scale 0..100, so *0.01.
+               }
+               else
+               {
+                       t[i].mValue = b->mInterpolator[i].mSteady;
+               }
+       }
+}
+
+static float lerp(float a, float b, int t, int d)
+{
+       if (t <= 0)
+       {
+               return a;
+       }
+
+       if (t >= d)
+       {
+               return b;
+       }
+
+       float f = (float)t / (float)d;
+       return a + (b - a) * f;
+}
+
+static float interpolate(Slope *aStartSlope, Slope *aEndSlope, float aMidValue, int aTime, int aDuration)
+{
+       int steadyTime = aDuration - (aStartSlope->mTime + aEndSlope->mTime);
+
+       if (steadyTime >= 0)
+       {
+               // Interpolate to a midpoint, stay there for a while, then interpolate to end
+
+               if (aTime < aStartSlope->mTime)
+               {
+                       // interpolate to the first value
+                       return lerp(aStartSlope->mValue, aMidValue, aTime, aStartSlope->mTime);
+               }
+               // reached midpoint
+
+               aTime -= aStartSlope->mTime;
+
+               if (aTime <= steadyTime)
+               {
+                       // still at steady state
+                       return aMidValue;  
+               }
+
+               // interpolate to the end
+               return lerp(aMidValue, aEndSlope->mValue, aTime - steadyTime, aEndSlope->mTime);
+       }
+       else
+       {
+               // No steady state
+               float f = 1.0f - ((float) aTime / (float) aDuration);
+               float sp = lerp(aStartSlope->mValue, aMidValue, aTime, aStartSlope->mTime);
+               float ep = lerp(aEndSlope->mValue, aMidValue, aDuration - aTime, aEndSlope->mTime);
+               return f * sp + ((float) 1.0 - f) * ep;
+       }
+}
+
+
+
+void klatt::initsynth(int aElementCount,unsigned char *aElement)
+{
+       mElement = aElement;
+       mElementCount = aElementCount;
+       mElementIndex = 0;
+       mLastElement = &gElement[0];
+       mSeed = 5;
+       mTStress = 0;
+       mNTStress = 0;
+       mFrame.mF0FundamentalFreq = mBaseF0;
+       mTop = 1.1f * mFrame.mF0FundamentalFreq;
+       mFrame.mNasalPoleFreq = (int)mLastElement->mInterpolator[ELM_FN].mSteady;
+       mFrame.mFormant1ParallelBandwidth = mFrame.mFormant1Bandwidth = 60;
+       mFrame.mFormant2ParallelBandwidth = mFrame.mFormant2Bandwidth = 90;
+       mFrame.mFormant3ParallelBandwidth = mFrame.mFormant3Bandwidth = 150;
+//     mFrame.mFormant4ParallelBandwidth = (default)
+
+       // Set stress attack/decay slope 
+       mStressS.mTime = 40;
+       mStressE.mTime = 40;
+       mStressE.mValue = 0.0;
+}
+
+int klatt::synth(int /* aSampleCount */, short *aSamplePointer)
+{
+       short *samp = aSamplePointer;
+
+       if (mElementIndex >= mElementCount)
+               return -1;
+
+       Element * currentElement = &gElement[mElement[mElementIndex++]];
+       int dur = mElement[mElementIndex++];
+       mElementIndex++; // skip stress 
+       
+       if (currentElement->mRK == 31) // "END"
+       {
+               // Reset the fundamental frequency top
+               mFrame.mF0FundamentalFreq = mBaseF0;
+               mTop = 1.1f * mFrame.mF0FundamentalFreq;
+       }
+
+       // Skip zero length elements which are only there to affect
+       // boundary values of adjacent elements         
+
+       if (dur > 0)
+       {
+               Element * ne = (mElementIndex < mElementCount) ? &gElement[mElement[mElementIndex]] : &gElement[0];
+               Slope start[ELM_COUNT];
+               Slope end[ELM_COUNT];
+               int t;
+
+               if (currentElement->mRK > mLastElement->mRK)
+               {
+                       set_trans(start, currentElement, mLastElement, 0, 's');
+                       // we dominate last 
+               }
+               else
+               {
+                       set_trans(start, mLastElement, currentElement, 1, 's');
+                       // last dominates us 
+               }
+
+               if (ne->mRK > currentElement->mRK)
+               {
+                       set_trans(end, ne, currentElement, 1, 'e');
+                       // next dominates us 
+               }
+               else
+               {
+                       set_trans(end, currentElement, ne, 0, 'e');
+                       // we dominate next 
+               }
+
+               for (t = 0; t < dur; t++, mTStress++)
+               {
+                       float base = mTop * 0.8f; // 3 * top / 5 
+                       float tp[ELM_COUNT];
+
+                       if (mTStress == mNTStress)
+                       {
+                               int j = mElementIndex;
+                               mStressS = mStressE;
+                               mTStress = 0;
+                               mNTStress = dur;
+
+                               while (j <= mElementCount)
+                               {
+                                       Element * e   = (j < mElementCount) ? &gElement[mElement[j++]] : &gElement[0];
+                                       int du = (j < mElementCount) ? mElement[j++] : 0;
+                                       int s  = (j < mElementCount) ? mElement[j++] : 3;
+
+                                       if (s || e->mFeat & ELM_FEATURE_VWL)
+                                       {
+                                               int d = 0;
+
+                                               if (s)
+                                                       mStressE.mValue = (float) s / 3;
+                                               else
+                                                       mStressE.mValue = (float) 0.1;
+
+                                               do
+                                               {
+                                                       d += du;
+                                                       e = (j < mElementCount) ? &gElement[mElement[j++]] : &gElement[0];
+                                                       du = mElement[j++];
+                                               }
+
+                                               while ((e->mFeat & ELM_FEATURE_VWL) && mElement[j++] == s);
+
+                                               mNTStress += d / 2;
+
+                                               break;
+                                       }
+
+                                       mNTStress += du;
+                               }
+                       }
+
+                       int j;
+                       for (j = 0; j < ELM_COUNT; j++)
+                       {
+                               tp[j] = interpolate(&start[j], &end[j], (float) currentElement->mInterpolator[j].mSteady, t, dur);
+                       }
+
+                       // Now call the synth for each frame 
+
+                       mFrame.mF0FundamentalFreq = (int)(base + (mTop - base) * interpolate(&mStressS, &mStressE, (float)0, mTStress, mNTStress));
+                       mFrame.mVoicingAmpdb = mFrame.mPalallelVoicingAmpdb = (int)tp[ELM_AV];
+                       mFrame.mFricationAmpdb = (int)tp[ELM_AF];
+                       mFrame.mNasalZeroFreq = (int)tp[ELM_FN];
+                       mFrame.mAspirationAmpdb = (int)tp[ELM_ASP];
+                       mFrame.mVoicingBreathiness = (int)tp[ELM_AVC];
+                       mFrame.mFormant1ParallelBandwidth = mFrame.mFormant1Bandwidth = (int)tp[ELM_B1];
+                       mFrame.mFormant2ParallelBandwidth = mFrame.mFormant2Bandwidth = (int)tp[ELM_B2];
+                       mFrame.mFormant3ParallelBandwidth = mFrame.mFormant3Bandwidth = (int)tp[ELM_B3];
+                       mFrame.mFormant1Freq = (int)tp[ELM_F1];
+                       mFrame.mFormant2Freq = (int)tp[ELM_F2];
+                       mFrame.mFormant3Freq = (int)tp[ELM_F3];
+
+                       // AMP_ADJ + is a kludge to get amplitudes up to klatt-compatible levels
+                               
+                               
+                       //pars.mParallelNasalPoleAmpdb  = AMP_ADJ + tp[ELM_AN];
+                               
+                       mFrame.mBypassFricationAmpdb = AMP_ADJ + (int)tp[ELM_AB];
+                       mFrame.mFormant5Ampdb = AMP_ADJ + (int)tp[ELM_A5];
+                       mFrame.mFormant6Ampdb = AMP_ADJ + (int)tp[ELM_A6];
+                       mFrame.mFormant1Ampdb = AMP_ADJ + (int)tp[ELM_A1];
+                       mFrame.mFormant2Ampdb = AMP_ADJ + (int)tp[ELM_A2];
+                       mFrame.mFormant3Ampdb = AMP_ADJ + (int)tp[ELM_A3];
+                       mFrame.mFormant4Ampdb = AMP_ADJ + (int)tp[ELM_A4];
+
+                       parwave(samp);
+
+                       samp += mNspFr;
+
+                       // Declination of f0 envelope 0.25Hz / cS 
+                       mTop -= mBaseDeclination;// 0.5;
+               }
+       }
+
+       mLastElement = currentElement;
+
+       return (int)(samp - aSamplePointer);
+}
+
+
+void klatt::init(int aBaseFrequency, float aBaseSpeed, float aBaseDeclination, int aBaseWaveform)
+{
+       mBaseF0 = aBaseFrequency;
+       mBaseSpeed = aBaseSpeed;
+       mBaseDeclination = aBaseDeclination;
+       mBaseWaveform = aBaseWaveform;
+
+    mSampleRate = 11025;
+    mF0Flutter = 0;
+       mF0FundamentalFreq = mBaseF0;
+       mFrame.mF0FundamentalFreq = mBaseF0;
+
+       int FLPhz = (950 * mSampleRate) / 10000;
+       int BLPhz = (630 * mSampleRate) / 10000;
+       mNspFr = (int)(mSampleRate * mBaseSpeed) / 1000; 
+
+       mDownSampLowPassFilter.initResonator(FLPhz, BLPhz, mSampleRate);
+
+       mNPer = 0;                        /* LG */
+       mT0 = 0;                          /* LG */
+
+       mVLast = 0;                       /* Previous output of voice  */
+       mNLast = 0;                       /* Previous output of random number generator  */
+       mGlotLast = 0;                    /* Previous value of glotout  */
+}
diff --git a/third_party/soloud_speech/klatt.h b/third_party/soloud_speech/klatt.h
new file mode 100644 (file)
index 0000000..6621090
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef KLATT_H
+#define KLATT_H
+
+#include "resonator.h"
+
+#define CASCADE_PARALLEL      1
+#define ALL_PARALLEL          2
+#define NPAR                 40
+
+class klatt_frame
+{
+public:
+       int mF0FundamentalFreq;          // Voicing fund freq in Hz                       
+       int mVoicingAmpdb;               // Amp of voicing in dB,            0 to   70    
+       int mFormant1Freq;               // First formant freq in Hz,        200 to 1300  
+       int mFormant1Bandwidth;          // First formant bw in Hz,          40 to 1000   
+       int mFormant2Freq;               // Second formant freq in Hz,       550 to 3000  
+       int mFormant2Bandwidth;          // Second formant bw in Hz,         40 to 1000   
+       int mFormant3Freq;               // Third formant freq in Hz,        1200 to 4999 
+       int mFormant3Bandwidth;          // Third formant bw in Hz,          40 to 1000   
+       int mFormant4Freq;               // Fourth formant freq in Hz,       1200 to 4999 
+       int mFormant4Bandwidth;          // Fourth formant bw in Hz,         40 to 1000   
+       int mFormant5Freq;               // Fifth formant freq in Hz,        1200 to 4999 
+       int mFormant5Bandwidth;          // Fifth formant bw in Hz,          40 to 1000   
+       int mFormant6Freq;               // Sixth formant freq in Hz,        1200 to 4999 
+       int mFormant6Bandwidth;          // Sixth formant bw in Hz,          40 to 2000   
+       int mNasalZeroFreq;              // Nasal zero freq in Hz,           248 to  528  
+       int mNasalZeroBandwidth;         // Nasal zero bw in Hz,             40 to 1000   
+       int mNasalPoleFreq;              // Nasal pole freq in Hz,           248 to  528  
+       int mNasalPoleBandwidth;         // Nasal pole bw in Hz,             40 to 1000   
+       int mAspirationAmpdb;            // Amp of aspiration in dB,         0 to   70    
+       int mNoSamplesInOpenPeriod;      // # of samples in open period,     10 to   65   
+       int mVoicingBreathiness;         // Breathiness in voicing,          0 to   80    
+       int mVoicingSpectralTiltdb;      // Voicing spectral tilt in dB,     0 to   24    
+       int mFricationAmpdb;             // Amp of frication in dB,          0 to   80    
+       int mSkewnessOfAlternatePeriods; // Skewness of alternate periods,   0 to   40 in sample#/2
+       int mFormant1Ampdb;              // Amp of par 1st formant in dB,    0 to   80  
+       int mFormant1ParallelBandwidth;  // Par. 1st formant bw in Hz,       40 to 1000 
+       int mFormant2Ampdb;              // Amp of F2 frication in dB,       0 to   80  
+       int mFormant2ParallelBandwidth;  // Par. 2nd formant bw in Hz,       40 to 1000 
+       int mFormant3Ampdb;              // Amp of F3 frication in dB,       0 to   80  
+       int mFormant3ParallelBandwidth;  // Par. 3rd formant bw in Hz,       40 to 1000 
+       int mFormant4Ampdb;              // Amp of F4 frication in dB,       0 to   80  
+       int mFormant4ParallelBandwidth;  // Par. 4th formant bw in Hz,       40 to 1000 
+       int mFormant5Ampdb;              // Amp of F5 frication in dB,       0 to   80  
+       int mFormant5ParallelBandwidth;  // Par. 5th formant bw in Hz,       40 to 1000 
+       int mFormant6Ampdb;              // Amp of F6 (same as r6pa),        0 to   80  
+       int mFormant6ParallelBandwidth;  // Par. 6th formant bw in Hz,       40 to 2000 
+       int mParallelNasalPoleAmpdb;     // Amp of par nasal pole in dB,     0 to   80  
+       int mBypassFricationAmpdb;       // Amp of bypass fric. in dB,       0 to   80  
+       int mPalallelVoicingAmpdb;       // Amp of voicing,  par in dB,      0 to   70  
+       int mOverallGaindb;              // Overall gain, 60 dB is unity,    0 to   60  
+       klatt_frame();
+};
+
+class darray;
+class Element;
+
+class Slope
+{
+public:
+       float mValue;                   /* boundary value */
+       int mTime;                      /* transition time */
+       Slope() 
+       {
+               mValue = 0;
+               mTime = 0;
+       }
+};
+
+
+enum KLATT_WAVEFORM
+{
+       KW_SAW,
+       KW_TRIANGLE,
+       KW_SIN,
+       KW_SQUARE,
+       KW_PULSE,
+       KW_NOISE,
+       KW_WARBLE
+};
+
+class klatt
+{
+       // resonators
+       resonator mParallelFormant1, mParallelFormant2, mParallelFormant3, 
+                     mParallelFormant4, mParallelFormant5, mParallelFormant6,
+                     mParallelResoNasalPole, mNasalPole, mNasalZero, 
+                         mCritDampedGlotLowPassFilter, mDownSampLowPassFilter, mOutputLowPassFilter;
+public:
+       int mBaseF0;
+       float mBaseSpeed;
+       float mBaseDeclination;
+       int mBaseWaveform;
+
+       int mF0Flutter;
+       int mSampleRate;
+       int mNspFr;
+       int mF0FundamentalFreq;        // Voicing fund freq in Hz  
+       int mVoicingAmpdb;          // Amp of voicing in dB,    0 to   70  
+       int mSkewnessOfAlternatePeriods;         // Skewness of alternate periods,0 to   40  
+       int mTimeCount;     // used for f0 flutter
+       int mNPer;          // Current loc in voicing period   40000 samp/s
+       int mT0;            // Fundamental period in output samples times 4 
+       int mNOpen;         // Number of samples in open phase of period  
+       int mNMod;          // Position in period to begin noise amp. modul 
+
+       // Various amplitude variables used in main loop
+
+       float mAmpVoice;     // mVoicingAmpdb converted to linear gain  
+       float mAmpBypas;     // mBypassFricationAmpdb converted to linear gain  
+       float mAmpAspir;     // AP converted to linear gain  
+       float mAmpFrica;     // mFricationAmpdb converted to linear gain  
+       float mAmpBreth;     // ATURB converted to linear gain  
+
+       // State variables of sound sources
+
+       int mSkew;                  // Alternating jitter, in half-period units  
+       float mVLast;               // Previous output of voice  
+       float mNLast;               // Previous output of random number generator  
+       float mGlotLast;            // Previous value of glotout  
+       float mDecay;               // mVoicingSpectralTiltdb converted to exponential time const  
+       float mOneMd;               // in voicing one-pole ELM_FEATURE_LOW-pass filter  
+
+       unsigned int mSeed;                     // random seed
+
+
+
+       float natural_source(int aNper);
+
+       void frame_init();
+       void flutter();
+       void pitch_synch_par_reset(int ns);
+       void parwave(short int *jwave);
+       void init(int aBaseFrequency = 1330, float aBaseSpeed = 10.0f, float aBaseDeclination = 0.5f, int aBaseWaveform = KW_SAW);
+       static int phone_to_elm(char *aPhoneme, int aCount, darray *aElement);
+
+       int mElementCount;
+       unsigned char *mElement;
+       int mElementIndex;
+       klatt_frame mFrame;
+       Element * mLastElement;
+       int mTStress;
+       int mNTStress;
+       Slope mStressS;
+       Slope mStressE;
+       float mTop;
+       void initsynth(int aElementCount,unsigned char *aElement);
+       int synth(int aSampleCount, short *aSamplePointer);
+       klatt();
+};
+
+#endif
\ No newline at end of file
diff --git a/third_party/soloud_speech/legal_readme.txt b/third_party/soloud_speech/legal_readme.txt
new file mode 100644 (file)
index 0000000..878ec6a
--- /dev/null
@@ -0,0 +1,38 @@
+The speech synth is based on rsynth by the late 
+Nick Ing-Simmons (et al).
+
+He described the legal status as:
+
+    This is a text to speech system produced by 
+    integrating various pieces of code and tables 
+    of data, which are all (I believe) in the 
+    public domain.
+    
+Since then, the rsynth source code has passed legal
+checks by several open source organizations, so it
+"should" be pretty safe.
+
+The primary copyright claims seem to have to do
+with text-to-speech dictionary use, which I've
+removed completely.
+
+I've done some serious refactoring, clean-up and 
+feature removal on the source, as all I need is 
+"a" free, simple speech synth, not a "good" 
+speech synth. Since I've removed a bunch of stuff,
+this is probably safer public domain release
+than the original.
+
+(I'm rather surprised there's no good public domain
+speech synths out there; after all, it's 2013..)
+
+I'm placing my changes in public domain as well,
+or if that's not acceptable for you, then CC0:
+http://creativecommons.org/publicdomain/zero/1.0/
+
+The SoLoud interface files (soloud_speech.*) are
+under ZLib/LibPNG license.
+
+-- Jari Komppa
+   2013
+   
\ No newline at end of file
diff --git a/third_party/soloud_speech/resonator.cpp b/third_party/soloud_speech/resonator.cpp
new file mode 100644 (file)
index 0000000..6214444
--- /dev/null
@@ -0,0 +1,74 @@
+#include <math.h>
+#include "resonator.h"
+
+#ifndef PI
+#define PI 3.1415926535897932384626433832795f
+#endif
+
+/* Convert formant freqencies and bandwidth into resonator difference equation coefficents
+       */
+void resonator::initResonator(
+       int aFrequency,                       /* Frequency of resonator in Hz  */
+       int aBandwidth,                      /* Bandwidth of resonator in Hz  */
+       int aSamplerate)
+{
+       float arg = (-PI / aSamplerate) * aBandwidth;
+       float r = (float)exp(arg);  
+       mC = -(r * r);             
+       arg = (-2.0f * PI / aSamplerate) * aFrequency;
+       mB = r * (float)cos(arg) * 2.0f;   
+       mA = 1.0f - mB - mC;    
+}
+
+/* Convert formant freqencies and bandwidth into anti-resonator difference equation constants
+       */
+void resonator::initAntiresonator(
+       int aFrequency,                       /* Frequency of resonator in Hz  */
+       int aBandwidth,                      /* Bandwidth of resonator in Hz  */
+       int aSamplerate)
+{
+       initResonator(aFrequency, aBandwidth, aSamplerate); /* First compute ordinary resonator coefficients */
+       /* Now convert to antiresonator coefficients */
+       mA = 1.0f / mA;             /* a'=  1/a */
+       mB *= -mA;                  /* b'= -b/a */
+       mC *= -mA;                  /* c'= -c/a */
+}
+
+/* Generic resonator function */
+float resonator::resonate(float input)
+{
+       float x = mA * input + mB * mP1 + mC * mP2;
+       mP2 = mP1;
+       mP1 = x;
+       return x;
+}
+
+/* Generic anti-resonator function
+       Same as resonator except that a,b,c need to be set with initAntiresonator()
+       and we save inputs in p1/p2 rather than outputs.
+       There is currently only one of these - "mNasalZero"
+*/
+/*  Output = (mNasalZero.a * input) + (mNasalZero.b * oldin1) + (mNasalZero.c * oldin2) */
+
+float resonator::antiresonate(float input)
+{
+       float x = mA * input + mB * mP1 + mC * mP2;
+       mP2 = mP1;
+       mP1 = input;
+       return x;
+}
+
+resonator::resonator()
+{
+       mA = mB = mC = mP1 = mP2 = 0;
+}
+
+resonator::~resonator()
+{
+}
+
+void resonator::setGain(float aG)
+{
+       mA *= aG;
+}
+
diff --git a/third_party/soloud_speech/resonator.h b/third_party/soloud_speech/resonator.h
new file mode 100644 (file)
index 0000000..2e75e31
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef RESONATOR_H
+#define RESONATOR_H
+
+class resonator
+{
+       float mA, mB, mC, mP1, mP2;
+public:
+
+       /* Convert formant freqencies and bandwidth into resonator difference equation coefficents
+        */
+       void initResonator(
+               int aFrequency,                       /* Frequency of resonator in Hz  */
+               int aBandwidth,                      /* Bandwidth of resonator in Hz  */
+               int aSamplerate);
+
+       /* Convert formant freqencies and bandwidth into anti-resonator difference equation constants
+        */
+       void initAntiresonator(
+               int aFrequency,                       /* Frequency of resonator in Hz  */
+               int aBandwidth,                      /* Bandwidth of resonator in Hz  */
+               int aSamplerate);
+
+       /* Set gain */
+       void setGain(float aG);
+
+       /* Generic resonator function */
+       float resonate(float input);
+
+       /* Generic anti-resonator function
+          Same as resonator except that a,b,c need to be set with initAntiresonator()
+          and we save inputs in p1/p2 rather than outputs.
+          There is currently only one of these - "mNasalZero"
+       
+          Output = (mNasalZero.a * input) + (mNasalZero.b * oldin1) + (mNasalZero.c * oldin2) 
+        */
+
+       float antiresonate(float input);
+
+       resonator();
+
+       ~resonator();
+};
+
+#endif
\ No newline at end of file
diff --git a/third_party/soloud_speech/tts.cpp b/third_party/soloud_speech/tts.cpp
new file mode 100644 (file)
index 0000000..8c825b6
--- /dev/null
@@ -0,0 +1,1425 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "darray.h"
+#include "tts.h"
+
+static const char *ASCII[] =
+{
+       "null", "", "", "",
+       "", "", "", "",
+       "", "", "", "",
+       "", "", "", "",
+       "", "", "", "",
+       "", "", "", "",
+       "", "", "", "",
+       "", "", "", "",
+       "space", "exclamation mark", "double quote", "hash",
+       "dollar", "percent", "ampersand", "quote",
+       "open parenthesis", "close parenthesis", "asterisk", "plus",
+       "comma", "minus", "full stop", "slash",
+       "zero", "one", "two", "three",
+       "four", "five", "six", "seven",
+       "eight", "nine", "colon", "semi colon",
+       "less than", "equals", "greater than", "question mark",
+#ifndef ALPHA_IN_DICT
+       "at", "ay", "bee", "see",
+       "dee", "e", "eff", "gee",
+       "aych", "i", "jay", "kay",
+       "ell", "em", "en", "ohe",
+       "pee", "kju", "are", "es",
+       "tee", "you", "vee", "double you",
+       "eks", "why", "zed", "open bracket",
+#else                             /* ALPHA_IN_DICT */
+       "at", "A", "B", "C",
+       "D", "E", "F", "G",
+       "H", "I", "J", "K",
+       "L", "M", "N", "O",
+       "P", "Q", "R", "S",
+       "T", "U", "V", "W",
+       "X", "Y", "Z", "open bracket",
+#endif                            /* ALPHA_IN_DICT */
+       "back slash", "close bracket", "circumflex", "underscore",
+#ifndef ALPHA_IN_DICT
+       "back quote", "ay", "bee", "see",
+       "dee", "e", "eff", "gee",
+       "aych", "i", "jay", "kay",
+       "ell", "em", "en", "ohe",
+       "pee", "kju", "are", "es",
+       "tee", "you", "vee", "double you",
+       "eks", "why", "zed", "open brace",
+#else                             /* ALPHA_IN_DICT */
+       "back quote", "A", "B", "C",
+       "D", "E", "F", "G",
+       "H", "I", "J", "K",
+       "L", "M", "N", "O",
+       "P", "Q", "R", "S",
+       "T", "U", "V", "W",
+       "X", "Y", "Z", "open brace",
+#endif                            /* ALPHA_IN_DICT */
+       "vertical bar", "close brace", "tilde", "delete",
+       NULL
+};
+
+/* Context definitions */
+static const char Anything[] = "";
+/* No context requirement */
+
+static const char Nothing[] = " ";
+/* Context is beginning or end of word */
+
+static const char Silent[] = "";
+/* No phonemes */
+
+
+#define LEFT_PART       0
+#define MATCH_PART      1
+#define RIGHT_PART      2
+#define OUT_PART        3
+
+typedef const char *Rule[4];
+/* Rule is an array of 4 character pointers */
+
+
+/*0 = Punctuation */
+/*
+**      LEFT_PART       MATCH_PART      RIGHT_PART      OUT_PART
+*/
+
+
+static Rule punct_rules[] =
+{
+       {Anything, " ", Anything, " "},
+       {Anything, "-", Anything, ""},
+       {".", "'S", Anything, "z"},
+       {"#:.E", "'S", Anything, "z"},
+       {"#", "'S", Anything, "z"},
+       {Anything, "'", Anything, ""},
+       {Anything, ",", Anything, " "},
+       {Anything, ".", Anything, " "},
+       {Anything, "?", Anything, " "},
+       {Anything, "!", Anything, " "},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule A_rules[] =
+{
+       {Anything, "A", Nothing, "@"},
+       {Nothing, "ARE", Nothing, "0r"},
+       {Nothing, "AR", "O", "@r"},
+       {Anything, "AR", "#", "er"},
+       {"^", "AS", "#", "eIs"},
+       {Anything, "A", "WA", "@"},
+       {Anything, "AW", Anything, "O"},
+       {" :", "ANY", Anything, "eni"},
+       {Anything, "A", "^+#", "eI"},
+       {"#:", "ALLY", Anything, "@li"},
+       {Nothing, "AL", "#", "@l"},
+       {Anything, "AGAIN", Anything, "@gen"},
+       {"#:", "AG", "E", "IdZ"},
+       {Anything, "A", "^+:#", "&"},
+       {" :", "A", "^+ ", "eI"},
+       {Anything, "A", "^%", "eI"},
+       {Nothing, "ARR", Anything, "@r"},
+       {Anything, "ARR", Anything, "&r"},
+       {" :", "AR", Nothing, "0r"},
+       {Anything, "AR", Nothing, "3"},
+       {Anything, "AR", Anything, "0r"},
+       {Anything, "AIR", Anything, "er"},
+       {Anything, "AI", Anything, "eI"},
+       {Anything, "AY", Anything, "eI"},
+       {Anything, "AU", Anything, "O"},
+       {"#:", "AL", Nothing, "@l"},
+       {"#:", "ALS", Nothing, "@lz"},
+       {Anything, "ALK", Anything, "Ok"},
+       {Anything, "AL", "^", "Ol"},
+       {" :", "ABLE", Anything, "eIb@l"},
+       {Anything, "ABLE", Anything, "@b@l"},
+       {Anything, "ANG", "+", "eIndZ"},
+       {"^", "A", "^#", "eI"},
+       {Anything, "A", Anything, "&"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule B_rules[] =
+{
+       {Nothing, "BE", "^#", "bI"},
+       {Anything, "BEING", Anything, "biIN"},
+       {Nothing, "BOTH", Nothing, "b@UT"},
+       {Nothing, "BUS", "#", "bIz"},
+       {Anything, "BUIL", Anything, "bIl"},
+       {Anything, "B", Anything, "b"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule C_rules[] =
+{
+       {Nothing, "CH", "^", "k"},
+       {"^E", "CH", Anything, "k"},
+       {Anything, "CH", Anything, "tS"},
+       {" S", "CI", "#", "saI"},
+       {Anything, "CI", "A", "S"},
+       {Anything, "CI", "O", "S"},
+       {Anything, "CI", "EN", "S"},
+       {Anything, "C", "+", "s"},
+       {Anything, "CK", Anything, "k"},
+       {Anything, "COM", "%", "kVm"},
+       {Anything, "C", Anything, "k"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule D_rules[] =
+{
+       {"#:", "DED", Nothing, "dId"},
+       {".E", "D", Nothing, "d"},
+       {"#:^E", "D", Nothing, "t"},
+       {Nothing, "DE", "^#", "dI"},
+       {Nothing, "DO", Nothing, "mDU"},
+       {Nothing, "DOES", Anything, "dVz"},
+       {Nothing, "DOING", Anything, "duIN"},
+       {Nothing, "DOW", Anything, "daU"},
+       {Anything, "DU", "A", "dZu"},
+       {Anything, "D", Anything, "d"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule E_rules[] =
+{
+       {"#:", "E", Nothing, ""},
+       {"':^", "E", Nothing, ""},
+       {" :", "E", Nothing, "i"},
+       {"#", "ED", Nothing, "d"},
+       {"#:", "E", "D ", ""},
+       {Anything, "EV", "ER", "ev"},
+       {Anything, "E", "^%", "i"},
+       {Anything, "ERI", "#", "iri"},
+       {Anything, "ERI", Anything, "erI"},
+       {"#:", "ER", "#", "3"},
+       {Anything, "ER", "#", "er"},
+       {Anything, "ER", Anything, "3"},
+       {Nothing, "EVEN", Anything, "iven"},
+       {"#:", "E", "W", ""},
+       {"T", "EW", Anything, "u"},
+       {"S", "EW", Anything, "u"},
+       {"R", "EW", Anything, "u"},
+       {"D", "EW", Anything, "u"},
+       {"L", "EW", Anything, "u"},
+       {"Z", "EW", Anything, "u"},
+       {"N", "EW", Anything, "u"},
+       {"J", "EW", Anything, "u"},
+       {"TH", "EW", Anything, "u"},
+       {"CH", "EW", Anything, "u"},
+       {"SH", "EW", Anything, "u"},
+       {Anything, "EW", Anything, "ju"},
+       {Anything, "E", "O", "i"},
+       {"#:S", "ES", Nothing, "Iz"},
+       {"#:C", "ES", Nothing, "Iz"},
+       {"#:G", "ES", Nothing, "Iz"},
+       {"#:Z", "ES", Nothing, "Iz"},
+       {"#:X", "ES", Nothing, "Iz"},
+       {"#:J", "ES", Nothing, "Iz"},
+       {"#:CH", "ES", Nothing, "Iz"},
+       {"#:SH", "ES", Nothing, "Iz"},
+       {"#:", "E", "S ", ""},
+       {"#:", "ELY", Nothing, "li"},
+       {"#:", "EMENT", Anything, "ment"},
+       {Anything, "EFUL", Anything, "fUl"},
+       {Anything, "EE", Anything, "i"},
+       {Anything, "EARN", Anything, "3n"},
+       {Nothing, "EAR", "^", "3"},
+       {Anything, "EAD", Anything, "ed"},
+       {"#:", "EA", Nothing, "i@"},
+       {Anything, "EA", "SU", "e"},
+       {Anything, "EA", Anything, "i"},
+       {Anything, "EIGH", Anything, "eI"},
+       {Anything, "EI", Anything, "i"},
+       {Nothing, "EYE", Anything, "aI"},
+       {Anything, "EY", Anything, "i"},
+       {Anything, "EU", Anything, "ju"},
+       {Anything, "E", Anything, "e"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule F_rules[] =
+{
+       {Anything, "FUL", Anything, "fUl"},
+       {Anything, "F", Anything, "f"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule G_rules[] =
+{
+       {Anything, "GIV", Anything, "gIv"},
+       {Nothing, "G", "I^", "g"},
+       {Anything, "GE", "T", "ge"},
+       {"SU", "GGES", Anything, "gdZes"},
+       {Anything, "GG", Anything, "g"},
+       {" B#", "G", Anything, "g"},
+       {Anything, "G", "+", "dZ"},
+       {Anything, "GREAT", Anything, "greIt"},
+       {"#", "GH", Anything, ""},
+       {Anything, "G", Anything, "g"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule H_rules[] =
+{
+       {Nothing, "HAV", Anything, "h&v"},
+       {Nothing, "HERE", Anything, "hir"},
+       {Nothing, "HOUR", Anything, "aU3"},
+       {Anything, "HOW", Anything, "haU"},
+       {Anything, "H", "#", "h"},
+       {Anything, "H", Anything, ""},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule I_rules[] =
+{
+       {Nothing, "IAIN", Nothing, "I@n"},
+       {Nothing, "ING", Nothing, "IN"},
+       {Nothing, "IN", Anything, "In"},
+       {Nothing, "I", Nothing, "aI"},
+       {Anything, "IN", "D", "aIn"},
+       {Anything, "IER", Anything, "i3"},
+       {"#:R", "IED", Anything, "id"},
+       {Anything, "IED", Nothing, "aId"},
+       {Anything, "IEN", Anything, "ien"},
+       {Anything, "IE", "T", "aIe"},
+       {" :", "I", "%", "aI"},
+       {Anything, "I", "%", "i"},
+       {Anything, "IE", Anything, "i"},
+       {Anything, "I", "^+:#", "I"},
+       {Anything, "IR", "#", "aIr"},
+       {Anything, "IZ", "%", "aIz"},
+       {Anything, "IS", "%", "aIz"},
+       {Anything, "I", "D%", "aI"},
+       {"+^", "I", "^+", "I"},
+       {Anything, "I", "T%", "aI"},
+       {"#:^", "I", "^+", "I"},
+       {Anything, "I", "^+", "aI"},
+       {Anything, "IR", Anything, "3"},
+       {Anything, "IGH", Anything, "aI"},
+       {Anything, "ILD", Anything, "aIld"},
+       {Anything, "IGN", Nothing, "aIn"},
+       {Anything, "IGN", "^", "aIn"},
+       {Anything, "IGN", "%", "aIn"},
+       {Anything, "IQUE", Anything, "ik"},
+       {"^", "I", "^#", "aI"},
+       {Anything, "I", Anything, "I"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule J_rules[] =
+{
+       {Anything, "J", Anything, "dZ"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule K_rules[] =
+{
+       {Nothing, "K", "N", ""},
+       {Anything, "K", Anything, "k"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule L_rules[] =
+{
+       {Anything, "LO", "C#", "l@U"},
+       {"L", "L", Anything, ""},
+       {"#:^", "L", "%", "@l"},
+       {Anything, "LEAD", Anything, "lid"},
+       {Anything, "L", Anything, "l"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule M_rules[] =
+{
+       {Anything, "MOV", Anything, "muv"},
+       {"#", "MM", "#", "m"},
+       {Anything, "M", Anything, "m"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule N_rules[] =
+{
+       {"E", "NG", "+", "ndZ"},
+       {Anything, "NG", "R", "Ng"},
+       {Anything, "NG", "#", "Ng"},
+       {Anything, "NGL", "%", "Ng@l"},
+       {Anything, "NG", Anything, "N"},
+       {Anything, "NK", Anything, "Nk"},
+       {Nothing, "NOW", Nothing, "naU"},
+       {"#", "NG", Nothing, "Ng"},
+       {Anything, "N", Anything, "n"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule O_rules[] =
+{
+       {Anything, "OF", Nothing, "@v"},
+       {Anything, "OROUGH", Anything, "3@U"},
+       {"#:", "OR", Nothing, "3"},
+       {"#:", "ORS", Nothing, "3z"},
+       {Anything, "OR", Anything, "Or"},
+       {Nothing, "ONE", Anything, "wVn"},
+       {Anything, "OW", Anything, "@U"},
+       {Nothing, "OVER", Anything, "@Uv3"},
+       {Anything, "OV", Anything, "Vv"},
+       {Anything, "O", "^%", "@U"},
+       {Anything, "O", "^EN", "@U"},
+       {Anything, "O", "^I#", "@U"},
+       {Anything, "OL", "D", "@Ul"},
+       {Anything, "OUGHT", Anything, "Ot"},
+       {Anything, "OUGH", Anything, "Vf"},
+       {Nothing, "OU", Anything, "aU"},
+       {"H", "OU", "S#", "aU"},
+       {Anything, "OUS", Anything, "@s"},
+       {Anything, "OUR", Anything, "Or"},
+       {Anything, "OULD", Anything, "Ud"},
+       {"^", "OU", "^L", "V"},
+       {Anything, "OUP", Anything, "up"},
+       {Anything, "OU", Anything, "aU"},
+       {Anything, "OY", Anything, "oI"},
+       {Anything, "OING", Anything, "@UIN"},
+       {Anything, "OI", Anything, "oI"},
+       {Anything, "OOR", Anything, "Or"},
+       {Anything, "OOK", Anything, "Uk"},
+       {Anything, "OOD", Anything, "Ud"},
+       {Anything, "OO", Anything, "u"},
+       {Anything, "O", "E", "@U"},
+       {Anything, "O", Nothing, "@U"},
+       {Anything, "OA", Anything, "@U"},
+       {Nothing, "ONLY", Anything, "@Unli"},
+       {Nothing, "ONCE", Anything, "wVns"},
+       {Anything, "ON'T", Anything, "@Unt"},
+       {"C", "O", "N", "0"},
+       {Anything, "O", "NG", "O"},
+       {" :^", "O", "N", "V"},
+       {"I", "ON", Anything, "@n"},
+       {"#:", "ON", Nothing, "@n"},
+       {"#^", "ON", Anything, "@n"},
+       {Anything, "O", "ST ", "@U"},
+       {Anything, "OF", "^", "Of"},
+       {Anything, "OTHER", Anything, "VD3"},
+       {Anything, "OSS", Nothing, "Os"},
+       {"#:^", "OM", Anything, "Vm"},
+       {Anything, "O", Anything, "0"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule P_rules[] =
+{
+       {Anything, "PH", Anything, "f"},
+       {Anything, "PEOP", Anything, "pip"},
+       {Anything, "POW", Anything, "paU"},
+       {Anything, "PUT", Nothing, "pUt"},
+       {Anything, "P", Anything, "p"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule Q_rules[] =
+{
+       {Anything, "QUAR", Anything, "kwOr"},
+       {Anything, "QU", Anything, "kw"},
+       {Anything, "Q", Anything, "k"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule R_rules[] =
+{
+       {Nothing, "RE", "^#", "ri"},
+       {Anything, "R", Anything, "r"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule S_rules[] =
+{
+       {Anything, "SH", Anything, "S"},
+       {"#", "SION", Anything, "Z@n"},
+       {Anything, "SOME", Anything, "sVm"},
+       {"#", "SUR", "#", "Z3"},
+       {Anything, "SUR", "#", "S3"},
+       {"#", "SU", "#", "Zu"},
+       {"#", "SSU", "#", "Su"},
+       {"#", "SED", Nothing, "zd"},
+       {"#", "S", "#", "z"},
+       {Anything, "SAID", Anything, "sed"},
+       {"^", "SION", Anything, "S@n"},
+       {Anything, "S", "S", ""},
+       {".", "S", Nothing, "z"},
+       {"#:.E", "S", Nothing, "z"},
+       {"#:^##", "S", Nothing, "z"},
+       {"#:^#", "S", Nothing, "s"},
+       {"U", "S", Nothing, "s"},
+       {" :#", "S", Nothing, "z"},
+       {Nothing, "SCH", Anything, "sk"},
+       {Anything, "S", "C+", ""},
+       {"#", "SM", Anything, "zm"},
+       {"#", "SN", "'", "z@n"},
+       {Anything, "S", Anything, "s"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule T_rules[] =
+{
+       {Nothing, "THE", Nothing, "D@"},
+       {Anything, "TO", Nothing, "tu"},
+       {Anything, "THAT", Nothing, "D&t"},
+       {Nothing, "THIS", Nothing, "DIs"},
+       {Nothing, "THEY", Anything, "DeI"},
+       {Nothing, "THERE", Anything, "Der"},
+       {Anything, "THER", Anything, "D3"},
+       {Anything, "THEIR", Anything, "Der"},
+       {Nothing, "THAN", Nothing, "D&n"},
+       {Nothing, "THEM", Nothing, "Dem"},
+       {Anything, "THESE", Nothing, "Diz"},
+       {Nothing, "THEN", Anything, "Den"},
+       {Anything, "THROUGH", Anything, "Tru"},
+       {Anything, "THOSE", Anything, "D@Uz"},
+       {Anything, "THOUGH", Nothing, "D@U"},
+       {Nothing, "THUS", Anything, "DVs"},
+       {Anything, "TH", Anything, "T"},
+       {"#:", "TED", Nothing, "tId"},
+       {"S", "TI", "#N", "tS"},
+       {Anything, "TI", "O", "S"},
+       {Anything, "TI", "A", "S"},
+       {Anything, "TIEN", Anything, "S@n"},
+       {Anything, "TUR", "#", "tS3"},
+       {Anything, "TU", "A", "tSu"},
+       {Nothing, "TWO", Anything, "tu"},
+       {Anything, "T", Anything, "t"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule U_rules[] =
+{
+       {Nothing, "UN", "I", "jun"},
+       {Nothing, "UN", Anything, "Vn"},
+       {Nothing, "UPON", Anything, "@pOn"},
+       {"T", "UR", "#", "Ur"},
+       {"S", "UR", "#", "Ur"},
+       {"R", "UR", "#", "Ur"},
+       {"D", "UR", "#", "Ur"},
+       {"L", "UR", "#", "Ur"},
+       {"Z", "UR", "#", "Ur"},
+       {"N", "UR", "#", "Ur"},
+       {"J", "UR", "#", "Ur"},
+       {"TH", "UR", "#", "Ur"},
+       {"CH", "UR", "#", "Ur"},
+       {"SH", "UR", "#", "Ur"},
+       {Anything, "UR", "#", "jUr"},
+       {Anything, "UR", Anything, "3"},
+       {Anything, "U", "^ ", "V"},
+       {Anything, "U", "^^", "V"},
+       {Anything, "UY", Anything, "aI"},
+       {" G", "U", "#", ""},
+       {"G", "U", "%", ""},
+       {"G", "U", "#", "w"},
+       {"#N", "U", Anything, "ju"},
+       {"T", "U", Anything, "u"},
+       {"S", "U", Anything, "u"},
+       {"R", "U", Anything, "u"},
+       {"D", "U", Anything, "u"},
+       {"L", "U", Anything, "u"},
+       {"Z", "U", Anything, "u"},
+       {"N", "U", Anything, "u"},
+       {"J", "U", Anything, "u"},
+       {"TH", "U", Anything, "u"},
+       {"CH", "U", Anything, "u"},
+       {"SH", "U", Anything, "u"},
+       {Anything, "U", Anything, "ju"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule V_rules[] =
+{
+       {Anything, "VIEW", Anything, "vju"},
+       {Anything, "V", Anything, "v"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule W_rules[] =
+{
+       {Nothing, "WERE", Anything, "w3"},
+       {Anything, "WA", "S", "w0"},
+       {Anything, "WA", "T", "w0"},
+       {Anything, "WHERE", Anything, "hwer"},
+       {Anything, "WHAT", Anything, "hw0t"},
+       {Anything, "WHOL", Anything, "h@Ul"},
+       {Anything, "WHO", Anything, "hu"},
+       {Anything, "WH", Anything, "hw"},
+       {Anything, "WAR", Anything, "wOr"},
+       {Anything, "WOR", "^", "w3"},
+       {Anything, "WR", Anything, "r"},
+       {Anything, "W", Anything, "w"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule X_rules[] =
+{
+       {Anything, "X", Anything, "ks"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule Y_rules[] =
+{
+       {Anything, "YOUNG", Anything, "jVN"},
+       {Nothing, "YOU", Anything, "ju"},
+       {Nothing, "YES", Anything, "jes"},
+       {Nothing, "Y", Anything, "j"},
+       {"#:^", "Y", Nothing, "i"},
+       {"#:^", "Y", "I", "i"},
+       {" :", "Y", Nothing, "aI"},
+       {" :", "Y", "#", "aI"},
+       {" :", "Y", "^+:#", "I"},
+       {" :", "Y", "^#", "aI"},
+       {Anything, "Y", Anything, "I"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule Z_rules[] =
+{
+       {Anything, "Z", Anything, "z"},
+       {Anything, 0, Anything, Silent},
+};
+
+static Rule *Rules[] =
+{
+       punct_rules,
+       A_rules, B_rules, C_rules, D_rules, E_rules, F_rules, G_rules,
+       H_rules, I_rules, J_rules, K_rules, L_rules, M_rules, N_rules,
+       O_rules, P_rules, Q_rules, R_rules, S_rules, T_rules, U_rules,
+       V_rules, W_rules, X_rules, Y_rules, Z_rules
+};
+
+
+static const char *Cardinals[] =
+{
+       "zero", "one", "two", "three", "four", 
+       "five", "six", "seven", "eight", "nine", 
+       "ten", "eleven", "twelve", "thirteen", "fourteen", 
+       "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+};
+
+
+static const char *Twenties[] =
+{
+       "twenty", "thirty", "forty", "fifty",
+       "sixty", "seventy", "eighty", "ninety"
+};
+
+
+static const char *Ordinals[] =
+{
+       "zeroth", "first", "second", "third", "fourth", 
+       "fifth", "sixth", "seventh","eighth", "ninth",
+       "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", 
+       "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
+};
+
+
+static const char *Ord_twenties[] =
+{
+       "twentieth", "thirtieth", "fortieth", "fiftieth",
+       "sixtieth", "seventieth", "eightieth", "ninetieth"
+};
+
+
+/*
+** Translate a number to phonemes.  This version is for CARDINAL numbers.
+**       Note: this is recursive.
+*/
+static int xlate_cardinal(int value, darray *phone)
+{
+       int nph = 0;
+
+       if (value < 0)
+       {
+               nph += xlate_string("minus", phone);
+               value = (-value);
+
+               if (value < 0)                 /* Overflow!  -32768 */
+               {
+                       nph += xlate_string("a lot", phone);
+                       return nph;
+               }
+       }
+
+       if (value >= 1000000000L)
+               /* Billions */
+       {
+               nph += xlate_cardinal(value / 1000000000L, phone);
+               nph += xlate_string("billion", phone);
+               value = value % 1000000000;
+
+               if (value == 0)
+                       return nph;                   /* Even billion */
+
+               if (value < 100)
+                       nph += xlate_string("and", phone);
+
+               /* as in THREE BILLION AND FIVE */
+       }
+
+       if (value >= 1000000L)
+               /* Millions */
+       {
+               nph += xlate_cardinal(value / 1000000L, phone);
+               nph += xlate_string("million", phone);
+               value = value % 1000000L;
+
+               if (value == 0)
+                       return nph;                   /* Even million */
+
+               if (value < 100)
+                       nph += xlate_string("and", phone);
+
+               /* as in THREE MILLION AND FIVE */
+       }
+
+       /* Thousands 1000..1099 2000..99999 */
+       /* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */
+
+       if ((value >= 1000L && value <= 1099L) || value >= 2000L)
+       {
+               nph += xlate_cardinal(value / 1000L, phone);
+               nph += xlate_string("thousand", phone);
+               value = value % 1000L;
+
+               if (value == 0)
+                       return nph;                   /* Even thousand */
+
+               if (value < 100)
+                       nph += xlate_string("and", phone);
+
+               /* as in THREE THOUSAND AND FIVE */
+       }
+
+       if (value >= 100L)
+       {
+               nph += xlate_string(Cardinals[value / 100], phone);
+               nph += xlate_string("hundred", phone);
+               value = value % 100;
+
+               if (value == 0)
+                       return nph;                   /* Even hundred */
+       }
+
+       if (value >= 20)
+       {
+               nph += xlate_string(Twenties[(value - 20) / 10], phone);
+               value = value % 10;
+
+               if (value == 0)
+                       return nph;                   /* Even ten */
+       }
+
+       nph += xlate_string(Cardinals[value], phone);
+
+       return nph;
+}
+
+#if 0
+/*
+** Translate a number to phonemes.  This version is for ORDINAL numbers.
+**       Note: this is recursive.
+*/
+static int xlate_ordinal(int value, darray *phone)
+{
+       int nph = 0;
+
+       if (value < 0)
+       {
+               nph += xlate_string("minus", phone);
+               value = (-value);
+
+               if (value < 0)                 /* Overflow!  -32768 */
+               {
+                       nph += xlate_string("a lot", phone);
+                       return nph;
+               }
+       }
+
+       if (value >= 1000000000L)
+               /* Billions */
+       {
+               nph += xlate_cardinal(value / 1000000000L, phone);
+               value = value % 1000000000;
+
+               if (value == 0)
+               {
+                       nph += xlate_string("billionth", phone);
+                       return nph;                  /* Even billion */
+               }
+
+               nph += xlate_string("billion", phone);
+
+               if (value < 100)
+                       nph += xlate_string("and", phone);
+
+               /* as in THREE BILLION AND FIVE */
+       }
+
+       if (value >= 1000000L)
+               /* Millions */
+       {
+               nph += xlate_cardinal(value / 1000000L, phone);
+               value = value % 1000000L;
+
+               if (value == 0)
+               {
+                       nph += xlate_string("millionth", phone);
+                       return nph;                  /* Even million */
+               }
+
+               nph += xlate_string("million", phone);
+
+               if (value < 100)
+                       nph += xlate_string("and", phone);
+
+               /* as in THREE MILLION AND FIVE */
+       }
+
+       /* Thousands 1000..1099 2000..99999 */
+       /* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */
+
+       if ((value >= 1000L && value <= 1099L) || value >= 2000L)
+       {
+               nph += xlate_cardinal(value / 1000L, phone);
+               value = value % 1000L;
+
+               if (value == 0)
+               {
+                       nph += xlate_string("thousandth", phone);
+                       return nph;                  /* Even thousand */
+               }
+
+               nph += xlate_string("thousand", phone);
+
+               if (value < 100)
+                       nph += xlate_string("and", phone);
+
+               /* as in THREE THOUSAND AND FIVE */
+       }
+
+       if (value >= 100L)
+       {
+               nph += xlate_string(Cardinals[value / 100], phone);
+               value = value % 100;
+
+               if (value == 0)
+               {
+                       nph += xlate_string("hundredth", phone);
+                       return nph;                  /* Even hundred */
+               }
+
+               nph += xlate_string("hundred", phone);
+       }
+
+       if (value >= 20)
+       {
+               if ((value % 10) == 0)
+               {
+                       nph += xlate_string(Ord_twenties[(value - 20) / 10], phone);
+                       return nph;                  /* Even ten */
+               }
+
+               nph += xlate_string(Twenties[(value - 20) / 10], phone);
+
+               value = value % 10;
+       }
+
+       nph += xlate_string(Ordinals[value], phone);
+
+       return nph;
+}
+#endif
+
+static int isvowel(int chr)
+{
+       return (chr == 'A' || chr == 'E' || chr == 'I' ||
+               chr == 'O' || chr == 'U');
+}
+
+static int isconsonant(int chr)
+{
+       return (isupper(chr) && !isvowel(chr));
+}
+
+static int leftmatch(
+       const char *pattern,                    /* first char of pattern to match in text */
+       const char *context)                     /* last char of text to be matched */
+
+{
+       const char *pat;
+       const char *text;
+       int count;
+
+       if (*pattern == '\0')
+               /* null string matches any context */
+       {
+               return 1;
+       }
+
+       /* point to last character in pattern string */
+       count = (int)strlen(pattern);
+
+       pat = pattern + (count - 1);
+
+       text = context;
+
+       for (; count > 0; pat--, count--)
+       {
+               /* First check for simple text or space */
+               if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
+               {
+                       if (*pat != *text)
+                       {
+                               return 0;
+                       }
+                       else
+                       {
+                               text--;
+                               continue;
+                       }
+               }
+
+               switch (*pat)
+               {
+
+               case '#':                   /* One or more vowels */
+
+                       if (!isvowel(*text))
+                               return 0;
+
+                       text--;
+
+                       while (isvowel(*text))
+                               text--;
+
+                       break;
+
+               case ':':                   /* Zero or more consonants */
+                       while (isconsonant(*text))
+                               text--;
+
+                       break;
+
+               case '^':                   /* One consonant */
+                       if (!isconsonant(*text))
+                               return 0;
+
+                       text--;
+
+                       break;
+
+               case '.':                   /* B, D, V, G, J, L, M, N, R, W, Z */
+                       if (*text != 'B' && *text != 'D' && *text != 'V'
+                               && *text != 'G' && *text != 'J' && *text != 'L'
+                               && *text != 'M' && *text != 'N' && *text != 'R'
+                               && *text != 'W' && *text != 'Z')
+                               return 0;
+
+                       text--;
+
+                       break;
+
+               case '+':                   /* E, I or Y (front vowel) */
+                       if (*text != 'E' && *text != 'I' && *text != 'Y')
+                               return 0;
+
+                       text--;
+
+                       break;
+
+               case '%':
+
+               default:
+                       fprintf(stderr, "Bad char in left rule: '%c'\n", *pat);
+
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int rightmatch(
+       const char *pattern,                    /* first char of pattern to match in text */
+       const char *context)                     /* last char of text to be matched */
+{
+       const char *pat;
+       const char *text;
+
+       if (*pattern == '\0')
+               /* null string matches any context */
+               return 1;
+
+       pat = pattern;
+
+       text = context;
+
+       for (pat = pattern; *pat != '\0'; pat++)
+       {
+               /* First check for simple text or space */
+               if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
+               {
+                       if (*pat != *text)
+                       {
+                               return 0;
+                       }
+                       else
+                       {
+                               text++;
+                               continue;
+                       }
+               }
+
+               switch (*pat)
+               {
+
+               case '#':                   /* One or more vowels */
+
+                       if (!isvowel(*text))
+                               return 0;
+
+                       text++;
+
+                       while (isvowel(*text))
+                               text++;
+
+                       break;
+
+               case ':':                   /* Zero or more consonants */
+                       while (isconsonant(*text))
+                               text++;
+
+                       break;
+
+               case '^':                   /* One consonant */
+                       if (!isconsonant(*text))
+                               return 0;
+
+                       text++;
+
+                       break;
+
+               case '.':                   /* B, D, V, G, J, L, M, N, R, W, Z */
+                       if (*text != 'B' && *text != 'D' && *text != 'V'
+                               && *text != 'G' && *text != 'J' && *text != 'L'
+                               && *text != 'M' && *text != 'N' && *text != 'R'
+                               && *text != 'W' && *text != 'Z')
+                               return 0;
+
+                       text++;
+
+                       break;
+
+               case '+':                   /* E, I or Y (front vowel) */
+                       if (*text != 'E' && *text != 'I' && *text != 'Y')
+                               return 0;
+
+                       text++;
+
+                       break;
+
+               case '%':                   /* ER, E, ES, ED, ING, ELY (a suffix) */
+                       if (*text == 'E')
+                       {
+                               text++;
+
+                               if (*text == 'L')
+                               {
+                                       text++;
+
+                                       if (*text == 'Y')
+                                       {
+                                               text++;
+                                               break;
+                                       }
+
+                                       else
+                                       {
+                                               text--;               /* Don't gobble L */
+                                               break;
+                                       }
+                               }
+
+                               else
+                                       if (*text == 'R' || *text == 'S' || *text == 'D')
+                                               text++;
+
+                               break;
+                       }
+
+                       else
+                               if (*text == 'I')
+                               {
+                                       text++;
+
+                                       if (*text == 'N')
+                                       {
+                                               text++;
+
+                                               if (*text == 'G')
+                                               {
+                                                       text++;
+                                                       break;
+                                               }
+                                       }
+
+                                       return 0;
+                               }
+
+                               else
+                                       return 0;
+
+               default:
+                       fprintf(stderr, "Bad char in right rule:'%c'\n", *pat);
+
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static void phone_cat(darray *arg, const char *s)
+{
+       char ch;
+
+       while ((ch = *s++))
+               arg->put(ch);
+}
+
+
+static int find_rule(darray *arg, char *word, int index, Rule *rules)
+{
+       for (;;)                         /* Search for the rule */
+       {
+               Rule *rule;
+               const char *left,
+                       *match,
+                       *right,
+                       *output;
+               int remainder;
+               rule = rules++;
+               match = (*rule)[1];
+
+               if (match == 0)
+                       /* bad symbol! */
+               {
+                       fprintf(stderr, "Error: Can't find rule for: '%c' in \"%s\"\n",
+                               word[index], word);
+                       return index + 1;            /* Skip it! */
+               }
+
+               for (remainder = index; *match != '\0'; match++, remainder++)
+               {
+                       if (*match != word[remainder])
+                               break;
+               }
+
+               if (*match != '\0')
+                       continue;                     /* found missmatch */
+
+               left = (*rule)[0];
+
+               right = (*rule)[2];
+
+               if (!leftmatch(left, &word[index - 1]))
+                       continue;
+
+               if (!rightmatch(right, &word[remainder]))
+                       continue;
+
+               output = (*rule)[3];
+
+               phone_cat(arg, output);
+
+               return remainder;
+       }
+}
+
+static void guess_word(darray *arg, char *word)
+{
+       int index;                       /* Current position in word */
+       int type;                        /* First letter of match part */
+       index = 1;                       /* Skip the initial blank */
+
+       do
+       {
+               if (isupper(word[index]))
+                       type = word[index] - 'A' + 1;
+               else
+                       type = 0;
+
+               index = find_rule(arg, word, index, Rules[type]);
+       }
+
+       while (word[index] != '\0');
+}
+
+
+static int NRL(const char *s, int n, darray *phone)
+{
+       int old = phone->getSize();
+       char *word = (char *) malloc(n + 3); // TODO: may return null
+       char *d = word;
+       *d++ = ' ';
+
+       while (n-- > 0)
+       {
+               char ch = *s++;
+
+               if (islower(ch))
+                       ch = (char)toupper(ch);
+
+               *d++ = ch;
+       }
+
+       *d++ = ' '; // kinda unnecessary
+
+       *d = '\0';
+       guess_word(phone, word);
+       free(word);
+       return phone->getSize() - old;
+}
+
+
+static int spell_out(const char *word, int n, darray *phone)
+{
+       int nph = 0;
+
+       while (n-- > 0)
+       {
+               nph += xlate_string(ASCII[*word++ & 0x7F], phone);
+       }
+
+       return nph;
+}
+
+static int suspect_word(const char *s, int n)
+{
+       int i = 0;
+       int seen_lower = 0;
+       int seen_upper = 0;
+       int seen_vowel = 0;
+       int last = 0;
+
+       for (i = 0; i < n; i++)
+       {
+               char ch = *s++;
+
+               if (i && last != '-' && isupper(ch))
+                       seen_upper = 1;
+
+               if (islower(ch))
+               {
+                       seen_lower = 1;
+                       ch = (char)toupper(ch);
+               }
+
+               if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y')
+                       seen_vowel = 1;
+
+               last = ch;
+       }
+
+       return !seen_vowel || (seen_upper && seen_lower) || !seen_lower;
+}
+
+static int xlate_word(const char *word, int n, darray *phone)
+{
+       int nph = 0;
+
+       if (*word != '[')
+       {
+               if (suspect_word(word, n))
+                       return spell_out(word, n, phone);
+               else
+               {
+                       nph += NRL(word, n, phone);
+               }
+       }
+
+       else
+       {
+               if ((++word)[(--n) - 1] == ']')
+                       n--;
+
+               while (n-- > 0)
+               {
+                       phone->put(*word++);
+                       nph++;
+               }
+       }
+
+       phone->put(' ');
+
+       return nph + 1;
+}
+
+
+int xlate_string(const char *string, darray *phone)
+{
+       int nph = 0;
+       const char *s = string;
+       char ch;
+
+       while (isspace(ch = *s))
+               s++;
+
+       while (*s)
+       {
+               ch = *s;
+               const char *word = s;
+
+               if (isalpha(ch))
+               {
+                       while (isalpha(ch = *s) || ((ch == '\'' || ch == '-' || ch == '.') && isalpha(s[1])))
+                       {
+                               s++;
+                       }
+
+                       if (!ch || isspace(ch) || ispunct(ch) || (isdigit(ch) && !suspect_word(word, (int)(s - word))))
+                       {
+                               nph += xlate_word(word, (int)(s - word), phone);
+                       }
+                       else
+                       {
+                               while (*s && !isspace(*s) && !ispunct(*s))
+                               {
+                                       ch = *s;
+                                       s++;
+                               }
+
+                               nph += spell_out(word, (int)(s - word), phone);
+                       }
+               }
+               else
+               {
+                       if (isdigit(ch) || (ch == '-' && isdigit(s[1])))
+                       {
+                               int sign = (ch == '-') ? -1 : 1;
+                               int value = 0;
+
+                               if (sign < 0)
+                               {
+                                       ch = *++s;
+                               }
+
+                               while (isdigit(ch = *s))
+                               {
+                                       value = value * 10 + ch - '0';
+                                       s++;
+                               }
+
+                               if (ch == '.' && isdigit(s[1]))
+                               {
+                                       word = ++s;
+                                       nph += xlate_cardinal(value * sign, phone);
+                                       nph += xlate_string("point", phone);
+
+                                       while (isdigit(ch = *s))
+                                       {
+                                               s++;
+                                       }
+
+                                       nph += spell_out(word, (int)(s - word), phone);
+                               }
+                               else
+                               {
+                                       /* check for ordinals, date, time etc. can go in here */
+                                       nph += xlate_cardinal(value * sign, phone);
+                               }
+                       }
+                       else
+                       {
+                               if (ch == '[' && strchr(s, ']'))
+                               {
+                                       const char *thisword = s;
+
+                                       while (*s && *s++ != ']')
+                                               /* nothing */
+                                               ;
+
+                                       nph += xlate_word(thisword, (int)(s - thisword), phone);
+                               }
+                               else
+                               {
+                                       if (ispunct(ch))
+                                       {
+                                               switch (ch)
+                                               {
+
+                                               case '!':
+
+                                               case '?':
+
+                                               case '.':
+                                                       s++;
+                                                       phone->put('.');// (' ');
+                                                       break;
+
+                                               case '"':                 /* change pitch ? */
+
+                                               case ':':
+
+                                               case '-':
+
+                                               case ';':
+
+                                               case ',':
+
+                                               case '(':
+
+                                               case ')':
+                                                       s++;
+                                                       phone->put(' ');
+                                                       break;
+
+                                               case '[':
+                                               {
+                                                       const char *e = strchr(s, ']');
+
+                                                       if (e)
+                                                       {
+                                                               s++;
+
+                                                               while (s < e)
+                                                                       phone->put(*s++);
+
+                                                               s = e + 1;
+
+                                                               break;
+                                                       }
+                                               }
+                                               // fallthrough
+                                               default:
+                                                       nph += spell_out(word, 1, phone);
+                                                       s++;
+                                                       break;
+                                               }
+                                       }
+                                       else
+                                       {
+                                               while (*s && !isspace(*s))
+                                               {
+                                                       ch = *s;
+                                                       s++;
+                                               }
+
+                                               nph += spell_out(word, (int)(s - word), phone);
+                                       }
+                               }
+                       }
+
+                       while (isspace(ch = *s))
+                               s++;
+               }
+       }
+
+       return nph;
+}
diff --git a/third_party/soloud_speech/tts.h b/third_party/soloud_speech/tts.h
new file mode 100644 (file)
index 0000000..8966e76
--- /dev/null
@@ -0,0 +1,5 @@
+
+extern int xlate_string (const char *string,darray *phone);
+
+
+       
diff --git a/third_party/stb b/third_party/stb
new file mode 160000 (submodule)
index 0000000..ae721c5
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit ae721c50eaf761660b4f90cc590453cdb0c2acd0
diff --git a/third_party/sun b/third_party/sun
new file mode 160000 (submodule)
index 0000000..a5b6b9e
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit a5b6b9ea53d78b2019201a9fb75b763b7d92873c