--- /dev/null
+[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
--- /dev/null
+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)
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#include <bz/audio/playback_internal.h>
+
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#include <bz/debug/assert.h>
+#include <bz/debug/log.h>
+#include <bz/debug/perfgraph.h>
\ No newline at end of file
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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];
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#include <bz/game/actor_internal.h>
+
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#include <bz/game/scene.h>
+#include <bz/scripting/static_crc32.hpp>
+
+BZIdentifierHash bzSceneChangeEventIdentifier = COMPILE_TIME_CRC32_STR("sceneChange");
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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)
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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");
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+// 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>
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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
--- /dev/null
+#include <bz/memory/allocator.h>
+
--- /dev/null
+#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
--- /dev/null
+#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");
+// }
+//}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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"
+ ]
+}
+*/
+
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#ifndef BZ_SCRIPTING_BINDINGS_H
+#define BZ_SCRIPTING_BINDINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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!!");
+}*/
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#include <bz/types/identifier_internal.h>
+
+#include <crc.h>
+#include <string.h>
+
+BZIdentifierHash bzIdentifierHashFromIdentifier(BZIdentifier identifier) {
+ BZIdentifierHash hash = crc32buf(identifier, strlen(identifier));
+ return hash;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#include <bz/debug/assert.h>
+
+#include <stdlib.h>
+
+void bzAssertExit() {
+ //exit(1);
+}
--- /dev/null
+#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() {
+}
--- /dev/null
+#include <playdate/entrypoint.h>
+
+void resetSystemTimer(void) {
+ playdate->system->resetElapsedTime();
+}
+
+float getSystemTimer(void) {
+ return playdate->system->getElapsedTime();
+}
--- /dev/null
+#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, ¤tX, ¤tY, 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
--- /dev/null
+#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(¤t, &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;
+}
+
--- /dev/null
+#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;
+}
--- /dev/null
+#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
+}
--- /dev/null
+#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);
+}
--- /dev/null
+//#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;
--- /dev/null
+#ifndef BTSPLY_PLAYDATE_ENTRYPOINT_H
+#define BTSPLY_PLAYDATE_ENTRYPOINT_H
+
+#include <pd_api.h>
+
+extern PlaydateAPI *playdate;
+
+#endif
--- /dev/null
+#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);
+}
--- /dev/null
+#include <bz/debug/assert.h>
+
+#include <stdlib.h>
+
+void bzAssertExit() {
+ exit(1);
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#include <SDL.h>
+
+static Uint64 baseTime;
+
+void resetSystemTimer(void) {
+ baseTime = SDL_GetTicks64();
+}
+
+float getSystemTimer(void) {
+ Uint64 delta = SDL_GetTicks64() - baseTime;
+ return delta / 1000.0f;
+}
--- /dev/null
+#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, ¤tX, ¤tY, 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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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;
+}
--- /dev/null
+Subproject commit ba29f4eda9ea7703a9f6a9cf2b0532a2605723c3
--- /dev/null
+Subproject commit bc39cd76ac3d541e618606bcc6e1e5ba5e5e6aa3
--- /dev/null
+Subproject commit d1ca6e14aa904363706a0f5712d8b8e6e13c5fa6
--- /dev/null
+Subproject commit efaa58732abb3faf3900b2d93a166b955fbd8ed0
--- /dev/null
+/* 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
--- /dev/null
+
+/*
+** 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
--- /dev/null
+/*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 */
+ }
+}
+
--- /dev/null
+#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;
+}
--- /dev/null
+#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
+
--- /dev/null
+#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 */
+}
--- /dev/null
+#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
--- /dev/null
+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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+
+extern int xlate_string (const char *string,darray *phone);
+
+
+
--- /dev/null
+Subproject commit ae721c50eaf761660b4f90cc590453cdb0c2acd0
--- /dev/null
+Subproject commit a5b6b9ea53d78b2019201a9fb75b763b7d92873c