From: Ben Sherratt Date: Sun, 12 May 2024 12:01:16 +0000 (+0100) Subject: Initial version X-Git-Url: https://git.bts.cx/benzene.git/commitdiff_plain/88fe7f92d3fd592960bc2534fcdc534021c870bc?ds=sidebyside Initial version * Dump everything from the current common codebase into a repo! --- 88fe7f92d3fd592960bc2534fcdc534021c870bc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..92f60db --- /dev/null +++ b/.gitmodules @@ -0,0 +1,18 @@ +[submodule "third_party/sdl"] + path = third_party/sdl + url = https://github.com/libsdl-org/SDL.git +[submodule "third_party/physfs"] + path = third_party/physfs + url = https://github.com/icculus/physfs.git +[submodule "third_party/parson"] + path = third_party/parson + url = https://github.com/kgabis/parson.git +[submodule "third_party/stb"] + path = third_party/stb + url = https://github.com/nothings/stb.git +[submodule "third_party/pcg-c-basic"] + path = third_party/pcg-c-basic + url = https://github.com/imneme/pcg-c-basic.git +[submodule "third_party/sun"] + path = third_party/sun + url = http://git.bts.cx/sun.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e20134d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,362 @@ +cmake_minimum_required(VERSION 3.12) + +if(PLAYDATE_DEVICE) + set(PROJECT_LANGUAGES C ASM CXX) +elseif(APPLE) + set(PROJECT_LANGUAGES C OBJC CXX) +else() + # Some error needs to go here, and more platforms... +endif() + +set(CMAKE_CXX_STANDARD 11) + +project(benzene ${PROJECT_LANGUAGES}) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/sun/runtime EXCLUDE_FROM_ALL) + +if(PLAYDATE_DEVICE) + # Playdate is software renderer +elseif(APPLE) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/sdl EXCLUDE_FROM_ALL) +else() + # Some error needs to go here, and more platforms... +endif() + +set(BENZENE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(BENZENE_SOURCES + ${BENZENE_SRC_DIR}/bz/impl.c + + ${BENZENE_SRC_DIR}/bz/audio/ogg.h + ${BENZENE_SRC_DIR}/bz/audio/ogg.c + ${BENZENE_SRC_DIR}/bz/audio/playback.h + ${BENZENE_SRC_DIR}/bz/audio/playback_internal.h + ${BENZENE_SRC_DIR}/bz/audio/playback.c + ${BENZENE_SRC_DIR}/bz/audio/speech.h + ${BENZENE_SRC_DIR}/bz/audio/speech.cpp + ${BENZENE_SRC_DIR}/bz/audio/wav.h + ${BENZENE_SRC_DIR}/bz/audio/wav.c + + ${BENZENE_SRC_DIR}/bz/collision/collision.h + ${BENZENE_SRC_DIR}/bz/collision/collision.c + ${BENZENE_SRC_DIR}/bz/collision/particle_collision.h + ${BENZENE_SRC_DIR}/bz/collision/particle_collision.c + + ${BENZENE_SRC_DIR}/bz/debug/assert.h + ${BENZENE_SRC_DIR}/bz/debug/assert.c + ${BENZENE_SRC_DIR}/bz/debug/log.h + ${BENZENE_SRC_DIR}/bz/debug/log.c + ${BENZENE_SRC_DIR}/bz/debug/perfgraph.h + ${BENZENE_SRC_DIR}/bz/debug/perfgraph.c + + ${BENZENE_SRC_DIR}/bz/fx/agent_simulation.h + ${BENZENE_SRC_DIR}/bz/fx/agent_simulation_internal.h + ${BENZENE_SRC_DIR}/bz/fx/agent_simulation.c + ${BENZENE_SRC_DIR}/bz/fx/particle_simulation.h + ${BENZENE_SRC_DIR}/bz/fx/particle_simulation_internal.h + ${BENZENE_SRC_DIR}/bz/fx/particle_simulation.c + ${BENZENE_SRC_DIR}/bz/fx/particle_system.h + ${BENZENE_SRC_DIR}/bz/fx/particle_system_internal.h + ${BENZENE_SRC_DIR}/bz/fx/particle_system.c + + ${BENZENE_SRC_DIR}/bz/game/actor.h + ${BENZENE_SRC_DIR}/bz/game/actor_internal.h + ${BENZENE_SRC_DIR}/bz/game/actor.c + ${BENZENE_SRC_DIR}/bz/game/scene.h + ${BENZENE_SRC_DIR}/bz/game/scene_internal.h + ${BENZENE_SRC_DIR}/bz/game/scene.c + ${BENZENE_SRC_DIR}/bz/game/scene_identifiers.cpp + ${BENZENE_SRC_DIR}/bz/game/tilemap.h + ${BENZENE_SRC_DIR}/bz/game/tilemap_internal.h + ${BENZENE_SRC_DIR}/bz/game/tilemap.c + + ${BENZENE_SRC_DIR}/bz/gfx/aseprite.h + ${BENZENE_SRC_DIR}/bz/gfx/aseprite_internal.h + ${BENZENE_SRC_DIR}/bz/gfx/aseprite.c + ${BENZENE_SRC_DIR}/bz/gfx/draw_queue.h + ${BENZENE_SRC_DIR}/bz/gfx/draw_queue.c + ${BENZENE_SRC_DIR}/bz/gfx/drawing.h + ${BENZENE_SRC_DIR}/bz/gfx/drawing.c + ${BENZENE_SRC_DIR}/bz/gfx/font.h + ${BENZENE_SRC_DIR}/bz/gfx/font_internal.h + ${BENZENE_SRC_DIR}/bz/gfx/font.c + ${BENZENE_SRC_DIR}/bz/gfx/gfx.h + ${BENZENE_SRC_DIR}/bz/gfx/gfx_internal.h + ${BENZENE_SRC_DIR}/bz/gfx/gfx.c + ${BENZENE_SRC_DIR}/bz/gfx/particle_drawing.h + ${BENZENE_SRC_DIR}/bz/gfx/particle_drawing.c + ${BENZENE_SRC_DIR}/bz/gfx/tilemap.h + ${BENZENE_SRC_DIR}/bz/gfx/tilemap_internal.h + ${BENZENE_SRC_DIR}/bz/gfx/tilemap.c + + ${BENZENE_SRC_DIR}/bz/input/input.h + ${BENZENE_SRC_DIR}/bz/input/input_internal.h + ${BENZENE_SRC_DIR}/bz/input/input.c + ${BENZENE_SRC_DIR}/bz/input/input_id.h + ${BENZENE_SRC_DIR}/bz/input/input_id_internal.h + ${BENZENE_SRC_DIR}/bz/input/platform.h + + ${BENZENE_SRC_DIR}/bz/math/math.h + ${BENZENE_SRC_DIR}/bz/math/math.c + ${BENZENE_SRC_DIR}/bz/math/matrix.h + ${BENZENE_SRC_DIR}/bz/math/matrix.c + ${BENZENE_SRC_DIR}/bz/math/random.h + ${BENZENE_SRC_DIR}/bz/math/random.c + ${BENZENE_SRC_DIR}/bz/math/vector.h + ${BENZENE_SRC_DIR}/bz/math/vector.c + + ${BENZENE_SRC_DIR}/bz/memory/allocator.h + ${BENZENE_SRC_DIR}/bz/memory/allocator.c + ${BENZENE_SRC_DIR}/bz/memory/arena.h + ${BENZENE_SRC_DIR}/bz/memory/arena_internal.h + ${BENZENE_SRC_DIR}/bz/memory/arena.c + + ${BENZENE_SRC_DIR}/bz/renderer/palette.h + ${BENZENE_SRC_DIR}/bz/renderer/palette_internal.h + ${BENZENE_SRC_DIR}/bz/renderer/palette.c + ${BENZENE_SRC_DIR}/bz/renderer/render_pass.h + ${BENZENE_SRC_DIR}/bz/renderer/render_pass_internal.h + ${BENZENE_SRC_DIR}/bz/renderer/renderer.h + ${BENZENE_SRC_DIR}/bz/renderer/renderer_internal.h + ${BENZENE_SRC_DIR}/bz/renderer/renderer.c + + ${BENZENE_SRC_DIR}/bz/resources/resource.h + ${BENZENE_SRC_DIR}/bz/resources/resource.c + + ${BENZENE_SRC_DIR}/bz/scripting/bindings.h + ${BENZENE_SRC_DIR}/bz/scripting/bindings_internal.h + ${BENZENE_SRC_DIR}/bz/scripting/bindings.cpp + ${BENZENE_SRC_DIR}/bz/scripting/environment.h + ${BENZENE_SRC_DIR}/bz/scripting/environment_internal.h + ${BENZENE_SRC_DIR}/bz/scripting/environment.c + ${BENZENE_SRC_DIR}/bz/scripting/script.h + ${BENZENE_SRC_DIR}/bz/scripting/script_internal.h + ${BENZENE_SRC_DIR}/bz/scripting/script.c + ${BENZENE_SRC_DIR}/bz/scripting/static_crc32.hpp + + ${BENZENE_SRC_DIR}/bz/types/common.h + ${BENZENE_SRC_DIR}/bz/types/identifier.h + ${BENZENE_SRC_DIR}/bz/types/identifier.c + ${BENZENE_SRC_DIR}/bz/types/point.h + ${BENZENE_SRC_DIR}/bz/types/rect.h + ${BENZENE_SRC_DIR}/bz/types/size.h + ${BENZENE_SRC_DIR}/bz/types/user_parameter.h +) + +if(PLAYDATE_DEVICE) + set(PLATFORM_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src_platform/playdate) + set(BENZENE_PLATFORM_SOURCES + ${PLATFORM_SRC_DIR}/bz/debug/assert.c + ${PLATFORM_SRC_DIR}/bz/debug/log.c + ${PLATFORM_SRC_DIR}/bz/debug/perfgraph.c + + ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.h + ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.c + + ${PLATFORM_SRC_DIR}/bz/input/input.c + + ${PLATFORM_SRC_DIR}/bz/memory/arena.c + + ${PLATFORM_SRC_DIR}/bz/renderer/renderer.c + + ${PLATFORM_SRC_DIR}/bz/resources/resource.c + + ${PLATFORM_SRC_DIR}/playdate/entrypoint.h + ${PLATFORM_SRC_DIR}/playdate/entrypoint.c + ) +else() + set(PLATFORM_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src_platform/sdl) + set(BENZENE_PLATFORM_SOURCES + ${PLATFORM_SRC_DIR}/bz/audio/playback.c + + ${PLATFORM_SRC_DIR}/bz/debug/assert.c + ${PLATFORM_SRC_DIR}/bz/debug/log.c + ${PLATFORM_SRC_DIR}/bz/debug/perfgraph.c + + ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.h + ${PLATFORM_SRC_DIR}/bz/gfx/gfx_platform.c + + ${PLATFORM_SRC_DIR}/bz/input/input.c + + ${PLATFORM_SRC_DIR}/bz/memory/arena.c + + ${PLATFORM_SRC_DIR}/bz/renderer/renderer.c + + ${PLATFORM_SRC_DIR}/bz/resources/file_system_internal.h + ${PLATFORM_SRC_DIR}/bz/resources/resource.c + + ${PLATFORM_SRC_DIR}/main.c + ) +endif() + +if(NOT PLAYDATE_DEVICE) + set(PHYSFS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/physfs/) + set(PHYSFS_SOURCES + ${PHYSFS_SRC_DIR}/src/physfs.c + ${PHYSFS_SRC_DIR}/src/physfs.h + ${PHYSFS_SRC_DIR}/src/physfs_archiver_7z.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_dir.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_grp.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_hog.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_iso9660.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_mvl.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_qpak.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_slb.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_unpacked.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_vdf.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_wad.c + ${PHYSFS_SRC_DIR}/src/physfs_archiver_zip.c + ${PHYSFS_SRC_DIR}/src/physfs_byteorder.c + ${PHYSFS_SRC_DIR}/src/physfs_casefolding.h + ${PHYSFS_SRC_DIR}/src/physfs_internal.h + ${PHYSFS_SRC_DIR}/src/physfs_lzmasdk.h + ${PHYSFS_SRC_DIR}/src/physfs_miniz.h + ${PHYSFS_SRC_DIR}/src/physfs_platform_android.c + ${PHYSFS_SRC_DIR}/src/physfs_platform_os2.c + ${PHYSFS_SRC_DIR}/src/physfs_platform_posix.c + ${PHYSFS_SRC_DIR}/src/physfs_platform_qnx.c + ${PHYSFS_SRC_DIR}/src/physfs_platform_unix.c + ${PHYSFS_SRC_DIR}/src/physfs_platform_windows.c + ${PHYSFS_SRC_DIR}/src/physfs_platforms.h + ${PHYSFS_SRC_DIR}/src/physfs_unicode.c + ) + if(APPLE) + list(APPEND PHYSFS_SOURCES ${PHYSFS_SRC_DIR}/src/physfs_platform_apple.m) + endif() +endif() + +set(PARSON_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/parson/) +set(PARSON_SOURCES + ${PARSON_SRC_DIR}/parson.h + ${PARSON_SRC_DIR}/parson.c +) + +set(PCG_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/pcg-c-basic/) +set(PCG_SOURCES + ${PCG_SRC_DIR}/pcg_basic.h + ${PCG_SRC_DIR}/pcg_basic.c +) + +set(CRC_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/snippets_org_crc/) +set(CRC_SOURCES + ${CRC_SRC_DIR}/crc.h + ${CRC_SRC_DIR}/crc.c +) + +set(SOLOUD_SPEECH_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/soloud_speech/) +set(SOLOUD_SPEECH_SOURCES + ${SOLOUD_SPEECH_SRC_DIR}/darray.h + ${SOLOUD_SPEECH_SRC_DIR}/darray.cpp + ${SOLOUD_SPEECH_SRC_DIR}/klatt.h + ${SOLOUD_SPEECH_SRC_DIR}/klatt.cpp + ${SOLOUD_SPEECH_SRC_DIR}/resonator.h + ${SOLOUD_SPEECH_SRC_DIR}/resonator.cpp + ${SOLOUD_SPEECH_SRC_DIR}/tts.h + ${SOLOUD_SPEECH_SRC_DIR}/tts.cpp +) + + +add_library(benzene STATIC ${BENZENE_SOURCES}) +set_property(TARGET benzene PROPERTY C_STANDARD 11) + +if(PLAYDATE_DEVICE) + # NB: This is copied out of the Playdate SDK. Check for correctness on update. + + set(ENVSDK $ENV{PLAYDATE_SDK_PATH}) + + if (NOT ${ENVSDK} STREQUAL "") + # Convert path from Windows + file(TO_CMAKE_PATH ${ENVSDK} SDK) + else() + execute_process( + COMMAND bash -c "egrep '^\\s*SDKRoot' $HOME/.Playdate/config" + COMMAND head -n 1 + COMMAND cut -c9- + OUTPUT_VARIABLE SDK + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + + if (NOT EXISTS ${SDK}) + message(FATAL_ERROR "SDK Path not found; set ENV value PLAYDATE_SDK_PATH") + return() + endif() + + ## + + include_directories("${SDK}/C_API") + set(PDC "${SDK}/bin/pdc" -sdkpath "${SDK}") + + add_compile_definitions(TARGET_EXTENSION=1) + + set(HEAP_SIZE 8388208) + set(STACK_SIZE 61800) + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -x assembler-with-cpp -D__HEAP_SIZE=${HEAP_SIZE} -D__STACK_SIZE=${STACK_SIZE}") + + set(MCFLAGS -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -D__FPU_USED=1) + + target_compile_definitions(benzene PUBLIC TARGET_PLAYDATE=1) + target_compile_options(benzene PUBLIC -Wall -Wno-unknown-pragmas -Wdouble-promotion) +# target_compile_options(benzene PUBLIC $<$:-O2>) +# target_compile_options(benzene PUBLIC $<$:-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 $<$:-fno-exceptions>) + + target_link_options(benzene PUBLIC ${MCFLAGS}) + + target_link_options(benzene PUBLIC -specs=nosys.specs) + +# target_compile_options(benzene PUBLIC "-funroll-loops") + + target_compile_options(benzene PUBLIC "-nostdlib") +# target_compile_options(benzene PUBLIC "-nostdinc") + target_compile_options(benzene PUBLIC "-nodefaultlibs") + +elseif(APPLE) + +endif() + +if(PLAYDATE_DEVICE) + target_sources(benzene PRIVATE ${BENZENE_PLATFORM_SOURCES} ${PARSON_SOURCES} ${PCG_SOURCES} ${CRC_SOURCES}) +else() + target_sources(benzene PRIVATE ${BENZENE_PLATFORM_SOURCES} ${PHYSFS_SOURCES} ${PARSON_SOURCES} ${PCG_SOURCES} ${CRC_SOURCES} ${SOLOUD_SPEECH_SOURCES}) +endif() + +target_precompile_headers(benzene PRIVATE ${BENZENE_SRC_DIR}/bz/common.pch) + +target_link_libraries(benzene PRIVATE sun) +target_include_directories(benzene PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/sun/runtime/src) + +if(PLAYDATE_DEVICE) + +else() + if(TARGET SDL2::SDL2main) + target_link_libraries(benzene PRIVATE SDL2::SDL2main) + endif() + + # FIXME, Ideally we want to use the DLLs so that Steam etc. can patch + #target_link_libraries(benzene PRIVATE SDL2::SDL2) + target_link_libraries(benzene PRIVATE SDL2::SDL2-static) +endif() + +if(APPLE) + target_link_libraries(benzene PRIVATE "-framework Foundation") + target_link_libraries(benzene PRIVATE "-framework IOKit") +endif() + +target_include_directories(benzene PRIVATE ${GAME_SRC_DIR}) +target_include_directories(benzene PRIVATE ${BENZENE_SRC_DIR}) +target_include_directories(benzene PRIVATE ${PLATFORM_SRC_DIR}) +target_include_directories(benzene PRIVATE ${PARSON_SRC_DIR}) +target_include_directories(benzene PRIVATE ${PCG_SRC_DIR}) +target_include_directories(benzene PRIVATE ${CRC_SRC_DIR}) +target_include_directories(benzene PRIVATE ${SOLOUD_SPEECH_SRC_DIR}) +target_include_directories(benzene PRIVATE ${PHYSFS_SRC_DIR}/src) +target_include_directories(benzene PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stb) diff --git a/src/bz/audio/ogg.c b/src/bz/audio/ogg.c new file mode 100644 index 0000000..bf00345 --- /dev/null +++ b/src/bz/audio/ogg.c @@ -0,0 +1,109 @@ +#include + +#include +#include +#include + +#include + +struct BZAudioOGGStream { + BZAudioOGGStreamDetails details; + BZResourceID handle; + size_t dataBlockSize; +// uint8_t *dataBlock; +// uint32_t rate; +// uint8_t channels; + stb_vorbis *vorbis; +}; + +BZAudioOGGStream *bzAudioOpenOGGStream(BZAudioOGGStreamDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZAudioOGGStream *stream = bzMemoryAlloc(arena, sizeof(BZAudioOGGStream)); + + stream->handle = bzResourcesOpenResource("music", "assets/music/%s.ogg", identifier); + + size_t initialSize = 128; + int memoryConsumed, error; + for (;;) { + bzResourcesSeek(stream->handle, 0); + + void *initialData = bzMemoryAllocTmp(arena, initialSize);// FIXME, realloc + bzResourcesReadBytes(stream->handle, initialData, initialSize); + + stream->vorbis = stb_vorbis_open_pushdata(initialData, initialSize, &memoryConsumed, &error, NULL); // FIXME, temp memory + if (stream->vorbis != NULL) { + break; + } + bzAssert(error == VORBIS_need_more_data); + initialSize <<= 1; + bzMemoryArenaResetTmp(arena); + } + + bzResourcesSeek(stream->handle, memoryConsumed); + + stb_vorbis_info info = stb_vorbis_get_info(stream->vorbis); + + stream->details = (BZAudioOGGStreamDetails) { + .rate = info.sample_rate, + .channels = info.channels, + .maxSamples = info.max_frame_size, + }; + + if (detailsOut != NULL) { + *detailsOut = stream->details; + } + + stream->dataBlockSize = info.temp_memory_required; + + //*outputSizeOut = stream->dataBlockSize; + +// stream->dataBlock = bzMemoryAlloc(arena, stream->dataBlockSize); + +// stream->rate = info.sample_rate; +// stream->channels = info.channels; + + //bzResourcesCloseResource(handle); + + return stream; +} +//size_t *samplesOut, uint32_t *rateOut, uint8_t *channelsOut, + +void bzAudioResetOGGData(BZAudioOGGStream *stream) { + bzResourcesSeek(stream->handle, 0); + stb_vorbis_flush_pushdata(stream->vorbis); +} + +size_t bzAudioQueryOGGData(BZAudioOGGStream *stream, size_t maxDataSize, float *data) { + float **output; + size_t samples = 0; + + void *dataBlock = alloca(stream->dataBlockSize); // FIXME + + for (;;) { + size_t offset = bzResourcesTell(stream->handle); + bzResourcesReadBytes(stream->handle, dataBlock, stream->dataBlockSize); + size_t bytesUsed = stb_vorbis_decode_frame_pushdata(stream->vorbis, dataBlock, stream->dataBlockSize, NULL, &output, &samples); + if (bytesUsed == 0 && samples == 0) { + break; + } + //bzAssert(bytesUsed != 0 || samples != 0); + bzResourcesSeek(stream->handle, offset + bytesUsed); + if (samples > 0) { + break; + } + } + + for (size_t i = 0, out = 0; i < samples; ++i) { + for (size_t j = 0; j < stream->details.channels; ++j, ++out) { + data[out] = output[j][i]; + } + } + + return samples * stream->details.channels * sizeof(float); +} + +void bzAudioCloseOGGStream(BZAudioOGGStream *stream) { + stb_vorbis_close(stream->vorbis); + bzResourcesCloseResource(stream->handle); +} diff --git a/src/bz/audio/ogg.h b/src/bz/audio/ogg.h new file mode 100644 index 0000000..8f8a7e5 --- /dev/null +++ b/src/bz/audio/ogg.h @@ -0,0 +1,30 @@ +#ifndef BZ_AUDIO_OGG_H +#define BZ_AUDIO_OGG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZAudioOGGStreamDetails { + uint32_t rate; + uint8_t channels; + uint32_t maxSamples; +}; +typedef struct BZAudioOGGStreamDetails BZAudioOGGStreamDetails; + +typedef struct BZAudioOGGStream BZAudioOGGStream; + +extern BZAudioOGGStream *bzAudioOpenOGGStream(BZAudioOGGStreamDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...); +extern void bzAudioCloseOGGStream(BZAudioOGGStream *stream); + +extern void bzAudioResetOGGData(BZAudioOGGStream *stream); +extern size_t bzAudioQueryOGGData(BZAudioOGGStream *stream, size_t maxDataSize, float *data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/audio/playback.c b/src/bz/audio/playback.c new file mode 100644 index 0000000..dcaa5f8 --- /dev/null +++ b/src/bz/audio/playback.c @@ -0,0 +1,2 @@ +#include + diff --git a/src/bz/audio/playback.h b/src/bz/audio/playback.h new file mode 100644 index 0000000..42c55b7 --- /dev/null +++ b/src/bz/audio/playback.h @@ -0,0 +1,26 @@ +#ifndef BZ_AUDIO_PLAYBACK_H +#define BZ_AUDIO_PLAYBACK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZAudioPlaybackEngine BZAudioPlaybackEngine; +typedef BZAudioPlaybackEngine * BZAudioPlaybackEngineID; + +extern BZAudioPlaybackEngineID bzAudioPlaybackInit(BZMemoryArenaID arena, const char *identifierFmt, ...); +extern void bzAudioPlaybackTeardown(BZAudioPlaybackEngineID engine); + +extern void bzAudioPlaybackSetParameter(BZAudioPlaybackEngineID engine, BZIdentifierHash parameterIdentifier, float value); +extern void bzAudioPlaybackPostEvent(BZAudioPlaybackEngineID engine, BZIdentifierHash eventIdentifier, ...); + +extern void bzAudioPlaybackUseSoundbank(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/audio/playback_internal.h b/src/bz/audio/playback_internal.h new file mode 100644 index 0000000..3d32be3 --- /dev/null +++ b/src/bz/audio/playback_internal.h @@ -0,0 +1,14 @@ +#ifndef BZ_AUDIO_PLAYBACK_INTERNAL_H +#define BZ_AUDIO_PLAYBACK_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/audio/speech.cpp b/src/bz/audio/speech.cpp new file mode 100644 index 0000000..ef8ece6 --- /dev/null +++ b/src/bz/audio/speech.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include // strcmp + +struct BZAudioVoice { + darray element; + klatt synth; + size_t maxFrames; +// short *sample; +}; + +BZAudioVoice *bzAudioLoadSpeechVoice(BZAudioVoiceDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + int baseFrequency = 1330; + float baseSpeed = 10.0f; + float baseDeclination = 0.5f; + int aBaseWaveform = KW_SAW; + + BZResourceID handle = bzResourcesOpenResource("voices", "assets/voices/%s.voice.json", identifier); + size_t length = bzResourcesFileLength(handle); + + char *data = (char *)alloca(length); // FIXME, temporary memory + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *voiceJson = json_parse_string(data); + JSON_Object *voiceJsonObject = json_object(voiceJson); + + if (json_object_has_value_of_type(voiceJsonObject, "frequency", JSONNumber)) { + baseFrequency = json_object_get_number(voiceJsonObject, "frequency"); + } + + if (json_object_has_value_of_type(voiceJsonObject, "speed", JSONNumber)) { + baseSpeed = json_object_get_number(voiceJsonObject, "speed"); + } + + if (json_object_has_value_of_type(voiceJsonObject, "declination", JSONNumber)) { + baseDeclination = json_object_get_number(voiceJsonObject, "declination"); + } + + if (json_object_has_value_of_type(voiceJsonObject, "waveform", JSONString)) { + const char *waveform = json_object_get_string(voiceJsonObject, "waveform"); + if (strcmp(waveform, "saw") == 0) { + aBaseWaveform = KW_SAW; + } else if (strcmp(waveform, "triangle") == 0) { + aBaseWaveform = KW_TRIANGLE; + } else if (strcmp(waveform, "sin") == 0) { + aBaseWaveform = KW_SIN; + } else if (strcmp(waveform, "square") == 0) { + aBaseWaveform = KW_SQUARE; + } else if (strcmp(waveform, "pulse") == 0) { + aBaseWaveform = KW_PULSE; + } else if (strcmp(waveform, "noise") == 0) { + aBaseWaveform = KW_NOISE; + } else if (strcmp(waveform, "warble") == 0) { + aBaseWaveform = KW_WARBLE; + } + } + + json_value_free(voiceJson); + + BZAudioVoice *voice = new(bzMemoryAlloc(arena, sizeof(BZAudioVoice))) BZAudioVoice(); + voice->synth.init(baseFrequency, baseSpeed, baseDeclination, aBaseWaveform); + voice->maxFrames = 500;//4 * 1024; +// voice->sample = (short *)bzMemoryAlloc(arena, voice->synth.mNspFr * voice->maxFrames * sizeof(short)); + + detailsOut->channels = 1; + detailsOut->rate = 11025; + detailsOut->maxSamples = voice->synth.mNspFr * voice->maxFrames; + +// *sizeOut = outputCount * sizeof(short); +// *samplingRateOut = 11025; +// *channelsOut = 1; +// *bitsPerSampleOut = 16; + + return voice; +} + +size_t bzAudioGenerateSpeech(BZAudioVoice *voice, short *dst, size_t dstSize, const char *speechFmt, ...) { + bzMakeIdentifier(speech, speechFmt); + + voice->element.clear(); + + darray phone; // FIXME, darray should have a stable memory footprint + xlate_string(speech, &phone); + int frames = klatt::phone_to_elm(phone.getData(), phone.getSize(), &voice->element); + bzAssertMessage(frames <= voice->maxFrames, "Too many frames, %d", frames); + + voice->synth.initsynth(voice->element.getSize(), (unsigned char *)voice->element.getData()); + + size_t sampleSize = voice->synth.mNspFr * frames; + size_t outputCount = 0; + while (outputCount < sampleSize) { + outputCount += voice->synth.synth(voice->synth.mNspFr /* This seems to be ignored... */, &dst[outputCount]); + if (outputCount == sampleSize) { break; } + } + + return outputCount * sizeof(short); +} diff --git a/src/bz/audio/speech.h b/src/bz/audio/speech.h new file mode 100644 index 0000000..1d7848f --- /dev/null +++ b/src/bz/audio/speech.h @@ -0,0 +1,26 @@ +#ifndef BZ_AUDIO_SPEECH_H +#define BZ_AUDIO_SPEECH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZAudioVoiceDetails { + uint32_t rate; + uint8_t channels; + uint32_t maxSamples; +}; +typedef struct BZAudioVoiceDetails BZAudioVoiceDetails; + +typedef struct BZAudioVoice BZAudioVoice; + +extern BZAudioVoice *bzAudioLoadSpeechVoice(BZAudioVoiceDetails *detailsOut, BZMemoryArenaID arena, const char *identifierFmt, ...); +extern size_t bzAudioGenerateSpeech(BZAudioVoice *voice, short *dst, size_t dstSize, const char *speechFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/audio/wav.c b/src/bz/audio/wav.c new file mode 100644 index 0000000..8942335 --- /dev/null +++ b/src/bz/audio/wav.c @@ -0,0 +1,107 @@ +#include + +#include +#include +#include + +#include // Static assertions + +// https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf + +#define PACKED_STRUCT __attribute__((__packed__)) + +typedef uint16_t RIFF_WORD; +typedef uint32_t RIFF_DWORD; +typedef uint8_t RIFF_BYTE; + +typedef RIFF_DWORD RIFF_FOURCC; // Four-character code + +typedef RIFF_FOURCC RIFF_CKID; // Four-character-code chunk identifier +typedef RIFF_DWORD RIFF_CKSIZE; // 32-bit unsigned size value + +struct PACKED_STRUCT RIFF_CK { + RIFF_CKID ckID; + RIFF_CKSIZE ckSize; + //RIFF_BYTE ckData[]; +}; +typedef struct RIFF_CK RIFF_CK; +static_assert(sizeof(RIFF_CK) == 8, "CK is wrong size"); + +#define CHUNK_ID(a,b,c,d) (((a) << 0) | ((b) << 8) | ((c) << 16) | ((d) << 24)) + +struct PACKED_STRUCT RIFF_WAVEFormatCK { + RIFF_WORD wFormatTag; // Format category + RIFF_WORD wChannels; // Number of channels + RIFF_DWORD dwSamplesPerSec; // Sampling rate + RIFF_DWORD dwAvgBytesPerSec; // For buffer estimation + RIFF_WORD wBlockAlign; // Data block size +}; +typedef struct RIFF_WAVEFormatCK RIFF_WAVEFormatCK; + +struct PACKED_STRUCT RIFF_PCMFormatCK { + RIFF_WORD wBitsPerSample; // Sample size +}; +typedef struct RIFF_PCMFormatCK RIFF_PCMFormatCK; + +enum { + RIFF_WAVE_FORMAT_PCM = 0x0001, // Microsoft Pulse Code Modulation (PCM) format + RIFF_IBM_FORMAT_MULAW = 0x0101, // IBM mu-law format + RIFF_IBM_FORMAT_ALAW = 0x0102, // IBM a-law format + RIFF_IBM_FORMAT_ADPCM = 0x0103 // IBM AVC Adaptive Differential Pulse Code Modulation format +}; + +void *bzAudioLoadWAV(size_t *sizeOut, BZMemoryArenaID arena, uint32_t *samplingRateOut, uint16_t *channelsOut, uint16_t *bitsPerSampleOut, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZResourceID handle = bzResourcesOpenResource("sounds", "assets/sounds/%s.wav", identifier); + + void *wavData = NULL; + + for (;;) { + RIFF_CK chunk; + size_t readLength = bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); + if (readLength != sizeof(chunk)) { + break; + } + + size_t chunkDataStartPosition = bzResourcesTell(handle); + + switch (chunk.ckID) { + case CHUNK_ID('R','I','F','F'): { + RIFF_CKID fileType; + bzResourcesReadBytes(handle, &fileType, sizeof(fileType)); + bzAssertMessage(fileType == CHUNK_ID('W','A','V','E'), "Not a WAV file"); + break; + } + + case CHUNK_ID('f','m','t',' '): { + RIFF_WAVEFormatCK format; + bzResourcesReadBytes(handle, &format, sizeof(format)); + bzAssertMessage(format.wFormatTag == RIFF_WAVE_FORMAT_PCM, "Not a PCM file"); + RIFF_PCMFormatCK pcmFormat; + bzResourcesReadBytes(handle, &pcmFormat, sizeof(pcmFormat)); + + *samplingRateOut = format.dwSamplesPerSec; + *channelsOut = format.wChannels; + *bitsPerSampleOut = pcmFormat.wBitsPerSample; + } + + case CHUNK_ID('d','a','t','a'): { + wavData = bzMemoryAlloc(arena, chunk.ckSize); + *sizeOut = chunk.ckSize; + bzResourcesReadBytes(handle, wavData, chunk.ckSize); + } + + default: + // Skip this chunk... + bzResourcesSeek(handle, chunkDataStartPosition + chunk.ckSize); + break; + } + } + + bzResourcesCloseResource(handle); + + bzLog("Loaded WAV '%s', %d, %d, %d", identifier, *samplingRateOut, *channelsOut, *bitsPerSampleOut); + + return wavData; +} diff --git a/src/bz/audio/wav.h b/src/bz/audio/wav.h new file mode 100644 index 0000000..f4da91c --- /dev/null +++ b/src/bz/audio/wav.h @@ -0,0 +1,16 @@ +#ifndef BZ_AUDIO_WAV_H +#define BZ_AUDIO_WAV_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *bzAudioLoadWAV(size_t *sizeOut, BZMemoryArenaID arena, uint32_t *samplingRateOut, uint16_t *channelsOut, uint16_t *bitsPerSampleOut, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/collision/collision.c b/src/bz/collision/collision.c new file mode 100644 index 0000000..20710dd --- /dev/null +++ b/src/bz/collision/collision.c @@ -0,0 +1,277 @@ +#include + +#include +#include +#include + +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 +void bzCollisionDrawDebug(BZCollisionSpaceID space) { + for (size_t i = 0; i < space->nextBody; ++i) { + switch (space->bodies[i].shape.type) { + case BZCollisionShapeTypeCircle: + bzCirc(space->bodies[i].shape.circleOrigin.x, space->bodies[i].shape.circleOrigin.y, space->bodies[i].shape.circleRadius, 2); + break; + + default: + break; + } + } +} diff --git a/src/bz/collision/collision.h b/src/bz/collision/collision.h new file mode 100644 index 0000000..24418ff --- /dev/null +++ b/src/bz/collision/collision.h @@ -0,0 +1,75 @@ +#ifndef BZ_COLLISION_COLLISION_H +#define BZ_COLLISION_COLLISION_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum BZCollisionBodyType { + BZCollisionBodyTypeSolid, +}; +typedef enum BZCollisionBodyType BZCollisionBodyType; + +enum BZCollisionShapeType { + BZCollisionShapeTypeUnknown, + BZCollisionShapeTypeCircle, + BZCollisionShapeTypeCirclePerimeter, + BZCollisionShapeTypeLine, + BZCollisionShapeTypeTriangle, +}; +typedef enum BZCollisionShapeType BZCollisionShapeType; + +struct BZCollisionShape { + BZCollisionShapeType type; + union { + struct { + BZVector circleOrigin; + float circleRadius; + }; + struct { + BZVector lineOrigin; + BZVector lineDirection; + float lineMagnitude; + }; + struct { + BZVector trianglePoint1; + BZVector trianglePoint2; + BZVector trianglePoint3; + }; + }; +}; +typedef struct BZCollisionShape BZCollisionShape; + +//typedef uint32_t BZCollisionTag; +typedef uint32_t BZCollisionTagMask; + +typedef struct BZCollisionSpace BZCollisionSpace; +typedef BZCollisionSpace * BZCollisionSpaceID; + +typedef struct BZCollisionBody BZCollisionBody; +typedef BZCollisionBody * BZCollisionBodyID; + +extern BZCollisionSpaceID bzCollisionMakeSpace(BZMemoryArenaID arena, size_t maxBodies, const char *identifierFmt, ...); + + + +extern void bzCollisionResetSpace(BZCollisionSpaceID space); + +extern BZCollisionBodyID bzCollisionAddBody(BZCollisionSpaceID space, BZCollisionBodyType bodyType, const BZCollisionShape *shape, BZIdentifierHash tagHash, void *userParameter); + +/////zones + +extern bool bzCollisionResolve(BZVector *positionOut, void **userParameterOut, BZCollisionSpaceID space, const BZCollisionShape *shape, BZIdentifierHash tagHash); + +extern void bzCollisionDrawDebug(BZCollisionSpaceID space); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/collision/particle_collision.c b/src/bz/collision/particle_collision.c new file mode 100644 index 0000000..6392628 --- /dev/null +++ b/src/bz/collision/particle_collision.c @@ -0,0 +1,20 @@ +#include + +#include + +void bzCollisionPopulateParticles(BZCollisionSpaceID space, BZParticleSimulationID simulation, BZIdentifierHash tagHash, float radiusMultiplier) { + BZCollisionShape s; + + s.type = BZCollisionShapeTypeCircle; + + for (size_t i = 0; i < simulation->maxParticles; ++i) { + float particleStartTicks = simulation->spawnTime[i]; + float lifetime = simulation->lifetime[i]; + if (particleStartTicks + lifetime >= simulation->time) { + s.circleOrigin.x = simulation->positionX[i]; + s.circleOrigin.y = simulation->positionY[i]; + s.circleRadius = simulation->size[i] * radiusMultiplier; + bzCollisionAddBody(space, BZCollisionBodyTypeSolid, &s, tagHash, (void *)i); + } + } +} diff --git a/src/bz/collision/particle_collision.h b/src/bz/collision/particle_collision.h new file mode 100644 index 0000000..5c9b7f9 --- /dev/null +++ b/src/bz/collision/particle_collision.h @@ -0,0 +1,17 @@ +#ifndef BZ_COLLISION_PARTICLE_COLLISION_H +#define BZ_COLLISION_PARTICLE_COLLISION_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzCollisionPopulateParticles(BZCollisionSpaceID space, BZParticleSimulationID simulation, BZIdentifierHash tagHash, float radiusMultiplier); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/common.pch b/src/bz/common.pch new file mode 100644 index 0000000..c253c3f --- /dev/null +++ b/src/bz/common.pch @@ -0,0 +1,3 @@ +#include +#include +#include \ No newline at end of file diff --git a/src/bz/debug/assert.c b/src/bz/debug/assert.c new file mode 100644 index 0000000..e69de29 diff --git a/src/bz/debug/assert.h b/src/bz/debug/assert.h new file mode 100644 index 0000000..72381df --- /dev/null +++ b/src/bz/debug/assert.h @@ -0,0 +1,19 @@ +#ifndef BZ_DEBUG_ASSERT_H +#define BZ_DEBUG_ASSERT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define bzAssertMessage(test, fmt, ...) {if ((test) != true) { bzError(fmt, ##__VA_ARGS__); bzAssertExit(); } } +#define bzAssert(test) bzAssertMessage(test, "failed assertion \""#test"\"") + +extern void bzAssertExit(void); // TODO: Consider moving this to generic failure handling + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/debug/log.c b/src/bz/debug/log.c new file mode 100644 index 0000000..e69de29 diff --git a/src/bz/debug/log.h b/src/bz/debug/log.h new file mode 100644 index 0000000..34c655d --- /dev/null +++ b/src/bz/debug/log.h @@ -0,0 +1,18 @@ +#ifndef BZ_DEBUG_LOG_H +#define BZ_DEBUG_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void _bzLog(const char *a1, unsigned long a2, const char *fmt, ...); +extern void _bzError(const char *a1, unsigned long a2, const char *fmt, ...); + +#define bzLog(fmt, ...) _bzLog(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define bzError(fmt, ...) _bzError(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/debug/log_internal.h b/src/bz/debug/log_internal.h new file mode 100644 index 0000000..7ac38ca --- /dev/null +++ b/src/bz/debug/log_internal.h @@ -0,0 +1,17 @@ +#ifndef BZ_DEBUG_LOG_INTERNAL_H +#define BZ_DEBUG_LOG_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzLogInit(const char *outputFilePath); +extern void bzLogTeardown(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/debug/perfgraph.c b/src/bz/debug/perfgraph.c new file mode 100644 index 0000000..e84ba36 --- /dev/null +++ b/src/bz/debug/perfgraph.c @@ -0,0 +1,75 @@ +#include + +#include +#include + +extern void resetSystemTimer(void); +extern float getSystemTimer(void); + +struct BZPerfTimer { + const char *identifier; + float startTime; + float elapsedTime; +}; +typedef struct BZPerfTimer BZPerfTimer; + +BZPerfTimer perfTimers[32]; + +void bzPerfReset() { + resetSystemTimer(); + + for (size_t i = 0; i < 32; ++i) { // FIXME + perfTimers[i].identifier = NULL; + perfTimers[i].elapsedTime = 0.0f; + } +} + +static BZPerfTimer *bzPerfTimerFind(const char *identifier) { + BZPerfTimer *timer = NULL; + + for (size_t i = 0; i < 32; ++i) { + if (perfTimers[i].identifier == NULL && timer == NULL) { + timer = &perfTimers[i]; + } else if (perfTimers[i].identifier == identifier) { + timer = &perfTimers[i]; + break; + } + } + + bzAssert(timer != NULL); + return timer; +} + +void _bzPerfTimerStart(const char *identifier) { + BZPerfTimer *timer = bzPerfTimerFind(identifier); + timer->identifier = identifier; + timer->startTime = getSystemTimer(); +} + +void _bzPerfTimerStop(const char *identifier) { + float endTime = getSystemTimer(); + BZPerfTimer *timer = bzPerfTimerFind(identifier); + timer->elapsedTime += endTime - timer->startTime; +} + +void _bzPerfTimerOutput() { + for (size_t i = 0; i < 32; ++i) { + if (perfTimers[i].identifier != NULL) { + bzLog("Timer '%s': %fs", perfTimers[i].identifier, perfTimers[i].elapsedTime); + } + } +} + +void _bzPerfTimerDraw() { + int y = 10; + int width = 50; + for (size_t i = 0; i < 32; ++i) { + if (perfTimers[i].identifier != NULL) { + bzRectFill(10, y, 10 + width, y+3, 5); + float t = perfTimers[i].elapsedTime / (1 / 30.0f); + bzRectFill(10, y, 10 + width * t, y + 3, 8); + bzPrint(10 + width, y, 7, perfTimers[i].identifier); + y += 5; + } + } +} diff --git a/src/bz/debug/perfgraph.h b/src/bz/debug/perfgraph.h new file mode 100644 index 0000000..ae9870e --- /dev/null +++ b/src/bz/debug/perfgraph.h @@ -0,0 +1,34 @@ +#ifndef BZ_DEBUG_PERFGRAPH_H +#define BZ_DEBUG_PERFGRAPH_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void _bzPerfTimerStart(const char *identifier); +extern void _bzPerfTimerStop(const char *identifier); +extern void _bzPerfTimerOutput(void); +extern void _bzPerfTimerDraw(void); + +#define _bzPerfTimerStartNull(id) {} +#define _bzPerfTimerStopNull(id) {} +#define _bzPerfTimerOutputNull() {} +#define _bzPerfTimerDrawNull() {} + +#if 1 + #define bzPerfTimerStart _bzPerfTimerStartNull + #define bzPerfTimerStop _bzPerfTimerStopNull + #define bzPerfTimerOutput _bzPerfTimerOutputNull + #define bzPerfTimerDraw _bzPerfTimerDrawNull +#else + #define bzPerfTimerStart _bzPerfTimerStart + #define bzPerfTimerStop _bzPerfTimerStop + #define bzPerfTimerOutput _bzPerfTimerOutput + #define bzPerfTimerDraw _bzPerfTimerDraw +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/debug/perfgraph_internal.h b/src/bz/debug/perfgraph_internal.h new file mode 100644 index 0000000..d81e937 --- /dev/null +++ b/src/bz/debug/perfgraph_internal.h @@ -0,0 +1,16 @@ +#ifndef BZ_DEBUG_PERFGRAPH_INTERNAL_H +#define BZ_DEBUG_PERFGRAPH_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzPerfReset(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/fx/agent_simulation.c b/src/bz/fx/agent_simulation.c new file mode 100644 index 0000000..f550aab --- /dev/null +++ b/src/bz/fx/agent_simulation.c @@ -0,0 +1,202 @@ +#include "agent_simulation_internal.h" + +#include +#include +#include + +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 +void bzFXAgentSimulationDrawDebug(BZAgentSimulationID simulation) { + for (size_t i = 0; i < simulation->agentCount; ++i) { + BZAgent *agent = &simulation->agents[i]; + bzCirc(agent->details.position.x, agent->details.position.y, 2, 9); + + BZVector forward; + bzVectorMakeAngle(&forward, agent->details.angle); + //bzLine(agent->details.position.x, agent->details.position.y, agent->details.position.x + forward.x * 40, agent->details.position.y + forward.y * 40, 7); + + bzLine( + agent->details.position.x, agent->details.position.y, + agent->details.position.x + agent->velocity.x, agent->details.position.y + agent->velocity.y, + 7); + + bzLine( + agent->details.position.x, agent->details.position.y, + agent->details.position.x + agent->force.x, agent->details.position.y + agent->force.y, + 8); + } +} diff --git a/src/bz/fx/agent_simulation.h b/src/bz/fx/agent_simulation.h new file mode 100644 index 0000000..8172165 --- /dev/null +++ b/src/bz/fx/agent_simulation.h @@ -0,0 +1,47 @@ +#ifndef BZ_FX_AGENT_SIMULATION_H +#define BZ_FX_AGENT_SIMULATION_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZAgentSimulation BZAgentSimulation; +typedef BZAgentSimulation * BZAgentSimulationID; + +typedef struct BZAgent BZAgent; +typedef BZAgent * BZAgentID; + +struct BZAgentDetails { + float mass; + BZVector position; + float angle; + float maxForce; + float maxSpeed; +}; +typedef struct BZAgentDetails BZAgentDetails; + +#define bzAgentDefaultMass 1.0f +#define bzAgentDefaultMaxForce 50.0f +#define bzAgentDefaultMaxSpeed 100.0f + +extern BZAgentSimulationID bzFXCreateAgentSimulation(BZMemoryArenaID arena, size_t maxAgents, const char *identifierFmt, ...); + +extern void bzFXUpdateAgentSimulation(BZAgentSimulationID simulation, float deltaTime, BZCollisionSpaceID collisionSpace, BZVector *v); + +extern BZAgentID bzFXAgentSimulationAddAgent(BZAgentSimulationID simulation, const BZAgentDetails *details); +extern void bzFXAgentSimulationRemoveAgent(BZAgentSimulationID simulation, BZAgentID agent); + +extern void bzFXAgentSimulationGetAgentDetails(BZAgentDetails *detailsOut, BZAgentSimulationID simulation, BZAgentID agent); + +extern void bzFXAgentSimulationDrawDebug(BZAgentSimulationID simulation); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/fx/agent_simulation_internal.h b/src/bz/fx/agent_simulation_internal.h new file mode 100644 index 0000000..bb9e193 --- /dev/null +++ b/src/bz/fx/agent_simulation_internal.h @@ -0,0 +1,16 @@ +#ifndef BZ_FX_AGENT_SIMULATION_INTERNAL_H +#define BZ_FX_AGENT_SIMULATION_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/fx/particle_simulation.c b/src/bz/fx/particle_simulation.c new file mode 100644 index 0000000..477e982 --- /dev/null +++ b/src/bz/fx/particle_simulation.c @@ -0,0 +1,71 @@ +#include + +#include +#include +#include +#include +#include + +BZParticleSimulationID bzFXCreateParticleSimulation(BZMemoryArenaID arena, size_t maxParticles, BZParticleSystemID system, const char *identifierFmt, ...) { + BZParticleSimulationID simulation = bzMemoryAlloc(arena, sizeof(BZParticleSimulation)); + simulation->system = system; + + simulation->maxParticles = maxParticles;//10 * 1024; + simulation->nextParticle = 0; + + simulation->time = 0; + + simulation->spawnTime = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->lifetime = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->positionX = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->positionY = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->size = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->baseAngle = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->angle = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->color = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->alpha = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(float)); + simulation->userData = bzMemoryAlloc(arena, simulation->maxParticles * sizeof(void *)); + + return simulation; +} + +void bzFXUpdateParticleSystem(BZParticleSimulationID simulation, float deltaTime) { + simulation->time += deltaTime;//1.0f / 30.0f; + bzFXUpdateParticleSystemParticles(simulation->system, + simulation->maxParticles, + simulation->time, + simulation->spawnTime, + simulation->lifetime, + simulation->positionX, + simulation->positionY, + simulation->size, + simulation->baseAngle, + simulation->angle, + simulation->color, + simulation->alpha, + simulation->userData); +} + +void bzFXSpawnParticles(BZParticleSimulationID simulation, BZIdentifierHash eventIdentifier, const BZVector *position, const float angle) { + BZParticleSystemID particleSystem = simulation->system; + simulation->nextParticle += bzFXSpawnParticleSystemParticles(particleSystem, + eventIdentifier, + position, angle, + simulation->maxParticles, simulation->nextParticle, + simulation->time, + simulation->spawnTime, + simulation->lifetime, + simulation->positionX, + simulation->positionY, + simulation->size, + simulation->baseAngle, + simulation->angle, + simulation->color, + simulation->alpha, + simulation->userData); + simulation->nextParticle %= simulation->maxParticles; +} + +void bzFXDespawnParticle(BZParticleSimulationID simulation, size_t idx) { + simulation->lifetime[idx] = 0.0f; +} diff --git a/src/bz/fx/particle_simulation.h b/src/bz/fx/particle_simulation.h new file mode 100644 index 0000000..676a7a4 --- /dev/null +++ b/src/bz/fx/particle_simulation.h @@ -0,0 +1,27 @@ +#ifndef BZ_FX_PARTICLE_SIMULATION_H +#define BZ_FX_PARTICLE_SIMULATION_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZParticleSimulation BZParticleSimulation; +typedef BZParticleSimulation * BZParticleSimulationID; + +extern BZParticleSimulationID bzFXCreateParticleSimulation(BZMemoryArenaID arena, size_t maxParticles, BZParticleSystemID system, const char *identifierFmt, ...); + +extern void bzFXUpdateParticleSystem(BZParticleSimulationID simulation, float deltaTime); + +extern void bzFXSpawnParticles(BZParticleSimulationID simulation, BZIdentifierHash eventIdentifier, const BZVector *position, const float angle); +extern void bzFXDespawnParticle(BZParticleSimulationID simulation, size_t idx); // Do not use this + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/fx/particle_simulation_internal.h b/src/bz/fx/particle_simulation_internal.h new file mode 100644 index 0000000..97bce7f --- /dev/null +++ b/src/bz/fx/particle_simulation_internal.h @@ -0,0 +1,36 @@ +#ifndef BZ_FX_PARTICLE_SIMULATION_INTERNAL_H +#define BZ_FX_PARTICLE_SIMULATION_INTERNAL_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZParticleSimulation { + BZParticleSystemID system; + + size_t maxParticles; + size_t nextParticle; + float time; + + float *spawnTime; + float *lifetime; + float *positionX; + float *positionY; + float *size; + float *baseAngle; + float *angle; + float *color; + float *alpha; + void **userData; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/fx/particle_system.c b/src/bz/fx/particle_system.c new file mode 100644 index 0000000..389e114 --- /dev/null +++ b/src/bz/fx/particle_system.c @@ -0,0 +1,402 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 +void bzFXUpdateParticleSystemParticles(BZParticleSystemID particleSystem, size_t count, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]) { + BZMatrix local; + + for (size_t i = 0; i < count; ++i) { + float particleTicks = time - spawnTime[i]; + float l = lifetime[i]; + if (particleTicks <= l) { + BZParticleSubsystem *particleSubsystem = (BZParticleSubsystem *)userData[i]; + + float t = bzUnlerp(0, l, particleTicks); + size_t frame = (int)(t * ParticleSlices); + + size_t startIdx = bzFloor((spawnTime[i] + l) * 1000); + size_t tableIdx1 = frame + ((startIdx + 12) % ParticleVariants) * ParticleSlices; + size_t tableIdx2 = frame + ((startIdx + 34) % ParticleVariants) * ParticleSlices; + size_t tableIdx3 = frame + ((startIdx + 26) % ParticleVariants) * ParticleSlices; + size_t tableIdx4 = frame + ((startIdx + 74) % ParticleVariants) * ParticleSlices; + size_t tableIdx5 = frame + ((startIdx + 87) % ParticleVariants) * ParticleSlices; // These numbers don't matter. Use anything. + + float a = baseAngle[i] + particleSubsystem->angleValueTable[tableIdx1]; + bzMatrixRotation(&local, a); + angle[i] = a; + + BZVector v = bzVectorMake(particleSubsystem->velocityXValueTable[tableIdx2], particleSubsystem->velocityYValueTable[tableIdx2]); + bzMatrixTransformVector(&v, &v, &local); + + positionX[i] += v.x; + positionY[i] += v.y; + size[i] = particleSubsystem->sizeValueTable[tableIdx3]; + color[i] = particleSubsystem->colorValueTable[tableIdx4]; + alpha[i] = particleSubsystem->alphaValueTable[tableIdx5]; + } + } +} diff --git a/src/bz/fx/particle_system.h b/src/bz/fx/particle_system.h new file mode 100644 index 0000000..4ccfc17 --- /dev/null +++ b/src/bz/fx/particle_system.h @@ -0,0 +1,19 @@ +#ifndef BZ_FX_PARTICLE_SYSTEM_H +#define BZ_FX_PARTICLE_SYSTEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZParticleSystem BZParticleSystem; +typedef BZParticleSystem * BZParticleSystemID; + +extern BZParticleSystemID bzFXLoadParticleSystem(BZMemoryArenaID arena, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/fx/particle_system_internal.h b/src/bz/fx/particle_system_internal.h new file mode 100644 index 0000000..8172184 --- /dev/null +++ b/src/bz/fx/particle_system_internal.h @@ -0,0 +1,21 @@ +#ifndef BZ_FX_PARTICLE_SYSTEM_INTERNAL_H +#define BZ_FX_PARTICLE_SYSTEM_INTERNAL_H + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern size_t bzFXSpawnParticleSystemParticles(BZParticleSystemID particleSystem, BZIdentifierHash eventIdentifier, const BZVector *spawnPosition, const float spawnAngle, size_t maxCount, size_t currentIdx, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]); +extern void bzFXUpdateParticleSystemParticles(BZParticleSystemID particleSystem, size_t count, float time, float spawnTime[], float lifetime[], float positionX[], float positionY[], float size[], float baseAngle[], float angle[], float color[], float alpha[], void *userData[]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/game/actor.c b/src/bz/game/actor.c new file mode 100644 index 0000000..d742655 --- /dev/null +++ b/src/bz/game/actor.c @@ -0,0 +1,2 @@ +#include + diff --git a/src/bz/game/actor.h b/src/bz/game/actor.h new file mode 100644 index 0000000..21694eb --- /dev/null +++ b/src/bz/game/actor.h @@ -0,0 +1,17 @@ +#ifndef BZ_GAME_ACTOR_H +#define BZ_GAME_ACTOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZActor BZActor; +typedef BZActor * BZActorID; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/game/actor_internal.h b/src/bz/game/actor_internal.h new file mode 100644 index 0000000..6fd912e --- /dev/null +++ b/src/bz/game/actor_internal.h @@ -0,0 +1,27 @@ +#ifndef BZ_GAME_ACTOR_INTERNAL_H +#define BZ_GAME_ACTOR_INTERNAL_H + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZActor { + char identifier[kBZMaxIdentifierLength]; // fixme + BZIdentifierHash identifierHash; + + //BZScriptInstanceID actorScript; + uint32_t parameters[16]; // FIXME + SVMModuleInstance *instance; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/game/scene.c b/src/bz/game/scene.c new file mode 100644 index 0000000..907fccb --- /dev/null +++ b/src/bz/game/scene.c @@ -0,0 +1,553 @@ +#include + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // strncpy + +struct BZSceneActorDefinition { + //char identifier[kBZMaxIdentifierLength]; // fixme + BZIdentifierHash identifierHash; + SVMModule *module; + size_t instanceSize; + int32_t priority; + size_t spawnCount; +}; +typedef struct BZSceneActorDefinition BZSceneActorDefinition; + +/*struct BZSceneParticleSystem { + char identifier[kBZMaxIdentifierLength]; // fixme + BZIdentifierHash identifierHash; + BZParticleSystemID particleSystem; +}; +typedef struct BZSceneParticleSystem BZSceneParticleSystem;*/ + +struct BZSceneLayer { + char identifier[kBZMaxIdentifierLength]; // fixme + BZIdentifierHash identifierHash; + BZDrawQueueID drawQueue; + BZTilemapID tilemap; + BZParticleSimulationID particleSimulation; + BZCollisionSpaceID collisionSpace; + BZIdentifierHash particleCollisionTagHash; + float particleCollisionRadiusMultiplier; + int outputBuffer; +}; + +////#define kMaxActorInstances 50//256 + +struct BZSceneActor { + BZActor actor; + BZScriptBindingMetadata scriptMetadata; + int32_t priority; + bool running; +}; +typedef struct BZSceneActor BZSceneActor; + +#define kBZScriptingEnvironmentPublicCount 256 +#define kBZScriptingEnvironmentEventCount 256 + +struct BZScriptingEnvironmentPublic { + BZIdentifierHash identifierHash; + SVMOperand value; +}; +typedef struct BZScriptingEnvironmentPublic BZScriptingEnvironmentPublic; + +struct BZSceneEvent { + BZIdentifierHash identifierHash; + BZSceneEventData data; +}; +typedef struct BZSceneEvent BZSceneEvent; + +struct BZScene { +// BZScriptingEnvironmentID sceneEnvironment; +// BZScriptID sceneScript; +// BZScriptBindingMetadata sceneScriptMetadata; +// BZScriptInstanceID sceneScriptInstance; + BZSceneActorDefinition *actorDefinitions; + size_t actorDefinitionCount; + BZSceneLayer *layers; + size_t layerCount; + uint64_t ticks; + + int32_t minActorPriority; + int32_t maxActorPriority; + + size_t lastActorIndex; + size_t actorInstanceCount; + BZSceneActor *actorInstances; + + BZScriptingEnvironmentPublic publics[kBZScriptingEnvironmentPublicCount]; + + BZAudioPlaybackEngineID audioEngine; + +// BZAgentSimulationID agentSimulation; + + size_t eventCount; + BZSceneEvent *currentEvents; + size_t nextEnqueuedEvent; + BZSceneEvent *enqueuedEvents; + +// char targetSceneIdentifier[kBZMaxIdentifierLength]; // fixme +}; + +uint8_t gSystemTicksPerSecond; + +static void bzGameSceneActorLoadDefinition(BZSceneActorDefinition *definitionOut, BZSceneID scene, BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + definitionOut->identifierHash = bzIdentifierHashFromIdentifier(identifier); + + BZResourceID handle = bzResourcesOpenResource("scene", "assets/actors/%s.actor.json", identifier); + bzAssertMessage(handle != NULL, "Invalid actor definition '%s'", identifier); + + size_t length = bzResourcesFileLength(handle); + char *data = alloca(length);//(char *)bzMemoryAlloc(kBZSystemMemoryArena, length);//alloca(length); // FIXME, need working space off the stack + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *actorDefinitionJson = json_parse_string(data); + JSON_Object *actorDefinitionJsonObject = json_object(actorDefinitionJson); + + const char *scriptName = json_object_get_string(actorDefinitionJsonObject, "script"); + if (scriptName != NULL) { + definitionOut->module = bzScriptingLoadScriptModule(arena, "%s", scriptName); + definitionOut->instanceSize = svmGetModuleInstanceSize(definitionOut->module); + } + + definitionOut->priority = json_object_get_number(actorDefinitionJsonObject, "priority"); + scene->minActorPriority = bzMin(scene->minActorPriority, definitionOut->priority); + scene->maxActorPriority = bzMax(scene->maxActorPriority, definitionOut->priority); + + json_value_free(actorDefinitionJson); + + bzLog("Opened actor %s", identifier); +} + +static BZActorID bzGameSceneSetupActor(BZSceneActor *sceneActor, BZSceneID scene, const BZSceneActorDefinition *definition, const char *identifierFmt, ...) { + BZActorID actor = &sceneActor->actor; + BZScriptBindingMetadata *metadata = &sceneActor->scriptMetadata; + + bzInsertIdentifier(actor->identifier, identifierFmt); + actor->identifierHash = bzIdentifierHashFromIdentifier(actor->identifier); + + bzAssertMessage(definition != NULL, "Could not find definition for '%s'", actor->identifier); + + sceneActor->priority = definition->priority; + + actor->instance = svnResetModuleInstanceMemory(definition->module, actor->instance, metadata, bzScriptingBindingsLookupNativeFunction); + bzScriptingInitialiseMetadata((BZScriptBindingMetadata *)&sceneActor->scriptMetadata, actor, scene, scene->lastActorIndex); + + sceneActor->running = true; + + return actor; +} + +BZSceneID bzGameSceneSwitch(BZMemoryArenaID arena, BZAudioPlaybackEngineID audioEngine, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZSceneID scene = bzMemoryAlloc(arena, sizeof(BZScene)); + + BZResourceID handle = bzResourcesOpenResource("scene", "assets/scenes/%s.scene.json", identifier); + size_t length = bzResourcesFileLength(handle); + char *data = alloca(length);//(char *)bzMemoryAlloc(kBZSystemMemoryArena, length);//alloca(length); // FIXME, need working space off the stack + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *sceneJson = json_parse_string(data); + JSON_Object *sceneJsonObject = json_object(sceneJson); + + const char *soundbankName = json_object_get_string(sceneJsonObject, "soundbank"); + if (soundbankName != NULL && audioEngine != NULL) { + scene->audioEngine = audioEngine; + bzAudioPlaybackUseSoundbank(scene->audioEngine, arena, soundbankName); + } + + const char *spritesheetName = json_object_get_string(sceneJsonObject, "spritesheet"); + if (spritesheetName != NULL) { + size_t imageWidth, imageHeight; + void *imageData = bzGfxLoadAsepriteImage(arena, &imageWidth, &imageHeight, spritesheetName); // FIXME, temp arena + bzGfxPrepareSpritesheet(arena, imageWidth, imageHeight, imageData); + //free(imageData); + } + + const char *fontName = json_object_get_string(sceneJsonObject, "font"); + BZFont *font = bzGfxLoadFont(arena, fontName); // FIXME, temp arena?? + bzGfxPrepareFont(arena, font); + + JSON_Array *actorDefinitionsArray = json_object_get_array(sceneJsonObject, "actors"); + size_t actorDefinitionsArrayCount = json_array_get_count(actorDefinitionsArray); + scene->actorDefinitionCount = actorDefinitionsArrayCount; + + size_t startDefinition = 0; + const char *sceneActorName = json_object_get_string(sceneJsonObject, "scene_actor"); + if (sceneActorName != NULL) { + scene->actorDefinitionCount += 1; + startDefinition = 1; + } + + scene->actorDefinitions = (BZSceneActorDefinition *)bzMemoryAlloc(arena, scene->actorDefinitionCount * sizeof(BZSceneActorDefinition)); + + size_t maxInstanceSize = 0; + + if (sceneActorName != NULL) { + bzGameSceneActorLoadDefinition(&scene->actorDefinitions[0], scene, arena, "%s", sceneActorName); // fixme + maxInstanceSize = scene->actorDefinitions[0].instanceSize; + } + + for (size_t i = 0; i < actorDefinitionsArrayCount; ++i) { + const char *actorName = json_array_get_string(actorDefinitionsArray, i); + size_t outIdx = i + startDefinition; + bzGameSceneActorLoadDefinition(&scene->actorDefinitions[outIdx], scene, arena, "%s", actorName); // fixme + maxInstanceSize = bzMax(maxInstanceSize, scene->actorDefinitions[outIdx].instanceSize); + } + + // FIXME, alignment for instance size + + scene->lastActorIndex = 0; + scene->actorInstanceCount = 1024; + + scene->actorInstances = (BZSceneActor *)bzMemoryAlloc(arena, scene->actorInstanceCount * sizeof(BZSceneActor)); + for (size_t i = 0; i < scene->actorInstanceCount; ++i) { + scene->actorInstances[i].actor.instance = bzMemoryAlloc(arena, maxInstanceSize); + } + + if (sceneActorName != NULL) { + bzGameSceneSetupActor(&scene->actorInstances[0], scene, &scene->actorDefinitions[0], "scene"); + } + +// int hasAgents = json_object_get_boolean(sceneJsonObject, "agents"); +// if (hasAgents > 0) { +// scene->agentSimulation = bzFXCreateAgentSimulation(arena, scene->actorInstanceCount, "%s.agents", identifier); +// } + + JSON_Array *layerDefinitionsArray = json_object_get_array(sceneJsonObject, "layers"); + scene->layerCount = json_array_get_count(layerDefinitionsArray); + scene->layers = bzMemoryAlloc(arena, scene->layerCount * sizeof(BZSceneLayer)); + for (size_t i = 0; i < scene->layerCount; ++i) { + JSON_Object *layerDefinition = json_array_get_object(layerDefinitionsArray, i); + stbsp_snprintf(scene->layers[i].identifier, kBZMaxIdentifierLength, "%s", json_object_get_string(layerDefinition, "identifier")); + scene->layers[i].identifierHash = bzIdentifierHashFromIdentifier(scene->layers[i].identifier); + + const char *particleSystemName = json_object_get_string(layerDefinition, "particles"); + if (particleSystemName != NULL) { + BZParticleSystemID particleSystem = bzFXLoadParticleSystem(arena, "%s", particleSystemName); // FIXME, pool these... + int hasCollision2 = json_object_get_boolean(layerDefinition, "collision"); // FIXME + scene->layers[i].particleSimulation = bzFXCreateParticleSimulation(arena, (hasCollision2 > 0) ? 1024 : (5 * 1024), particleSystem, "%s.particles", scene->layers[i].identifier); // FIXME, memory + } + + if (json_object_has_value_of_type(layerDefinition, "output", JSONNumber)) { + scene->layers[i].outputBuffer = (int)json_object_get_number(layerDefinition, "output"); + if (particleSystemName == NULL || json_object_get_boolean(layerDefinition, "custom-particle") > 0) { + scene->layers[i].drawQueue = bzGfxDrawQueueCreate(arena, "scene.%s.drawQueue", scene->layers[i].identifier); + } + } + + const char *tilemapName = json_object_get_string(layerDefinition, "tilemap"); + if (tilemapName != NULL) { + scene->layers[i].tilemap = bzGameLoadTilemap(arena, "%s", tilemapName); + } + + int hasCollision = json_object_get_boolean(layerDefinition, "collision"); + if (hasCollision > 0) { + if (scene->layers[i].particleSimulation == NULL) { + scene->layers[i].collisionSpace = bzCollisionMakeSpace(arena, scene->actorInstanceCount, "%s.collision", scene->layers[i].identifier); + } else { + scene->layers[i].collisionSpace = bzCollisionMakeSpace(arena, 1024, "%s.particle.collision", scene->layers[i].identifier); + + if (json_object_has_value_of_type(layerDefinition, "collision-radius", JSONNumber)) { + scene->layers[i].particleCollisionRadiusMultiplier = json_object_get_number(layerDefinition, "collision-radius"); + } else { + scene->layers[i].particleCollisionRadiusMultiplier = 1.0f; + } + scene->layers[i].particleCollisionTagHash = bzIdentifierHashFromIdentifier(particleSystemName); + } + } + } + + scene->currentEvents = bzMemoryAlloc(arena, kBZScriptingEnvironmentEventCount * sizeof(BZSceneEvent)); + scene->enqueuedEvents = bzMemoryAlloc(arena, kBZScriptingEnvironmentEventCount * sizeof(BZSceneEvent)); + + json_value_free(sceneJson); + + bzLog("Opened scene %s", identifier); + + return scene; +} + +bool bzGameSceneUpdate(BZSceneID scene, uint8_t pauseFlags, uint8_t systemTicks) { + float deltaTime = (float)systemTicks / (float)gSystemTicksPerSecond; + + for (size_t i = 0; i < scene->layerCount; ++i) { + BZSceneLayer *layer = &scene->layers[i]; + if (layer->particleSimulation == NULL) { + bzGfxDrawQueueClear(layer->drawQueue); + } + if (layer->collisionSpace != NULL) { + bzCollisionResetSpace(layer->collisionSpace); + } + } + +// if (scene->agentSimulation != NULL) { +// SVMOperand playerX; +// bzScriptingGetEnvironmentPublic(&playerX, scene, bzIdentifierHashFromIdentifier("shipX")); +// +// SVMOperand playerY; +// bzScriptingGetEnvironmentPublic(&playerY, scene, bzIdentifierHashFromIdentifier("shipY")); +// +// BZVector playerPos = bzVectorMake(FIXED_TO_FLOAT(playerX.floatLiteral), FIXED_TO_FLOAT(playerY.floatLiteral)); +// +// bzFXUpdateAgentSimulation(scene->agentSimulation, deltaTime, NULL, &playerPos); +// } + + for (size_t i = 0; i < scene->layerCount; ++i) { + BZSceneLayer *layer = &scene->layers[i]; + if (layer->particleSimulation != NULL) { + bzFXUpdateParticleSystem(layer->particleSimulation, deltaTime); + if (layer->collisionSpace != NULL) { + bzCollisionPopulateParticles(layer->collisionSpace, layer->particleSimulation, layer->particleCollisionTagHash, layer->particleCollisionRadiusMultiplier); + } + } + } + + for (int32_t priority = scene->minActorPriority; priority <= scene->maxActorPriority; ++priority) { + for (size_t i = 0; i < scene->actorInstanceCount; ++i) { + BZSceneActor *sceneActor = &scene->actorInstances[i]; + if (sceneActor->running && sceneActor->priority == priority) { + SVMRunOutcome outcome = svmRunModuleInstance(sceneActor->actor.instance); + sceneActor->running = (outcome == SVMRunOutcomeSuspended); + } + } + } + + scene->ticks += systemTicks; + + scene->eventCount = scene->nextEnqueuedEvent; + scene->nextEnqueuedEvent = 0; + + BZSceneEvent *tmp = scene->currentEvents; + scene->currentEvents = scene->enqueuedEvents; + scene->enqueuedEvents = tmp; + + return scene->actorInstances[0].running; +} + +void bzGameSceneDraw(BZSceneID scene) { + for (size_t i = 0; i < scene->layerCount; ++i) { + bzSetOutputBuffer(scene->layers[i].outputBuffer); + if (scene->layers[i].particleSimulation != NULL) { + bzGfxDrawParticles(scene->layers[i].particleSimulation, scene->layers[i].drawQueue); + } else { + bzGfxDrawQueueRun(scene->layers[i].drawQueue); + } + + //if (scene->layers[i].collisionSpace != NULL) { + // bzCollisionDrawDebug(scene->layers[i].collisionSpace); + //} + +// if (i == 3 && scene->agentSimulation != NULL) { // FIXME +// bzFXAgentSimulationDrawDebug(scene->agentSimulation); +// } + } + + +} + +BZActorID bzGameSceneAddActor(BZSceneID scene, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZIdentifierHash definitionIdentifierHash = bzIdentifierHashFromIdentifier(identifier); + BZSceneActorDefinition *definition = NULL; + for (size_t i = 0; i < scene->actorDefinitionCount; ++i) { // fixme + if (definitionIdentifierHash == scene->actorDefinitions[i].identifierHash) { + definition = &scene->actorDefinitions[i]; + break; + } + } + bzAssertMessage(definition != NULL, "Undefined actor '%s'", identifier); + + BZSceneActor *sceneActor = NULL; + for (size_t i = 0; i < scene->actorInstanceCount; ++i) { + ++scene->lastActorIndex; + + size_t idx = (scene->lastActorIndex % (scene->actorInstanceCount - 1)) + 1; // 0 is only for the scene script + if (scene->actorInstances[idx].running == false) { + sceneActor = &scene->actorInstances[idx]; + break; + } + } + bzAssertMessage(sceneActor != NULL, "Out of actors"); + + definition->spawnCount += 1; + + return bzGameSceneSetupActor(sceneActor, scene, definition, "%s-%u", identifier, definition->spawnCount); +} + +BZActorID bzGameSceneFindActor(BZSceneID scene, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + BZIdentifierHash identifierHash = bzIdentifierHashFromIdentifier(identifier); + for (size_t i = 0; i < scene->actorInstanceCount; ++i) { // fixme + if (scene->actorInstances[i].actor.identifierHash == identifierHash) { + return &scene->actorInstances[i].actor; + } + } + return NULL; +} + +float bzGameSceneGetTime(BZSceneID scene) { + float time = (float)scene->ticks / (float)gSystemTicksPerSecond; + return time; +} + +bool bzGameSceneQueryEvent(BZSceneEventData *dataOut, BZSceneID scene, BZIdentifierHash eventIdentifier) { + for (size_t i = 0; i < scene->eventCount; ++i) { + BZSceneEvent *event = &scene->currentEvents[i]; + if (event->identifierHash == eventIdentifier) { + if (dataOut != NULL) { + *dataOut = event->data; + } + return true; + } + } + return false; +} + +void bzGameScenePostEvent(BZSceneID scene, BZIdentifierHash eventIdentifier, const BZSceneEventData *data) { + bzAssert(scene->nextEnqueuedEvent < kBZScriptingEnvironmentEventCount); + BZSceneEvent *event = &scene->enqueuedEvents[scene->nextEnqueuedEvent++]; + event->identifierHash = eventIdentifier; + if (data != NULL) { + event->data = *data; + } +} + +BZSceneLayer *getLayer(BZSceneID scene, BZIdentifierHash layerIdentifierHash) { + for (size_t i = 0; i < scene->layerCount; ++i) { + if (scene->layers[i].identifierHash == layerIdentifierHash) { + return &scene->layers[i]; + } + } + return NULL; +} + +BZTilemapID bzGameGetSceneLayerTilemap(BZSceneID scene, BZIdentifierHash layerIdentifierHash) { + BZSceneLayer *layer = getLayer(scene, layerIdentifierHash); + if (layer != NULL) { + return layer->tilemap; + } else { + return NULL; + } +} + +BZDrawQueueID bzGameGetSceneLayerDrawQueue(BZSceneID scene, BZIdentifierHash layerIdentifierHash) { + BZSceneLayer *layer = getLayer(scene, layerIdentifierHash); + if (layer != NULL) { + return layer->drawQueue; + } else { + return NULL; + } +} + +BZParticleSimulationID bzGameGetSceneLayerParticleSimulation(BZSceneID scene, BZIdentifierHash layerIdentifierHash) { + BZSceneLayer *layer = getLayer(scene, layerIdentifierHash); + if (layer != NULL) { + return layer->particleSimulation; + } else { + return NULL; + } +} + +BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace(BZSceneID scene, BZIdentifierHash layerIdentifierHash) { + BZSceneLayer *layer = getLayer(scene, layerIdentifierHash); + if (layer != NULL) { + return layer->collisionSpace; + } else { + return NULL; + } +} + + +/* +extern void bzGameSceneSetTimeTicksPerSecond(uint8_t tps); +extern void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags); // Pause timer on these flags +extern float bzGameSceneTime(uint8_t timeIdx); + +void bzGameSceneSetTimeTicksPerSecond(uint8_t tps) { + sceneTPS = tps; +} + +void bzGameSceneSetTimePauseFlags(uint8_t timeIdx, uint8_t pauseFlags) { + times[timeIdx].pauseFlags = pauseFlags; +} + +float bzGameSceneTime(uint8_t timeIdx) { + float time = (float)times[timeIdx].ticks / (float)sceneTPS; + return time; +} +*/ + +//BZAgentSimulationID bzGameGetAgentSimulation(BZSceneID scene) { +// return scene->agentSimulation; +//} + +BZAudioPlaybackEngineID bzGameGetAudioPlaybackEngine(BZSceneID scene) { + return scene->audioEngine; +} + +bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZSceneID scene, BZIdentifierHash identifierHash) { + for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) { + if (scene->publics[i].identifierHash == identifierHash) { + if (out != NULL) { + out->__raw = scene->publics[i].value.__raw; + } + return true; + } + } + return false; +} + +void bzScriptingSetEnvironmentPublic(BZSceneID scene, BZIdentifierHash identifierHash, SVMOperand value) { + for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) { + if (scene->publics[i].identifierHash == 0) { + scene->publics[i].identifierHash = identifierHash; + } + if (scene->publics[i].identifierHash == identifierHash) { + scene->publics[i].value.__raw = value.__raw; + return; + } + } + bzError("Out of globals!!"); +} + +size_t bzGameGetSceneLayerCount(BZSceneID scene) { + return scene->layerCount; +} + +BZSceneLayerID bzGameGetSceneLayerAtIndex(BZSceneID scene, size_t idx) { + bzAssert(idx < scene->layerCount); + return &scene->layers[idx]; +} + + +BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace2(BZSceneLayerID layer) { + return layer->collisionSpace; +} + +BZParticleSimulationID bzGameGetSceneLayerParticleSimulation2(BZSceneLayerID layer) { + return layer->particleSimulation; +} diff --git a/src/bz/game/scene.h b/src/bz/game/scene.h new file mode 100644 index 0000000..6fb195a --- /dev/null +++ b/src/bz/game/scene.h @@ -0,0 +1,48 @@ +#ifndef BZ_GAME_SCENE_H +#define BZ_GAME_SCENE_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZSceneEventData { + union { + uint32_t integerValue; + const char *stringValue; + void *value; + }; +}; +typedef struct BZSceneEventData BZSceneEventData; + +typedef struct BZScene BZScene; +typedef BZScene * BZSceneID; + +extern BZSceneID bzGameSceneSwitch(BZMemoryArenaID arena, BZAudioPlaybackEngineID audioEngine, const char *identifierFmt, ...); + +//extern void bzGameSceneGetTargetSceneIdentifier(char *identifierOut, BZSceneID scene); // We wipe our memory so we need to copy this out + +//.extern BZMemoryArenaID bzGameSceneAddActorExt(BZGameSceneActorConfiguration **configurationOutPtr, const char *identifierFmt, ...); +extern bool bzGameSceneUpdate(BZSceneID scene, uint8_t pauseFlags, uint8_t systemTicks); +extern void bzGameSceneDraw(BZSceneID scene); + +extern BZActorID bzGameSceneAddActor(BZSceneID scene, const char *identifierFmt, ...); +extern BZActorID bzGameSceneFindActor(BZSceneID scene, const char *identifierFmt, ...); + +extern float bzGameSceneGetTime(BZSceneID scene); + +extern bool bzGameSceneQueryEvent(BZSceneEventData *dataOut, BZSceneID scene, BZIdentifierHash eventIdentifier); +extern void bzGameScenePostEvent(BZSceneID scene, BZIdentifierHash eventIdentifier, const BZSceneEventData *data); + +extern BZIdentifierHash bzSceneChangeEventIdentifier; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/game/scene_identifiers.cpp b/src/bz/game/scene_identifiers.cpp new file mode 100644 index 0000000..4a3ae46 --- /dev/null +++ b/src/bz/game/scene_identifiers.cpp @@ -0,0 +1,4 @@ +#include +#include + +BZIdentifierHash bzSceneChangeEventIdentifier = COMPILE_TIME_CRC32_STR("sceneChange"); diff --git a/src/bz/game/scene_internal.h b/src/bz/game/scene_internal.h new file mode 100644 index 0000000..421bc4d --- /dev/null +++ b/src/bz/game/scene_internal.h @@ -0,0 +1,65 @@ +#ifndef BZ_GAME_SCENE_INTERNAL_H +#define BZ_GAME_SCENE_INTERNAL_H + +#include + +#include +//#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZScriptBindingMetadata { + BZActorID actor; + BZSceneID scene; + BZIdentifierHash layer; + size_t sortIdx; + BZDrawQueueID drawQueue; + BZVector position; + float angle; + BZVector scale; + BZMatrix matrix; + uint32_t uuid; +// BZAgentID agent; +}; +typedef struct BZScriptBindingMetadata BZScriptBindingMetadata; + +typedef struct BZSceneLayer BZSceneLayer; +typedef BZSceneLayer * BZSceneLayerID; + +extern size_t bzGameGetSceneLayerCount(BZSceneID scene); +extern BZSceneLayerID bzGameGetSceneLayerAtIndex(BZSceneID scene, size_t idx); + +extern BZParticleSimulationID bzGameGetSceneLayerParticleSimulation2(BZSceneLayerID layer); // FIXME, what do we want to do... +extern BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace2(BZSceneLayerID layer); + +extern BZTilemapID bzGameGetSceneLayerTilemap(BZSceneID scene, BZIdentifierHash layerIdentifierHash); +extern BZDrawQueueID bzGameGetSceneLayerDrawQueue(BZSceneID scene, BZIdentifierHash layerIdentifierHash); +extern BZParticleSimulationID bzGameGetSceneLayerParticleSimulation(BZSceneID scene, BZIdentifierHash layerIdentifierHash); +extern BZCollisionSpaceID bzGameGetSceneLayerCollisionSpace(BZSceneID scene, BZIdentifierHash layerIdentifierHash); + +//extern BZAgentSimulationID bzGameGetAgentSimulation(BZSceneID scene); + +extern BZAudioPlaybackEngineID bzGameGetAudioPlaybackEngine(BZSceneID scene); + +extern void bzGameSceneSetTargetSceneIdentifier(BZSceneID scene, const char *identifier); + +extern bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZSceneID scene, BZIdentifierHash identifierHash); +extern void bzScriptingSetEnvironmentPublic(BZSceneID scene, BZIdentifierHash identifierHash, SVMOperand value); + +extern void bzScriptingInitialiseMetadata(BZScriptBindingMetadata *metadata, BZActorID actor, BZSceneID scene, uint32_t uuid); + +extern uint8_t gSystemTicksPerSecond; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/game/tilemap.c b/src/bz/game/tilemap.c new file mode 100644 index 0000000..3600fdb --- /dev/null +++ b/src/bz/game/tilemap.c @@ -0,0 +1,181 @@ +#include + +#include +#include +#include +#include +#include + +static uint32_t tilemapAddTileTagFlag(BZTilemapID tilemap, BZIdentifierHash tag) { + for (size_t i = 0; i < 32; ++i) { + if (tag == tilemap->tags[i]) { + return 1 << i; + } + + if (tilemap->tags[i] == 0) { + tilemap->tags[i] = tag; + return 1 << i; + } + } + + bzAssertMessage(false, "Out of flags"); + + return 0; +} + +BZTilemapID bzGameLoadTilemap(BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZResourceID handle = bzResourcesOpenResource("tilemap", "assets/tilemaps/%s.tilemap.json", identifier); + size_t length = bzResourcesFileLength(handle); + char *data = (char *)alloca(length); // FIXME, need working space off the stack + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *tilemapJson = json_parse_string(data); + JSON_Object *tilemapJsonObject = json_object(tilemapJson); + + size_t tilemapWidth = json_object_get_number(tilemapJsonObject, "width"); + size_t tilemapHeight = json_object_get_number(tilemapJsonObject, "height"); + + BZTilemap *tilemap = (BZTilemap *)bzMemoryAlloc(arena, sizeof(BZTilemap) + tilemapWidth * tilemapHeight * sizeof(BZTilemapTile)); + tilemap->width = tilemapWidth; + tilemap->height = tilemapHeight; + + JSON_Array *tilesArray = json_object_get_array(tilemapJsonObject, "tiles"); + size_t tilesArraySize = json_array_get_count(tilesArray); + for (size_t y = 0, i = 0; y < tilemap->height; ++y) { + for (size_t x = 0; x < tilemap->width; ++x, ++i) { + if (i < tilesArraySize) { + uint8_t t = (uint8_t)json_array_get_number(tilesArray, i); + tilemap->tiles[i].tileDefinitionIdx = t; + tilemap->tiles[i].spriteIdx = 0; + + bzAssert(t < 256); // fixme, define + tilemap->tileDefinitions[t].spriteIdx = t; + tilemap->tileDefinitions[t].height = 1; + tilemap->tileDefinitions[t].tagsFlag = 0; + } else { + tilemap->tiles[i].tileDefinitionIdx = 0; + tilemap->tiles[i].spriteIdx = 0; + } + } + } + + JSON_Array *tileTagArray = json_object_get_array(tilemapJsonObject, "tags"); + size_t tileTagArraySize = json_array_get_count(tileTagArray); + for (size_t i = 0; i < tileTagArraySize; ++i) { + JSON_Object *tileTagObject = json_array_get_object(tileTagArray, i); + BZIdentifierHash tag = bzIdentifierHashFromIdentifier(json_object_get_string(tileTagObject, "tag")); + + uint32_t tagsFlag = tilemapAddTileTagFlag(tilemap, tag); + + JSON_Array *tileArray = json_object_get_array(tileTagObject, "tiles"); + size_t tileArraySize = json_array_get_count(tileArray); + for (size_t j = 0; j < tileArraySize; ++j) { + size_t t = (size_t)json_array_get_number(tileArray, j); + bzAssert(t < 256); // fixme, define + tilemap->tileDefinitions[t].tagsFlag |= tagsFlag; + } + } + + JSON_Array *tileDefinitionArray = json_object_get_array(tilemapJsonObject, "definitions"); + size_t tileDefinitionArraySize = json_array_get_count(tileDefinitionArray); + for (size_t i = 0; i < tileDefinitionArraySize; ++i) { + JSON_Object *tileDefinitionObject = json_array_get_object(tileDefinitionArray, i); + size_t tileIndex = json_object_get_number(tileDefinitionObject, "tile"); + bzAssert(tileIndex < 256); // fixme + + if (json_object_has_value(tileDefinitionObject, "height")) { + tilemap->tileDefinitions[tileIndex].height = json_object_get_number(tileDefinitionObject, "height"); + } + + if (json_object_has_value(tileDefinitionObject, "tags")) { + JSON_Array *tileTagArray = json_object_get_array(tileDefinitionObject, "tags"); + size_t tileTagArraySize = json_array_get_count(tileTagArray); + for (size_t j = 0; j < tileTagArraySize; ++j) { + BZIdentifierHash tag = bzIdentifierHashFromIdentifier(json_array_get_string(tileTagArray, j)); + + uint32_t tagsFlag = tilemapAddTileTagFlag(tilemap, tag); + + tilemap->tileDefinitions[tileIndex].tagsFlag |= tagsFlag; + } + } + + if (json_object_has_value(tileDefinitionObject, "sprite")) { + JSON_Array *tileSpritesArray = json_object_get_array(tileDefinitionObject, "sprite"); + size_t tileSpritesArraySize = json_array_get_count(tileSpritesArray); + for (size_t y = 0, j = 0; y < tilemap->height; ++y) { + for (size_t x = 0; x < tilemap->width; ++x, ++j) { + if (tilemap->tiles[j].tileDefinitionIdx == tileIndex) { + tilemap->tiles[j].spriteIdx = (uint8_t)json_array_get_number(tileSpritesArray, bzRandomInteger(tileSpritesArraySize)); + } + } + } + } + } + + json_value_free(tilemapJson); + + return tilemap; +} + +uint8_t bzGameGetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my) { + size_t tileIdx = my * tilemap->width + mx; + + //uint8_t spriteIdx = 0; + + uint8_t tileDefinitionIdx = tilemap->tiles[tileIdx].tileDefinitionIdx; + /*if (tileDefinitionIdx > 0) { + spriteIdx = tilemap->tiles[tileIdx].spriteIdx; + if (spriteIdx == 0) { + spriteIdx = tilemap->tileDefinitions[tileDefinitionIdx].spriteIdx; + } + }*/ + + return tileDefinitionIdx; +} + +uint8_t bzGameGetTilemapSprite(BZTilemapID tilemap, uint8_t mx, uint8_t my) { + size_t tileIdx = my * tilemap->width + mx; + + uint8_t spriteIdx = 0; + + uint8_t tileDefinitionIdx = bzGameGetTilemapTile(tilemap, mx, my); + if (tileDefinitionIdx > 0) { + spriteIdx = tilemap->tiles[tileIdx].spriteIdx; + if (spriteIdx == 0) { + spriteIdx = tilemap->tileDefinitions[tileDefinitionIdx].spriteIdx; + } + } + + return spriteIdx; +} + +uint8_t bzGameSetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my, uint8_t idx) { + size_t tileIdx = my * tilemap->width + mx; + tilemap->tiles[tileIdx].spriteIdx = idx; + return bzGameGetTilemapTile(tilemap, mx, my); +} + +bool bzGameGetTilemapHasFlag(BZTilemapID tilemap, uint8_t mx, uint8_t my, BZIdentifierHash tag) { + uint32_t flagMask = bzGameCalculateTilemapFlagsMask(tilemap, 1, &tag); + uint8_t tileDefinitionIdx = bzGameGetTilemapTile(tilemap, mx, my); + return (tilemap->tileDefinitions[tileDefinitionIdx].tagsFlag & flagMask) > 0; +} + +uint32_t bzGameCalculateTilemapFlagsMask(BZTilemapID tilemap, size_t tagCount, BZIdentifierHash tags[]) { + uint32_t flagMask = 0; + + for (size_t i = 0; i < tagCount; ++i) { + for (size_t j = 0; j < 32; ++j) { + if (tags[i] == tilemap->tags[j]) { + flagMask |= 1 << j; + break; + } + } + } + + return flagMask; +} diff --git a/src/bz/game/tilemap.h b/src/bz/game/tilemap.h new file mode 100644 index 0000000..8e625d5 --- /dev/null +++ b/src/bz/game/tilemap.h @@ -0,0 +1,29 @@ +#ifndef BZ_GAME_TILEMAP_H +#define BZ_GAME_TILEMAP_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZTilemap BZTilemap; +typedef BZTilemap * BZTilemapID; + +extern BZTilemapID bzGameLoadTilemap(BZMemoryArenaID arena, const char *identifierFmt, ...); + +extern uint8_t bzGameGetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my); +extern uint8_t bzGameSetTilemapTile(BZTilemapID tilemap, uint8_t mx, uint8_t my, uint8_t idx); + +extern uint8_t bzGameGetTilemapSprite(BZTilemapID tilemap, uint8_t mx, uint8_t my); +extern bool bzGameGetTilemapHasFlag(BZTilemapID tilemap, uint8_t mx, uint8_t my, BZIdentifierHash tag); + +extern uint32_t bzGameCalculateTilemapFlagsMask(BZTilemapID tilemap, size_t tagCount, BZIdentifierHash tags[]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/game/tilemap_internal.h b/src/bz/game/tilemap_internal.h new file mode 100644 index 0000000..89ba298 --- /dev/null +++ b/src/bz/game/tilemap_internal.h @@ -0,0 +1,36 @@ +#ifndef BZ_GAME_TILEMAP_INTERNAL_H +#define BZ_GAME_TILEMAP_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZTilemapTileDefinition { + //bool replaceSprite; + uint8_t spriteIdx; + int height; + uint32_t tagsFlag; +}; +typedef struct BZTilemapTileDefinition BZTilemapTileDefinition; + +struct BZTilemapTile { + uint8_t spriteIdx; + uint8_t tileDefinitionIdx; +}; +typedef struct BZTilemapTile BZTilemapTile; + +struct BZTilemap { + size_t width; + size_t height; + BZIdentifierHash tags[32]; + BZTilemapTileDefinition tileDefinitions[256]; + BZTilemapTile tiles[]; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/aseprite.c b/src/bz/gfx/aseprite.c new file mode 100644 index 0000000..69dc446 --- /dev/null +++ b/src/bz/gfx/aseprite.c @@ -0,0 +1,467 @@ +#include + +#include +#include +#include +#include +#include +#include // Apparently stb_image.h need this?? +#include + +#include // Static assertions + +#define PACKED_STRUCT __attribute__((__packed__)) + +typedef uint8_t ASE_BYTE; +typedef uint16_t ASE_WORD; +typedef int16_t ASE_SHORT; +typedef uint32_t ASE_DWORD; +typedef uint32_t ASE_LONG; +typedef uint32_t ASE_FIXED; +typedef float ASE_FLOAT; +typedef double ASE_DOUBLE; +typedef uint64_t ASE_QWORD; +typedef int64_t ASE_LONG64; + + + +struct PACKED_STRUCT ASE_STRING { + ASE_WORD length; + //ASE_BYTE characters[]; +}; +typedef struct ASE_STRING ASE_STRING; +static_assert(sizeof(ASE_STRING) == 2, "ASE_STRING is wrong size"); + +struct PACKED_STRUCT ASE_POINT { + ASE_LONG x; + ASE_LONG y; +}; +typedef struct ASE_POINT ASE_POINT; +static_assert(sizeof(ASE_POINT) == 8, "ASE_POINT is wrong size"); + +struct PACKED_STRUCT ASE_SIZE { + ASE_LONG width; + ASE_LONG height; +}; +typedef struct ASE_SIZE ASE_SIZE; +static_assert(sizeof(ASE_SIZE) == 8, "ASE_SIZE is wrong size"); + +struct PACKED_STRUCT ASE_RECT { + ASE_POINT origin; + ASE_SIZE size; +}; +typedef struct ASE_RECT ASE_RECT; +static_assert(sizeof(ASE_RECT) == 16, "ASE_RECT is wrong size"); + +struct PACKED_STRUCT ASE_PIXEL_RBZA { + ASE_BYTE r; + ASE_BYTE g; + ASE_BYTE b; + ASE_BYTE a; +}; +typedef struct ASE_PIXEL_RBZA ASE_PIXEL_RBZA; +static_assert(sizeof(ASE_PIXEL_RBZA) == 4, "ASE_PIXEL_RBZA is wrong size"); + +struct PACKED_STRUCT ASE_PIXEL_GRAYSCALE { + ASE_BYTE v; + ASE_BYTE a; +}; +typedef struct ASE_PIXEL_GRAYSCALE ASE_PIXEL_GRAYSCALE; +static_assert(sizeof(ASE_PIXEL_GRAYSCALE) == 2, "ASE_PIXEL_GRAYSCALE is wrong size"); + +struct PACKED_STRUCT ASE_PIXEL_INDEXED { + ASE_BYTE idx; +}; +typedef struct ASE_PIXEL_INDEXED ASE_PIXEL_INDEXED; +static_assert(sizeof(ASE_PIXEL_INDEXED) == 1, "ASE_PIXEL_INDEXED is wrong size"); + +struct PACKED_STRUCT ASE_Header { + ASE_DWORD fileSize; + ASE_WORD magicNumber; // 0xA5E0 + ASE_WORD frames; + ASE_WORD width; + ASE_WORD height; + ASE_WORD depth; + ASE_DWORD flags; + ASE_WORD ___speed; + ASE_DWORD zero1; + ASE_DWORD zero2; + ASE_BYTE transparentColorIdx; + ASE_BYTE ___ignore[3]; + ASE_WORD numColors; + ASE_BYTE pixelWidth; + ASE_BYTE pixelHeight; + ASE_SHORT gridX; + ASE_SHORT gridY; + ASE_WORD gridWidth; + ASE_WORD gridHeight; + ASE_BYTE ___reserved[84]; +}; +typedef struct ASE_Header ASE_Header; +static_assert(sizeof(ASE_Header) == 128, "ASE_Header is wrong size"); + +struct PACKED_STRUCT ASE_Frame { + ASE_DWORD frameBytes; + ASE_WORD magicNumber; // 0xF1FA + ASE_WORD ___numChunks; + ASE_WORD duration; + ASE_BYTE ___reserved[2]; + ASE_DWORD numChunks; +}; +typedef struct ASE_Frame ASE_Frame; +static_assert(sizeof(ASE_Frame) == 16, "ASE_Frame is wrong size"); + +struct PACKED_STRUCT ASE_Chunk { + ASE_DWORD chunkSize; + ASE_WORD type; + //ASE_BYTE data[]; +}; +typedef struct ASE_Chunk ASE_Chunk; +static_assert(sizeof(ASE_Chunk) == 6, "ASE_Chunk is wrong size"); + +#if 0 +struct PACKED_STRUCT ASE_Chunk_OldPalette { + ASE_WORD numPackets; + //BZAsepriteOldPalettePacket packets[]; +}; +typedef struct ASE_Chunk_OldPalette ASE_Chunk_OldPalette; +static_assert(sizeof(ASE_Chunk_OldPalette) == 2, "ASE_Chunk_OldPalette is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet { + ASE_BYTE skip; + ASE_BYTE numColors; + //BZAsepriteOldPaletteColor colors[]; +}; +typedef struct ASE_Chunk_OldPalette_Packet ASE_Chunk_OldPalette_Packet; +static_assert(sizeof(ASE_Chunk_OldPalette_Packet) == 2, "ASE_Chunk_OldPalette_Packet is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_OldPalette_Packet_Color { + ASE_BYTE r; + ASE_BYTE g; + ASE_BYTE b; +}; +typedef struct ASE_Chunk_OldPalette_Packet_Color ASE_Chunk_OldPalette_Packet_Color; +static_assert(sizeof(ASE_Chunk_OldPalette_Packet_Color) == 3, "ASE_Chunk_OldPalette_Packet_Color is wrong size"); +#endif + +struct PACKED_STRUCT ASE_Chunk_Layer { + ASE_WORD flags; + ASE_WORD layerType; + ASE_WORD childLevel; + ASE_WORD width; + ASE_WORD height; + ASE_WORD blendMode; + ASE_BYTE opacity; + ASE_BYTE ___reserved[3]; + ASE_STRING name; +}; +typedef struct ASE_Chunk_Layer ASE_Chunk_Layer; +static_assert(sizeof(ASE_Chunk_Layer) == 18, "ASE_Chunk_Layer is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Layer_Tileset { + ASE_DWORD tilesetIndex; +}; +typedef struct ASE_Chunk_Layer_Tileset ASE_Chunk_Layer_Tileset; +static_assert(sizeof(ASE_Chunk_Layer_Tileset) == 4, "ASE_Chunk_Layer_Tileset is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Cel { + ASE_WORD layerIndex; + ASE_SHORT positionX; + ASE_SHORT positionY; + ASE_BYTE opacity; + ASE_WORD celType; + ASE_SHORT zIndex; + ASE_BYTE ___reserved[5]; +}; +typedef struct ASE_Chunk_Cel ASE_Chunk_Cel; +static_assert(sizeof(ASE_Chunk_Cel) == 16, "ASE_Chunk_Cel is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Cel_RawImage { + ASE_WORD pixelWidth; + ASE_WORD pixelHeight; + //ASE_BYTE pixelData[]; +}; +typedef struct ASE_Chunk_Cel_RawImage ASE_Chunk_Cel_RawImage; +static_assert(sizeof(ASE_Chunk_Cel_RawImage) == 4, "ASE_Chunk_Cel_RawImage is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Cel_LinkedCel { + ASE_WORD framePosition; +}; +typedef struct ASE_Chunk_Cel_LinkedCel ASE_Chunk_Cel_LinkedCel; +static_assert(sizeof(ASE_Chunk_Cel_LinkedCel) == 2, "ASE_Chunk_Cel_LinkedCel is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Cel_CompressedImage { + ASE_WORD pixelWidth; + ASE_WORD pixelHeight; + //ASE_BYTE pixelData[]; +}; +typedef struct ASE_Chunk_Cel_CompressedImage ASE_Chunk_Cel_CompressedImage; +static_assert(sizeof(ASE_Chunk_Cel_CompressedImage) == 4, "ASE_Chunk_Cel_CompressedImage is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Cel_CompressedTilemap { + ASE_WORD tileWidth; + ASE_WORD tileHeight; + ASE_WORD tileBits; + ASE_DWORD tileIDBitmap; + ASE_DWORD flipXBitmap; + ASE_DWORD flipYBitmap; + ASE_DWORD rotationBitmap; + ASE_BYTE ___reserved[10]; + //ASE_DWORD tileData[]; +}; +typedef struct ASE_Chunk_Cel_CompressedTilemap ASE_Chunk_Cel_CompressedTilemap; +static_assert(sizeof(ASE_Chunk_Cel_CompressedTilemap) == 32, "ASE_Chunk_Cel_CompressedTilemap is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Cel_Extra { + ASE_DWORD flags; + ASE_FIXED positionX; + ASE_FIXED positionY; + ASE_FIXED width; + ASE_FIXED height; + ASE_BYTE ___reserved[16]; +}; +typedef struct ASE_Chunk_Cel_Extra ASE_Chunk_Cel_Extra; +static_assert(sizeof(ASE_Chunk_Cel_Extra) == 36, "ASE_Chunk_Cel_Extra is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_ColorProfile { + ASE_WORD type; + ASE_WORD flags; + ASE_FIXED gamma; + ASE_BYTE ___reserved[8]; +}; +typedef struct ASE_Chunk_ColorProfile ASE_Chunk_ColorProfile; +static_assert(sizeof(ASE_Chunk_ColorProfile) == 16, "ASE_Chunk_ColorProfile is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_ColorProfile_ICC { + ASE_DWORD length; + //ASE_BYTE data[]; +}; +typedef struct ASE_Chunk_ColorProfile_ICC ASE_Chunk_ColorProfile_ICC; +static_assert(sizeof(ASE_Chunk_ColorProfile_ICC) == 4, "ASE_Chunk_ColorProfile_ICC is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_ExternalFiles { + ASE_DWORD entries; + ASE_BYTE ___reserved[8]; +}; +typedef struct ASE_Chunk_ExternalFiles ASE_Chunk_ExternalFiles; +static_assert(sizeof(ASE_Chunk_ExternalFiles) == 12, "ASE_Chunk_ExternalFiles is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_ExternalFiles_Entry { + ASE_DWORD entryID; + ASE_BYTE type; + ASE_BYTE ___reserved[7]; + ASE_STRING externalFilename; +}; +typedef struct ASE_Chunk_ExternalFiles_Entry ASE_Chunk_ExternalFiles_Entry; +static_assert(sizeof(ASE_Chunk_ExternalFiles_Entry) == 14, "ASE_Chunk_ExternalFiles_Entry is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Mask { + ASE_SHORT positionX; + ASE_SHORT positionY; + ASE_WORD width; + ASE_WORD height; + ASE_BYTE ___reserved[8]; + ASE_STRING maskName; +}; +typedef struct ASE_Chunk_Mask ASE_Chunk_Mask; +static_assert(sizeof(ASE_Chunk_Mask) == 18, "ASE_Chunk_Mask is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Tags { + ASE_WORD numTags; + ASE_BYTE ___reserved[8]; +}; +typedef struct ASE_Chunk_Tags ASE_Chunk_Tags; +static_assert(sizeof(ASE_Chunk_Tags) == 10, "ASE_Chunk_Tags is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Tags_Tag { + ASE_WORD fromFrame; + ASE_WORD toFrame; + ASE_BYTE loopDirection; + ASE_WORD repeat; + ASE_BYTE ___reserved[6]; + ASE_BYTE ___tagRBZ[3]; + ASE_BYTE ___extra; + ASE_STRING tagName; +}; +typedef struct ASE_Chunk_Tags_Tag ASE_Chunk_Tags_Tag; +static_assert(sizeof(ASE_Chunk_Tags_Tag) == 19, "ASE_Chunk_Tags_Tag is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Palette { + ASE_DWORD paletteSize; + ASE_DWORD firstColorIdx; + ASE_DWORD lastColorIdx; + ASE_BYTE ___reserved[8]; +}; +typedef struct ASE_Chunk_Palette ASE_Chunk_Palette; +static_assert(sizeof(ASE_Chunk_Palette) == 20, "ASE_Chunk_Palette is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Palette_Entry { + ASE_WORD flags; + ASE_BYTE r; + ASE_BYTE g; + ASE_BYTE b; + ASE_BYTE a; +}; +typedef struct ASE_Chunk_Palette_Entry ASE_Chunk_Palette_Entry; +static_assert(sizeof(ASE_Chunk_Palette_Entry) == 6, "ASE_Chunk_Palette_Entry is wrong size"); + +struct PACKED_STRUCT ASE_Chunk_Palette_Entry_Name { + ASE_STRING name; +}; +typedef struct ASE_Chunk_Palette_Entry_Name ASE_Chunk_Palette_Entry_Name; +static_assert(sizeof(ASE_Chunk_Palette_Entry_Name) == 2, "ASE_Chunk_Palette_Entry_Name is wrong size"); + +void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZResourceID handle = bzResourcesOpenResource("sprite", "assets/sprites/%s.aseprite", identifier); + //PHYSFS_sint64 length = PHYSFS_fileLength(handle); + //void *data = alloca(length); + //PHYSFS_readBytes(handle, data, length); + + ASE_Header header; + bzResourcesReadBytes(handle, &header, sizeof(header)); + + bzAssert(header.magicNumber == 0xA5E0); + + size_t pixelDataSize; + switch (header.depth) { + case 8: + pixelDataSize = sizeof(ASE_PIXEL_INDEXED); + break; + + case 16: + pixelDataSize = sizeof(ASE_PIXEL_GRAYSCALE); + break; + + case 32: + pixelDataSize = sizeof(ASE_PIXEL_RBZA); + break; + + default: + assert(false);//, "Invalid image"); + break; + } + + uint8_t *outData = (uint8_t *)bzMemoryAlloc(arena, header.width * header.height * pixelDataSize); + *widthOut = (size_t)header.width; + *heightOut = (size_t)header.height; + + for (ASE_WORD frame = 0; frame < header.frames; ++frame) { + ASE_Frame frame; + bzResourcesReadBytes(handle, &frame, sizeof(frame)); + + for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) { + size_t chunkStartPosition = bzResourcesTell(handle); + + ASE_Chunk chunk; + bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); + + switch (chunk.type) { + case 0x2005: { + ASE_Chunk_Cel cel; + bzResourcesReadBytes(handle, &cel, sizeof(cel)); + + if (cel.celType == 2) { + bzMemoryArenaPushWatermark(arena); + + ASE_Chunk_Cel_CompressedImage image; + bzResourcesReadBytes(handle, &image, sizeof(image)); + + size_t compressedSize = chunk.chunkSize - sizeof(ASE_Chunk_Cel_CompressedImage) - sizeof(ASE_Chunk); + int8_t *compressedData = bzMemoryAlloc(arena, compressedSize); // If we do this on the stack (alloca) the Playdate will explode! + bzResourcesReadBytes(handle, compressedData, compressedSize); + + size_t imagePixelDataSize = image.pixelWidth * image.pixelHeight * pixelDataSize; + int8_t *imagePixelData = bzMemoryAlloc(arena, imagePixelDataSize); + stbi_zlib_decode_buffer(imagePixelData, imagePixelDataSize, compressedData, compressedSize); + + for (size_t y = 0; y < image.pixelHeight; ++y) { + for (size_t x = 0; x < image.pixelWidth; ++x) { + size_t outX = cel.positionX + x; + size_t outY = cel.positionY + y; + + for (size_t i = 0; i < pixelDataSize; ++i) { + outData[(outY * header.width + outX) * pixelDataSize + i] = imagePixelData[(y * image.pixelWidth + x) * pixelDataSize + i]; + } + } + } + + bzMemoryArenaPopWatermark(arena); + + // FIXME + //bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + } else { + // Skip this chunk... + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + } + + break; + } + + default: + // Skip this chunk... + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + break; + } + } + } + + bzResourcesCloseResource(handle); + + return outData; +} + +size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename) { + BZResourceID handle = bzResourcesOpenResource("palette", "assets/palettes/%s", filename); + + ASE_Header header; + bzResourcesReadBytes(handle, &header, sizeof(header)); + + bzAssert(header.magicNumber == 0xA5E0); + + size_t colorCount = 0; + + for (ASE_WORD frame = 0; frame < header.frames; ++frame) { + ASE_Frame frame; + bzResourcesReadBytes(handle, &frame, sizeof(frame)); + + for (ASE_DWORD chunk = 0; chunk < frame.numChunks; ++chunk) { + size_t chunkStartPosition = bzResourcesTell(handle); + + ASE_Chunk chunk; + bzResourcesReadBytes(handle, &chunk, sizeof(chunk)); + + switch (chunk.type) { + case 0x2019: { + ASE_Chunk_Palette palette; + bzResourcesReadBytes(handle, &palette, sizeof(palette)); + + if (palette.firstColorIdx < maxColors) { + size_t lastIdx = bzMin(palette.lastColorIdx + 1, maxColors); + for (size_t i = palette.firstColorIdx; i < lastIdx; ++i) { + ASE_Chunk_Palette_Entry paletteEntry; + bzResourcesReadBytes(handle, &paletteEntry, sizeof(paletteEntry)); + colorsOut[i] = bzPaletteMakeColor(paletteEntry.r, paletteEntry.g, paletteEntry.b); + } + colorCount = bzMax(colorCount, lastIdx); + } + + // FIXME + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + + break; + } + + default: + // Skip this chunk... + bzResourcesSeek(handle, chunkStartPosition + chunk.chunkSize); + break; + } + } + } + + bzResourcesCloseResource(handle); + + return colorCount; +} \ No newline at end of file diff --git a/src/bz/gfx/aseprite.h b/src/bz/gfx/aseprite.h new file mode 100644 index 0000000..a5d540c --- /dev/null +++ b/src/bz/gfx/aseprite.h @@ -0,0 +1,18 @@ +#ifndef BZ_GFX_ASEPRITE_H +#define BZ_GFX_ASEPRITE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *bzGfxLoadAsepriteImage(BZMemoryArenaID arena, size_t *widthOut, size_t *heightOut, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/aseprite_internal.h b/src/bz/gfx/aseprite_internal.h new file mode 100644 index 0000000..bc69849 --- /dev/null +++ b/src/bz/gfx/aseprite_internal.h @@ -0,0 +1,16 @@ +#ifndef BZ_GFX_ASEPRITE_INTERNAL_H +#define BZ_GFX_ASEPRITE_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern size_t bzGfxLoadAsepritePalette(uint32_t *colorsOut, size_t maxColors, const char *filename); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/draw_queue.c b/src/bz/gfx/draw_queue.c new file mode 100644 index 0000000..a070d60 --- /dev/null +++ b/src/bz/gfx/draw_queue.c @@ -0,0 +1,112 @@ +#include + +#include +#include +#include // memcpy + +static BZMemoryArena instructionArena; + +struct BZDrawQueueInstruction { + //BZRect cullRect; + BZDrawQueueCall drawCall; + void *userParameter; + + uint8_t sortIdx; + struct BZDrawQueueInstruction *sortedNextInstruction; +}; +typedef struct BZDrawQueueInstruction BZDrawQueueInstruction; + +// FIXME, make this better?? Dynamic?? +#define kNumberDrawInstructions 512 +#define kDrawInstructionMemory 32 + +struct BZDrawQueue { + size_t enqueuedInstructions; + BZDrawQueueInstruction instructions[kNumberDrawInstructions]; + uint8_t *instructionMemory; + BZMemoryArena instructionArena; + BZDrawQueueInstruction *sortedFirstInstruction[kNumberDrawInstructions]; + BZDrawQueueInstruction *sortedLastInstruction[kNumberDrawInstructions]; +}; + +#define kInstructionMemorySize kNumberDrawInstructions * kDrawInstructionMemory + +//static size_t numDrawQueues = 0; +//static BZDrawQueue drawQueues[300]; + +BZDrawQueueID bzGfxDrawQueueCreate(BZMemoryArenaID arena, const char *identifierFmt, ...) { +// bzLog("Creating queue size %d", sizeof(BZDrawQueue)); + BZDrawQueueID drawQueue = (BZDrawQueueID)bzMemoryAlloc(arena, sizeof(BZDrawQueue)); + bzAssert(drawQueue != NULL); + drawQueue->instructionMemory = bzMemoryAlloc(arena, kInstructionMemorySize); + bzMemoryArenaSetup(&drawQueue->instructionArena, drawQueue->instructionMemory, kInstructionMemorySize, false); // Don't zero this out because it's just some temp memory stuff + bzGfxDrawQueueClear(drawQueue); + return drawQueue; +// return &drawQueues[numDrawQueues++]; +} + +void bzGfxDrawQueueTeardown(BZDrawQueueID drawQueue) { + +} + +size_t bzGfxDrawQueueEnqueuedInstructionCount(BZDrawQueueID drawQueue) { + return drawQueue->enqueuedInstructions; +} + +void bzGfxDrawQueueClear(BZDrawQueueID drawQueue) { + drawQueue->enqueuedInstructions = 0; + + for (size_t i = 0; i < kNumberDrawInstructions; ++i) { + drawQueue->sortedFirstInstruction[i] = NULL; + drawQueue->sortedLastInstruction[i] = NULL; + } + + bzMemoryArenaReset(&drawQueue->instructionArena); +} + +/*void bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, BZDrawQueueCall drawCall, void *userParameter) { + BZDrawQueueInstruction *instruction = &drawQueue->instructions[drawQueue->enqueuedInstructions++]; + instruction->drawCall = drawCall; + instruction->userParameter = userParameter; +}*/ + + +extern void *bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall, size_t userParameterSize) { + bzAssert(drawQueue->enqueuedInstructions < kNumberDrawInstructions); + + BZDrawQueueInstruction *instruction = &drawQueue->instructions[drawQueue->enqueuedInstructions++]; + instruction->sortedNextInstruction = NULL; + //instruction->cullRect = cullRect; + instruction->drawCall = drawCall; + instruction->userParameter = bzMemoryAlloc(&drawQueue->instructionArena, userParameterSize); + + if (drawQueue->sortedFirstInstruction[sortIdx] == NULL) { + drawQueue->sortedFirstInstruction[sortIdx] = instruction; + drawQueue->sortedLastInstruction[sortIdx] = instruction; + } else { + drawQueue->sortedLastInstruction[sortIdx]->sortedNextInstruction = instruction; + drawQueue->sortedLastInstruction[sortIdx] = instruction; + } + + return instruction->userParameter; +} + +void bzGfxDrawQueueRun(BZDrawQueueID drawQueue) { + for (size_t i = 0; i < kNumberDrawInstructions; ++i) { + bzGfxDrawQueueRunSortBin(drawQueue, i); + } + +// for (size_t i = 0; i < drawQueue->enqueuedInstructions; ++i) { +// //if (true) { // FIXME, cull +// drawQueue->instructions[i].drawCall(drawQueue->instructions[i].userParameter); +// //} +// } +} + +void bzGfxDrawQueueRunSortBin(BZDrawQueueID drawQueue, uint8_t sortIdx) { + BZDrawQueueInstruction *instruction = drawQueue->sortedFirstInstruction[sortIdx]; + while (instruction != NULL) { + instruction->drawCall(instruction->userParameter); + instruction = instruction->sortedNextInstruction; + } +} diff --git a/src/bz/gfx/draw_queue.h b/src/bz/gfx/draw_queue.h new file mode 100644 index 0000000..5880fc7 --- /dev/null +++ b/src/bz/gfx/draw_queue.h @@ -0,0 +1,37 @@ +#ifndef BZ_GFX_DRAW_QUEUE_H +#define BZ_GFX_DRAW_QUEUE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZDrawQueue BZDrawQueue; +typedef BZDrawQueue * BZDrawQueueID; + +typedef void (*BZDrawQueueCall)(void *userParameter); +typedef void (*BZDrawQueueSortFunction)(void); + +extern BZDrawQueueID bzGfxDrawQueueCreate(BZMemoryArenaID arena, const char *identifierFmt, ...); +extern void bzGfxDrawQueueTeardown(BZDrawQueueID drawQueue); + +extern void bzGfxDrawQueueSetSortFunction(BZDrawQueueID drawQueue, BZDrawQueueSortFunction sortFunction); // FIXME, use this for map thing. + +extern size_t bzGfxDrawQueueEnqueuedInstructionCount(BZDrawQueueID drawQueue); + +extern void bzGfxDrawQueueClear(BZDrawQueueID drawQueue); +//extern void bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, BZRect cullRect, BZDrawQueueCall drawCall, void *userParameter); +//extern BZMemoryArenaID bzGfxDrawQueueEnqueueExt(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall); +//extern void bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall, size_t userParameterSize, void *userParameter); +extern void *bzGfxDrawQueueEnqueue(BZDrawQueueID drawQueue, uint8_t sortIdx, BZDrawQueueCall drawCall, size_t userParameterSize); + +extern void bzGfxDrawQueueRun(BZDrawQueueID drawQueue); +extern void bzGfxDrawQueueRunSortBin(BZDrawQueueID drawQueue, uint8_t sortIdx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/drawing.c b/src/bz/gfx/drawing.c new file mode 100644 index 0000000..4fcc6cb --- /dev/null +++ b/src/bz/gfx/drawing.c @@ -0,0 +1,148 @@ +#include + +#include +#include +#include +#include + +#define DRAW_DATA(name, members, ...) \ + struct data##name { members; }; typedef struct data##name data##name; \ + static void dispatch##name(void *userParam) { data##name *data = (data##name *)userParam; name(__VA_ARGS__); } \ + +#define DRAW_DATA_DISPATCH(drawQueue, name, ...) \ + if (drawQueue != NULL) { \ + data##name *data = (data##name *)bzGfxDrawQueueEnqueue(drawQueue, sortIdx, dispatch##name, sizeof(data##name)); \ + *data = (data##name){ __VA_ARGS__ }; \ + } else { \ + name(__VA_ARGS__); \ + } + +DRAW_DATA(bzPSet, float x; float y; int c, data->x, data->y, data->c) +DRAW_DATA(bzCls, uint8_t c, data->c) +DRAW_DATA(bzCamera, float x; float y, data->x, data->y) +DRAW_DATA(bzCirc, int x; int y; int r; int c, data->x, data->y, data->r, data->c) +DRAW_DATA(bzCircFill, int x; int y; int r; int c, data->x, data->y, data->r, data->c) +DRAW_DATA(bzLine, int x0; int y0; int x1; int y1; int c, data->x0, data->y0, data->x1, data->y1, data->c) +DRAW_DATA(bzRect, int x0; int y0; int x1; int y1; int c, data->x0, data->y0, data->x1, data->y1, data->c) +DRAW_DATA(bzRectFill, int x0; int y0; int x1; int y1; int c, data->x0, data->y0, data->x1, data->y1, data->c) +DRAW_DATA(bzTriangle, int x0; int y0; int x1; int y1; int x2; int y2; int c, data->x0, data->y0, data->x1, data->y1, data->x2, data->y2, data->c) +DRAW_DATA(bzSpr, int n; int x; int y, data->n, data->x, data->y) +DRAW_DATA(bzSprExt, int n; int x; int y; int w; int h; bool flipX; bool flipY, data->n, data->x, data->y, data->w, data->h, data->flipX, data->flipY) +DRAW_DATA(bzSSpr, int sx; int sy; int sw; int sh; int dx; int dy, data->sx, data->sy, data->sw, data->sh, data->dx, data->dy) +DRAW_DATA(bzSSprExt, int sx; int sy; int sw; int sh; int dx; int dy; int dw; int dh; bool flipX; bool flipY, data->sx, data->sy, data->sw, data->sh, data->dx, data->dy, data->dw, data->dh, data->flipX, data->flipY) +DRAW_DATA(bzPrint, int x; int y; int c; char string[], data->x, data->y, data->c, data->string) +DRAW_DATA(bzFillP, uint16_t p, data->p) +DRAW_DATA(bzSetPaletteColor, int palette; int colorIdx; int color, data->palette, data->colorIdx, data->color) +DRAW_DATA(bzSetOutputBuffer, int idx, data->idx) +DRAW_DATA(bzGfxMap, BZTilemapID tilemap; int tx; int ty; int x; int y; int mw; int mh; BZIdentifierHash tag, data->tilemap, data->tx, data->ty, data->x, data->y, data->mw, data->mh, data->tag) + +void bzDrawPSet(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y, int c) { + BZVector pos = bzVectorMake(x, y); + bzMatrixTransformVector(&pos, &pos, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzPSet, pos.x, pos.y, c) +} + +void bzDrawCls(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint8_t c) { + DRAW_DATA_DISPATCH(drawQueue, bzCls, c) +} + +void bzDrawCamera(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y) { + DRAW_DATA_DISPATCH(drawQueue, bzCamera, x, y) +} + +void bzDrawCirc(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c) { + BZVector pos = bzVectorMake(x, y); + BZVector radius = bzVectorMake(r, 0); + bzMatrixTransformVector(&pos, &pos, mtx); + bzMatrixScaleRotateVector(&radius, &radius, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzCirc, pos.x, pos.y, bzVectorMagnitude(&radius), c) // FIXME, RADIUS +} + +void bzDrawCircFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c) { + BZVector pos = bzVectorMake(x, y); + BZVector radius = bzVectorMake(r, 0); + bzMatrixTransformVector(&pos, &pos, mtx); + bzMatrixScaleRotateVector(&radius, &radius, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzCircFill, pos.x, pos.y, bzVectorMagnitude(&radius), c) // FIXME, RADIUS +} + +void bzDrawLine(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c) { + BZVector pos0 = bzVectorMake(x0, y0); + BZVector pos1 = bzVectorMake(x1, y1); + bzMatrixTransformVector(&pos0, &pos0, mtx); + bzMatrixTransformVector(&pos1, &pos1, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzLine, pos0.x, pos0.y, pos1.x, pos1.y, c) +} + +void bzDrawRect(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c) { + BZVector pos0 = bzVectorMake(x0, y0); + BZVector pos1 = bzVectorMake(x1, y1); + bzMatrixTransformVector(&pos0, &pos0, mtx); + bzMatrixTransformVector(&pos1, &pos1, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzRect, pos0.x, pos0.y, pos1.x, pos1.y, c) // FIXME, rotation?? +} + +void bzDrawRectFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c) { + BZVector pos0 = bzVectorMake(x0, y0); + BZVector pos1 = bzVectorMake(x1, y1); + bzMatrixTransformVector(&pos0, &pos0, mtx); + bzMatrixTransformVector(&pos1, &pos1, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzRectFill, pos0.x, pos0.y, pos1.x, pos1.y, c) // FIXME, rotation?? +} + +void bzDrawTriangle(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int x2, int y2, int c) { + BZVector pos0 = bzVectorMake(x0, y0); + BZVector pos1 = bzVectorMake(x1, y1); + BZVector pos2 = bzVectorMake(x2, y2); + bzMatrixTransformVector(&pos0, &pos0, mtx); + bzMatrixTransformVector(&pos1, &pos1, mtx); + bzMatrixTransformVector(&pos2, &pos2, mtx); + DRAW_DATA_DISPATCH(drawQueue, bzTriangle, pos0.x, pos0.y, pos1.x, pos1.y, pos2.x, pos2.y, c) +} + +void bzDrawSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y) { + DRAW_DATA_DISPATCH(drawQueue, bzSpr, n, x, y) +} + +void bzDrawSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y, int w, int h, bool flipX, bool flipY) { + DRAW_DATA_DISPATCH(drawQueue, bzSprExt, n, x, y, w, h, flipX, flipY) +} + +void bzDrawSSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy) { + DRAW_DATA_DISPATCH(drawQueue, bzSSpr, sx, sy, sw, sh, dx, dy) +} + +void bzDrawSSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flipX, bool flipY) { + DRAW_DATA_DISPATCH(drawQueue, bzSSprExt, sx, sy, sw, sh, dx, dy, dw, dh, flipX, flipY) +} + +void bzDrawPrint(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int color, const char *text) { + BZVector pos = bzVectorMake(x, y); + bzMatrixTransformVector(&pos, &pos, mtx); + if (drawQueue != NULL) { + size_t dataSize = sizeof(databzPrint) + strlen(text); + databzPrint *data = (databzPrint *)bzGfxDrawQueueEnqueue(drawQueue, sortIdx, dispatchbzPrint, dataSize); + data->x = pos.x; + data->y = pos.y; + data->c = color; + strcpy(data->string, text); + } else { + bzPrint(pos.x, pos.y, color, text); + } +} + +void bzDrawFillP(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint16_t p) { + DRAW_DATA_DISPATCH(drawQueue, bzFillP, p) +} + +void bzDrawSetPaletteColor(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int palette, int colorIdx, int color) { + DRAW_DATA_DISPATCH(drawQueue, bzSetPaletteColor, palette, colorIdx, color) +} + +void bzDrawSetOutputBuffer(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int idx) { + DRAW_DATA_DISPATCH(drawQueue, bzSetOutputBuffer, idx) +} + +void bzDrawMap(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, BZIdentifierHash tag) { + DRAW_DATA_DISPATCH(drawQueue, bzGfxMap, tilemap, tx, ty, x, y, mw, mh, tag) +} diff --git a/src/bz/gfx/drawing.h b/src/bz/gfx/drawing.h new file mode 100644 index 0000000..95a2bb6 --- /dev/null +++ b/src/bz/gfx/drawing.h @@ -0,0 +1,58 @@ +#ifndef BZ_GFX_DRAWING_H +#define BZ_GFX_DRAWING_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//extern void bzDrawClip(BZDrawQueueID drawQueue, uint8_t sortIdx, int x, int y, int w, int h); + +extern void bzDrawPSet(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y, int c); + +//extern bool bzFGet(int n, int f); +//extern void bzFSet(int n, int f, bool v); + +extern void bzDrawCls(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint8_t c); + +extern void bzDrawCamera(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, float x, float y); + +extern void bzDrawCirc(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c); +extern void bzDrawCircFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int r, int c); + +//extern void bzDrawOval(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c); +//extern void bzDrawOvalFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c); + +extern void bzDrawLine(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c); + +extern void bzDrawRect(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c); +extern void bzDrawRectFill(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int c); + +extern void bzDrawTriangle(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x0, int y0, int x1, int y1, int x2, int y2, int c); + +extern void bzDrawSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y); +extern void bzDrawSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int n, int x, int y, int w, int h, bool flipX, bool flipY); + +extern void bzDrawSSpr(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy); +extern void bzDrawSSprExt(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, bool flipX, bool flipY); + +extern void bzDrawPrint(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int x, int y, int color, const char *text); + +extern void bzDrawFillP(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, uint16_t p); + +extern void bzDrawSetPaletteColor(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int palette, int colorIdx, int color); + +extern void bzDrawSetOutputBuffer(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, int idx); + +extern void bzDrawMap(BZDrawQueueID drawQueue, uint8_t sortIdx, const BZMatrix *mtx, BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, BZIdentifierHash tag); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/font.c b/src/bz/gfx/font.c new file mode 100644 index 0000000..fdfada1 --- /dev/null +++ b/src/bz/gfx/font.c @@ -0,0 +1,138 @@ +#include + +#include +#include +#include +#include +#include + +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 . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to >5]); + putchar('\n'); + } + + return 0; +} +*/ + + +void bzGfxRenderFont(BZFontRenderPixelSetFunction pixelSetFunction, int *xPosition, int *yPosition, BZFontID font, int color, const char *text) { + for (const char *c = text; *c != '\0'; ++c) { + BZFontCodepoint *codepoint = &font->codepoints[*c]; + + for (int y = 0, i = 0; y < 32; ++y) { + for (int x = 0; x < 32; ++x, ++i) { + if (codepoint->sprite[i] > 100) { + pixelSetFunction(*xPosition + x + codepoint->x0, *yPosition + font->baseline + y + codepoint->y0, color); + } + } + } + + *xPosition += codepoint->advanceWidth; + } +} diff --git a/src/bz/gfx/font.h b/src/bz/gfx/font.h new file mode 100644 index 0000000..e4a72c0 --- /dev/null +++ b/src/bz/gfx/font.h @@ -0,0 +1,19 @@ +#ifndef BZ_GFX_FONT_H +#define BZ_GFX_FONT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZFont BZFont; +typedef BZFont * BZFontID; + +extern BZFontID bzGfxLoadFont(BZMemoryArenaID arena, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/font_internal.h b/src/bz/gfx/font_internal.h new file mode 100644 index 0000000..cd0b3b4 --- /dev/null +++ b/src/bz/gfx/font_internal.h @@ -0,0 +1,18 @@ +#ifndef BZ_GFX_FONT_INTERNAL_H +#define BZ_GFX_FONT_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void (*BZFontRenderPixelSetFunction)(int, int, int); + +extern void bzGfxRenderFont(BZFontRenderPixelSetFunction pixelSetFunction, int *xPosition, int *yPosition, BZFontID font, int color, const char *text); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/gfx.c b/src/bz/gfx/gfx.c new file mode 100644 index 0000000..0f26e3b --- /dev/null +++ b/src/bz/gfx/gfx.c @@ -0,0 +1,692 @@ +#include + +#include +#include +#include + +#include +#include // 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)= 0 && (y)= 0 && (x)= 0 && (y)= id##deltaY) { \ + if (id##x == id##xEnd) break; \ + id##err += id##deltaY; \ + id##x += id##signX; \ + } \ + if (id##e2 <= id##deltaX) { \ + if (id##y == id##yEnd) break; \ + id##err += id##deltaX; \ + id##y += id##signY; \ + } + +//uint32_t intermediateBuffer[canvasHeight][canvasWidth]; + +#define SS_WIDTH 128 +#define SS_HEIGHT 128 +#define kSpriteSheetStrideShift 7 + +//static uint8_t spritesheet[SS_HEIGHT][SS_WIDTH]; +static uint8_t *spritesheet = NULL; + +static BZFont *currentFont; + +/*uint8_t palettes[16][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, +};*/ + +static size_t gPaletteCount; +static size_t gPaletteColorCount; +static uint8_t *gPalettes; + +//BZRect bzCalculateCullRect() { +// return bzRectMake(cameraX, cameraY, canvasWidth, SS_HEIGHT); +//} + +void bzGfxPreparePalettes(BZMemoryArenaID arena, size_t paletteCount, size_t colorCount) { + gPaletteCount = paletteCount; + gPaletteColorCount = colorCount; + gPalettes = bzMemoryAlloc(arena, gPaletteCount * gPaletteColorCount * sizeof(uint8_t)); + + for (size_t p = 0, i = 0; p < gPaletteCount; ++p) { + for (size_t c = 0; c < gPaletteColorCount; ++c, ++i) { + gPalettes[i] = c; + } + } +} + +void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height) { + canvasWidth = width; + canvasHeight = height; + + bufferStrideShift = __SIZE_WIDTH__ - __builtin_clzl(canvasWidth - 1); + bufferStride = 1 << bufferStrideShift; + + size_t canvasMemorySize = bufferStride * height * sizeof(uint8_t); + drawBuffer = bzMemoryAlloc(arena, canvasMemorySize); + paletteBuffer = bzMemoryAlloc(arena, canvasMemorySize); + maskBuffer = bzMemoryAlloc(arena, canvasMemorySize); + overlayBuffer = bzMemoryAlloc(arena, canvasMemorySize); + bzGfxCompositedBuffer = bzMemoryAlloc(arena, canvasMemorySize); + + bzSetOutputBuffer(0); + + bzLog("Data: %d %d %d %d", canvasWidth, canvasMemorySize, bufferStrideShift, bufferStride); +} + +void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data) { + uint8_t *imageData = (uint8_t *)data; + bzLog("Preparing spritesheet %dx%d", width, height); + + size_t spritesheetMemorySize = width * height * sizeof(uint8_t); + spritesheet = bzMemoryAlloc(arena, spritesheetMemorySize); + memcpy(spritesheet, data, spritesheetMemorySize); +} + +void bzGfxPrepareFont(BZMemoryArenaID arena, void *font) { + currentFont = font; +} + +static bool prepareFrame(int *minXOut, int *minYOut, int *maxXOut, int *maxYOut, int x0, int y0, int x1, int y1, int *clippedLeftX, int *clippedTopY, int *clippedRightX, int *clippedBottomY) { + int xMin = x0, xMax = x1; + int yMin = y0, yMax = y1; + + if (x1 < x0) { + xMin = x1; + xMax = x0; + } + + if (y1 < y0) { + yMin = y1; + yMax = y0; + } + + int xMinOut = xMin/* - cameraX*/, xMaxOut = xMax/* - cameraX*/; + int yMinOut = yMin/* - cameraY*/, yMaxOut = yMax/* - cameraY*/; + + if (xMaxOut < 0 || yMaxOut < 0 || xMinOut >= canvasWidth || yMinOut >= canvasHeight) { + return false; + } + + int safeXMinOut = bzMax(0, xMinOut); + int safeXMaxOut = bzMin(canvasWidth - 1, xMaxOut); + int safeYMinOut = bzMax(0, yMinOut); + int safeYMaxOut = bzMin(canvasHeight - 1, yMaxOut); + + *minXOut = safeXMinOut; + *minYOut = safeYMinOut; + *maxXOut = safeXMaxOut; + *maxYOut = safeYMaxOut; + + if (clippedLeftX != NULL) { + *clippedLeftX = safeXMinOut - xMinOut; + *clippedTopY = safeYMinOut - yMinOut; + + *clippedRightX = xMaxOut - safeXMaxOut; + *clippedBottomY = yMaxOut - safeYMaxOut; + } + + return true; +} + +static bool preparePositionXY(int *xOut, int *yOut, float x, float y) { + //BZVector origin = bzVectorMake(, ); + //bzMatrixTransformVector(&origin, &origin, &globalViewMatrix); + BZVector position = bzVectorMake(x, y); + bzMatrixTransformVector/*bzMatrixTranslateVector*/(&position, &position, &globalViewMatrix); + //bzMatrixTransformVector(&position, &position, &globalViewMatrix); + //bzVectorAdd(&position, &origin, &position); + *xOut = bzFloor(position.x - cameraX); + *yOut = bzFloor(position.y - cameraY); + return true; +} + +static bool prepareSizeXY(int *xOut, int *yOut, float x, float y) { + BZVector size = bzVectorMake(x, y); + bzMatrixScaleRotateVector(&size, &size, &globalViewMatrix); + *xOut = bzFloor(size.x); + if (yOut != NULL) *yOut = bzFloor(size.y); + return size.x > 0 && size.y > 0; +} + +static bool prepareRadius(int *rOut, float r) { + BZVector size = bzVectorMake(r, 0); + bzMatrixScaleRotateVector(&size, &size, &globalViewMatrix); + *rOut = bzFloor(bzAbs(size.x) + bzAbs(size.y)); + return *rOut > 0; +} + +static bool prepareColor(int *colorOut, int color) { + *colorOut = gPalettes[color % gPaletteColorCount]; // FIXME + return true; + /*int c = (color < gPaletteColorCount) ? gPalettes[color] : color; + if (c > 0) { + *colorOut = c & globalBlendColor; + return true; + } else { + return false; + }*/ +} + +void bzPSet(BZCoordinate x, BZCoordinate y, BZPaletteColor c) { + //if (c == 0) return; + + //c = ((c < gPaletteColorCount) ? gPalettes[c] : c); + + prepareColor(&c, c); + + int ox, oy; + preparePositionXY(&ox, &oy, x, y); + if (ox >= 0 && ox < canvasWidth && oy >=0 && oy < canvasHeight) { + bzBufferSet(currentBuffer, ox, oy, c); + } +} + +BZPaletteColor bzSGet(int x, int y) { + bzAssert(spritesheet != NULL); + if (x >= 0 && x < SS_WIDTH && y >=0 && y < SS_HEIGHT) { + return spritesheet[(y << kSpriteSheetStrideShift) + x]; + } else { + return 0; + } +} + +//void bzSSet(int x, int y, int c); + +//bool bzFGet(int n, int f); +//void bzFSet(int n, int f, bool v); + +void bzCls(BZPaletteColor c) { + memset(currentBuffer, c, bufferStride * canvasHeight); + + //memset(&buffer[currentBuffer], c, BUFFER_WIDTH * canvasHeight); + +// for (size_t i = 0; i < ) + +// for (size_t y = 0; y < canvasHeight; ++y) { +// for (size_t x = 0; x < canvasWidth; ++x) { +// bzBufferSet(currentBuffer, x, y, c); +// } +// } + + //bzCamera(0, 0); + bzFillP(0x0000); +} + +void bzGetCamera(BZCoordinate *x, BZCoordinate *y) { + if (x != NULL) *x = cameraX; + if (y != NULL) *y = cameraY; +} + +void bzCamera(BZCoordinate x, BZCoordinate y) { + cameraX = x; + cameraY = y; + cameraXInt = bzFloor(cameraX); + cameraYInt = bzFloor(cameraY); +} + +void bzCirc(BZCoordinate xm, BZCoordinate ym, BZCoordinate r, BZPaletteColor c) { + int oxm, oym, or, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut; + if (preparePositionXY(&oxm, &oym, xm, ym) && prepareRadius(&or, r) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, oxm - or, oym - or, oxm + or, oym + or, NULL, NULL, NULL, NULL)) { + int x = -or; + int y = 0; + int err = 2 - 2 * or; + + do { + int x0 = oxm-x; + int y0 = oym+y; + if (x0 >= 0 && x0 < canvasWidth && y0 >=0 && y0 < canvasHeight) { // FIXME, easier way to offset this... + bzBufferSet(currentBuffer, x0, y0, cOut); + } + + int x1 = oxm-y; + int y1 = oym-x; + if (x1 >= 0 && x1 < canvasWidth && y1 >=0 && y1 < canvasHeight) { // FIXME, easier way to offset this... + bzBufferSet(currentBuffer, x1, y1, cOut); + } + + int x2 = oxm+x; + int y2 = oym-y; + if (x2 >= 0 && x2 < canvasWidth && y2 >=0 && y2 < canvasHeight) { // FIXME, easier way to offset this... + bzBufferSet(currentBuffer, x2, y2, cOut); + } + + int x3 = oxm+y; + int y3 = oym+x; + if (x3 >= 0 && x3 < canvasWidth && y3 >=0 && y3 < canvasHeight) { // FIXME, easier way to offset this... + bzBufferSet(currentBuffer, x3, y3, cOut); + } + + or = err; + + if (or <= y) err += (++y << 1) + 1; + if (or > x || err > y) err += (++x << 1) + 1; + } while (x <= 0); + } +} + +void bzCircFill(BZCoordinate xm, BZCoordinate ym, BZCoordinate r, BZPaletteColor c) { + int oxm, oym, or, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut; + if (preparePositionXY(&oxm, &oym, xm, ym) && prepareRadius(&or, r) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, oxm - or, oym - or, oxm + or, oym + or, NULL, NULL, NULL, NULL)) { + int x = -or; + int y = 0; + int err = 2 - 2 * or; + + do { + int fy1 = oym - bzAbs(y); + int fy2 = oym + bzAbs(y); + int fx = bzMax(oxm - bzAbs(x), 0); + int tx = bzMin(oxm + bzAbs(x), canvasWidth - 1); // FIXME, not as many abs + + bool validFy1 = fy1 >= 0 && fy1 < canvasHeight; + bool validFy2 = fy2 >= 0 && fy2 < canvasHeight; + + if (validFy1 && validFy2) { + for (int px = fx; px <= tx; ++px) { + bzBufferSet(currentBuffer, px, fy1, cOut); // FIXME, speed + bzBufferSet(currentBuffer, px, fy2, cOut); + } + } else if (validFy1) { + for (int px = fx; px <= tx; ++px) { + bzBufferSet(currentBuffer, px, fy1, cOut); + } + } else if (validFy2) { + for (int px = fx; px <= tx; ++px) { + bzBufferSet(currentBuffer, px, fy2, cOut); + } + } + + or = err; + + if (or <= y) err += (++y << 1) + 1; + if (or > x || err > y) err += (++x << 1) + 1; + } while (x <= 0); + } +} + +//void bzOval(int x0, int y0, int x1, int y1, int c); +//void bzOvalFill(int x0, int y0, int x1, int y1, int c); + +void bzLine(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) { + int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom; + if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) { + bresenhamVariables(line_, ox0, oy0, ox1, oy1) + + for (;;) { + if (line_x >= 0 && line_x < canvasWidth && line_y >=0 && line_y < canvasHeight) { // FIXME, easier way to offset this... + bzBufferSet(currentBuffer, line_x, line_y, cOut); + } + + bresenhamStep(line_) + } + } +} + +void bzRect(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) { + int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom; + if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) { + if (clipLeft == 0 && clipRight == 0) { + for (int py = yMinOut; py <= yMaxOut; ++py) { + bzBufferSet(currentBuffer, xMinOut, py, cOut); + bzBufferSet(currentBuffer, xMaxOut, py, cOut); + } + } else if (clipLeft == 0) { + for (int py = yMinOut; py <= yMaxOut; ++py) { + bzBufferSet(currentBuffer, xMinOut, py, cOut); + } + } else if (clipRight == 0) { + for (int py = yMinOut; py <= yMaxOut; ++py) { + bzBufferSet(currentBuffer, xMaxOut, py, cOut); + } + } + + if (clipTop == 0 && clipBottom == 0) { + for (int px = xMinOut; px <= xMaxOut; ++px) { + bzBufferSet(currentBuffer, px, yMinOut, cOut); + bzBufferSet(currentBuffer, px, yMaxOut, cOut); + } + } else if (clipTop == 0) { + for (int px = xMinOut; px <= xMaxOut; ++px) { + bzBufferSet(currentBuffer, px, yMinOut, cOut); + } + } else if (clipBottom == 0) { + for (int px = xMinOut; px <= xMaxOut; ++px) { + bzBufferSet(currentBuffer, px, yMaxOut, cOut); + } + } + } +} + +void bzRectFill(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c) { + int ox0, oy0, ox1, oy1, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom; + if (preparePositionXY(&ox0, &oy0, x0, y0) && preparePositionXY(&ox1, &oy1, x1, y1) && prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, ox0, oy0, ox1, oy1, &clipLeft, &clipTop, &clipRight, &clipBottom)) { + for (int py = yMinOut; py <= yMaxOut; ++py) { + for (int px = xMinOut; px <= xMaxOut; ++px) { + bzBufferSet(currentBuffer, px, py, cOut); + } + } + } +} + +void bzTriangle(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZCoordinate x2, BZCoordinate y2, BZPaletteColor c) { + int ox0, oy0, ox1, oy1, ox2, oy2; + + preparePositionXY(&ox0, &oy0, x0, y0); + preparePositionXY(&ox1, &oy1, x1, y1); + preparePositionXY(&ox2, &oy2, x2, y2); + + if (oy2 < oy1 || (oy2 == oy1 && ox2 < ox1)) { + swap(ox1, ox2); + swap(oy1, oy2); + } + + // after this, x0&y0 are always the smallest + if (oy1 < oy0 || (oy1 == oy0 && ox1 < ox0)) { + swap(ox0, ox1); + swap(oy0, oy1); + } + + // one final comparison to swap for mid values + if (oy2 < oy1 || (oy2 == oy1 && ox2 < ox1)) { + swap(ox1, ox2); + swap(oy1, oy2); + } + + // The above sorts pairs by Y position, but X isn't sorted + int minX = bzMin(bzMin(ox0, ox1), ox2); + int maxX = bzMax(bzMax(ox0, ox1), ox2); + + int xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom; + if (prepareColor(&cOut, c) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, minX, oy0, maxX, oy2, &clipLeft, &clipTop, &clipRight, &clipBottom)) { + bresenhamVariables(triangleLow_, ox0, oy0, ox1, oy1) + bresenhamVariables(triangleMid_, ox1, oy1, ox2, oy2) + bresenhamVariables(triangleHigh_, ox0, oy0, ox2, oy2) + + for (;;) { + int targetY = triangleLow_y; + + int fromX1 = triangleLow_x, fromX2 = triangleLow_x; + while (triangleLow_y == targetY) { + fromX1 = triangleLow_x, fromX2 = triangleLow_x; + bresenhamStep(triangleLow_) + } + + int toX1 = triangleHigh_x, toX2 = triangleHigh_x; + targetY = triangleHigh_y; + while (triangleHigh_y == targetY) { + toX1 = triangleHigh_x, toX2 = triangleHigh_x; + bresenhamStep(triangleHigh_) + } + + if (targetY >=0 && targetY < canvasHeight) { + int fromX, toX; + + if (fromX1 < toX2) { + fromX = bzMin(fromX1, fromX2); + toX = bzMax(toX1, toX2); + } else { + fromX = bzMin(toX1, toX2); + toX = bzMax(fromX1, fromX2); + } + + if (!((fromX < 0 && toX < 0) || (fromX >= canvasWidth && toX >= canvasWidth))) { + fromX = bzMax(0, bzMin(fromX, canvasWidth - 1)); + toX = bzMax(0, bzMin(toX, canvasWidth - 1)); + + for (int x = fromX; x <= toX; ++x) { + bzBufferSet(currentBuffer, x, targetY, cOut); + } + } + } + + if (targetY >= oy1) { + break; + } + } + + for (;;) { + int targetY = triangleMid_y; + + int fromX1 = triangleMid_x, fromX2 = triangleMid_x; + while (triangleMid_y == targetY) { + fromX1 = triangleMid_x, fromX2 = triangleMid_x; + bresenhamStep(triangleMid_) + } + + int toX1 = triangleHigh_x, toX2 = triangleHigh_x; + targetY = triangleHigh_y; + while (triangleHigh_y == targetY) { + toX1 = triangleHigh_x, toX2 = triangleHigh_x; + bresenhamStep(triangleHigh_) + } + + if (targetY >=0 && targetY < canvasHeight) { + int fromX, toX; + + if (fromX1 < toX2) { + fromX = bzMin(fromX1, fromX2); + toX = bzMax(toX1, toX2); + } else { + fromX = bzMin(toX1, toX2); + toX = bzMax(fromX1, fromX2); + } + + if (!((fromX < 0 && toX < 0) || (fromX >= canvasWidth && toX >= canvasWidth))) { + fromX = bzMax(0, bzMin(fromX, canvasWidth - 1)); + toX = bzMax(0, bzMin(toX, canvasWidth - 1)); + + for (int x = fromX; x <= toX; ++x) { + bzBufferSet(currentBuffer, x, targetY, cOut); + } + } + } + + if (targetY >= oy2) { + break; + } + } + } +} + +void bzSpr(size_t n, BZCoordinate x, BZCoordinate y) { + bzSprExt(n, x, y, 1, 1, false, false); +} + +void bzSprExt(size_t n, BZCoordinate x, BZCoordinate y, BZCoordinate w, BZCoordinate h, bool flipX, bool flipY) { + int px = (n % 16) * 8; + int py = (n / 16) * 8; + int pw = w * 8; + int ph = h * 8; + bzSSprExt(px, py, pw, ph, x, y, pw, ph, flipX, flipY); +} + +void bzSSpr(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy) { + bzSSprExt(sx, sy, sw, sh, dx, dy, sw, sh, false, false); +} + +void bzSSprExt(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy, BZCoordinate dw, BZCoordinate dh, bool flipX, bool flipY) { + bzAssert(spritesheet != NULL); + int odx, ody, odw, odh, xMinOut, yMinOut, xMaxOut, yMaxOut, cOut, clipLeft, clipTop, clipRight, clipBottom; + if (preparePositionXY(&odx, &ody, dx, dy) && prepareSizeXY(&odw, &odh, dw, dh) && prepareFrame(&xMinOut, &yMinOut, &xMaxOut, &yMaxOut, odx, ody, odx + odw - 1, ody + odh - 1, &clipLeft, &clipTop, &clipRight, &clipBottom)) { + int osx = bzFloor(sx); + int osy = bzFloor(sy); + int osw = bzFloor(sw); + int osh = bzFloor(sh); + + for (size_t y = 0; y < osh - (clipTop + clipBottom); ++y) { + for (size_t x = 0; x < osw - (clipLeft + clipRight); ++x) { + int color = gPalettes[spritesheet[((clipTop+osy+y) << kSpriteSheetStrideShift) + clipLeft+osx+x]]; + if (color > 0) bzBufferSet(currentBuffer, xMinOut+x, yMinOut+y, color); // FIXME, scaled up and 0 check removal?? + } + } + } + + /*int fromX = 0; + int toX = sw; + int dX = 1; + + if (flipX) { + fromX = toX - 1; + toX = -1; + dX = -1; + } + + int fromY = 0; + int toY = sh; + int dY = 1; + + if (flipY) { + fromY = toY - 1; + toY = -1; + dY = -1; + } + + for (int rY = fromY, wY = dy; rY != toY; rY += dY, ++wY) { + for (int rX = fromX, wX = dx; rX != toX; rX += dX, ++wX) { + bzPSet(wX, wY, bzSGet(sx + rX, sy + rY)); + } + }*/ +} + +void bzFillP(BZPattern p) { + fillPattern = ~p; +} + +static void bzPrintPSet(int x, int y, int c) { + if (x >= 0 && x < canvasWidth && y >=0 && y < canvasHeight) { + //c = ((c < gPaletteColorCount) ? gPalettes[c] : c); + prepareColor(&c, c); + bzBufferSet(currentBuffer, x, y, c); + } +} + +void bzPrint(BZCoordinate x, BZCoordinate y, BZPaletteColor color, const char *text) { + int ox, oy; + preparePositionXY(&ox, &oy, x, y); + bzGfxRenderFont(bzPrintPSet, &ox, &oy, currentFont, (int)color, text); + //if (preparePositionXY(&ox, &oy, x, y)) { + // bzGfxRenderFont(bzPSet, &ox, &oy, currentFont, (int)color, text); + // FIXME, this won't work + //} +} + +void bzSetPaletteColor(size_t palette, BZPaletteColor colorIdx, BZPaletteColor color) { + gPalettes[(palette * gPaletteColorCount) + (colorIdx % gPaletteColorCount)] = color; +} + +void bzSetGlobalViewMatrix(const BZMatrix *mtx) { + bzMatrixCopy(&globalViewMatrix, mtx); +} + +//void bzSetGlobalLocalMatrix(const BZMatrix *mtx) { +// bzMatrixCopy(&globalLocalMatrix, mtx); +//} + +//void bzSetGlobalBlendColor(BZPaletteColor color) { +// globalBlendColor = color; +//} + +void bzSetOutputBuffer(size_t idx) { + bzAssert(idx >= 0 && idx < 4); + + switch (idx) { + case 0: + currentBuffer = drawBuffer; + break; + + case 1: + currentBuffer = paletteBuffer; + break; + + case 2: + currentBuffer = maskBuffer; + break; + + case 3: + currentBuffer = overlayBuffer; + break; + + default: // FIXME + bzError("invalid output buffer"); + break; + } +} + +void bzGfxComposite() { + bzPerfTimerStart("game.blit.composite"); + + uint8_t *drawBufferReadHead = drawBuffer; + uint8_t *paletteBufferReadHead = paletteBuffer; + uint8_t *maskBufferReadHead = maskBuffer; + uint8_t *overlayBufferReadHead = overlayBuffer; + uint8_t *outputBufferWriteHead = bzGfxCompositedBuffer; + for (size_t i = 0, end = bufferStride * canvasHeight; i < end; ++i) { + //*outputBufferWriteHead = (palettes[*paletteBufferReadHead + 1][*drawBufferReadHead] & *maskBufferReadHead) | *overlayBufferReadHead; + *outputBufferWriteHead = *overlayBufferReadHead > 0 ? gPalettes[*overlayBufferReadHead] : gPalettes[(*paletteBufferReadHead + 1) *gPaletteColorCount + *drawBufferReadHead]; + + ++drawBufferReadHead; + ++paletteBufferReadHead; + ++maskBufferReadHead; + ++overlayBufferReadHead; + ++outputBufferWriteHead; + } + + bzPerfTimerStop("game.blit.composite"); +} diff --git a/src/bz/gfx/gfx.h b/src/bz/gfx/gfx.h new file mode 100644 index 0000000..770997c --- /dev/null +++ b/src/bz/gfx/gfx.h @@ -0,0 +1,77 @@ +#ifndef BZ_GFX_GFX_H +#define BZ_GFX_GFX_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//typedef struct BZCanvas BZCanvas; +//typedef BZCanvas * BZCanvasID; + +//static const int kBZGfxCanvasWidth = 256; +//static const int kBZGfxCanvasHeight = 128; + +extern BZRenderPassID bzGFXRenderPass; + +typedef float BZCoordinate; +typedef uint8_t BZPaletteColor; +typedef uint16_t BZPattern; + +//extern BZRect bzCalculateCullRect(void); + +//extern void bzClip(int x, int y, int w, int h); + +extern void bzPSet(BZCoordinate x, BZCoordinate y, BZPaletteColor c); + +extern BZPaletteColor bzSGet(int x, int y); +//extern void bzSSet(int x, int y, int c); + +//extern bool bzFGet(int n, int f); +//extern void bzFSet(int n, int f, bool v); + +extern void bzCls(BZPaletteColor c); + +extern void bzGetCamera(BZCoordinate *x, BZCoordinate *y); +extern void bzCamera(BZCoordinate x, BZCoordinate y); + +extern void bzCirc(BZCoordinate x, BZCoordinate y, BZCoordinate r, BZPaletteColor c); +extern void bzCircFill(BZCoordinate x, BZCoordinate y, BZCoordinate r, BZPaletteColor c); + +//extern void bzOval(int x0, int y0, int x1, int y1, int c); +//extern void bzOvalFill(int x0, int y0, int x1, int y1, int c); + +extern void bzLine(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c); + +extern void bzRect(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c); +extern void bzRectFill(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZPaletteColor c); + +extern void bzTriangle(BZCoordinate x0, BZCoordinate y0, BZCoordinate x1, BZCoordinate y1, BZCoordinate x2, BZCoordinate y2, BZPaletteColor c); + +extern void bzSpr(size_t n, BZCoordinate x, BZCoordinate y); +extern void bzSprExt(size_t n, BZCoordinate x, BZCoordinate y, BZCoordinate w, BZCoordinate h, bool flipX, bool flipY); + +extern void bzSSpr(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy); +extern void bzSSprExt(BZCoordinate sx, BZCoordinate sy, BZCoordinate sw, BZCoordinate sh, BZCoordinate dx, BZCoordinate dy, BZCoordinate dw, BZCoordinate dh, bool flipX, bool flipY); + +extern void bzPrint(BZCoordinate x, BZCoordinate y, BZPaletteColor color, const char *text); + +extern void bzFillP(BZPattern p); + +extern void bzSetPaletteColor(size_t palette, BZPaletteColor colorIdx, BZPaletteColor color); + +extern void bzSetGlobalViewMatrix(const BZMatrix *mtx); +//extern void bzSetGlobalLocalMatrix(const BZMatrix *mtx); +//extern void bzSetGlobalBlendColor(BZPaletteColor color); + +extern void bzSetOutputBuffer(size_t idx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/gfx_internal.h b/src/bz/gfx/gfx_internal.h new file mode 100644 index 0000000..c03c73a --- /dev/null +++ b/src/bz/gfx/gfx_internal.h @@ -0,0 +1,28 @@ +#ifndef BZ_GFX_GFX_INTERNAL_H +#define BZ_GFX_GFX_INTERNAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzGfxPreparePalettes(BZMemoryArenaID arena, size_t paletteCount, size_t colorCount); +extern void bzGfxPrepareCanvasBuffer(BZMemoryArenaID arena, size_t width, size_t height); + +extern void bzGfxPrepareSpritesheet(BZMemoryArenaID arena, size_t width, size_t height, void *data); +extern void bzGfxPrepareFont(BZMemoryArenaID arena, void *font); + +extern void bzGfxComposite(void); + +extern uint8_t *bzGfxCompositedBuffer; +extern size_t bufferStride; // FIXME, name +extern size_t bufferStrideShift; // FIXME, name + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/particle_drawing.c b/src/bz/gfx/particle_drawing.c new file mode 100644 index 0000000..d02a297 --- /dev/null +++ b/src/bz/gfx/particle_drawing.c @@ -0,0 +1,80 @@ +#include + +#include +#include + +//static void drawBasicParticle(size_t particleCount, float ticks, float spawnTick[], float lifetimes[], float positionX[], float positionY[], float sizes[], float colors[], float alpha[]); + +void bzGfxDrawParticles(BZParticleSimulationID simulation, BZDrawQueueID drawQueue) { + BZMatrix m; + + for (size_t i = 0; i < simulation->maxParticles; ++i) { + float particleStartTicks = simulation->spawnTime[i]; + float lifetime = simulation->lifetime[i]; + if (particleStartTicks + lifetime >= simulation->time) { + if (simulation->alpha[i] < 0.25f) { + bzFillP(0); + } else if (simulation->alpha[i] < 0.50f) { + bzFillP(23130);//bzFillP(23130); + } else if (simulation->alpha[i] < 0.75f) { + bzFillP(32125);//bzFillP(32125); + } else { + bzFillP(32735);//bzFillP(0xFF); // FIXME + } + + bzSetPaletteColor(0, 0, simulation->color[i]); + //bzSetGlobalBlendColor(simulation->color[i]); + + bzMatrixSRT(&m, simulation->positionX[i], simulation->positionY[i], simulation->angle[i], simulation->size[i], simulation->size[i]); + bzSetGlobalViewMatrix(&m); + //bzMatrixTRS(&m, 0, 0, simulation->angle[i], simulation->size[i], simulation->size[i]); + //bzSetGlobalLocalMatrix(&m); + + if (drawQueue != NULL) { + bzGfxDrawQueueRun(drawQueue); + } else { + bzCircFill(0, 0, 1, 0); + } + } + } + bzFillP(0); + + bzMatrixIdentity(&m); + bzSetGlobalViewMatrix(&m); + //bzSetGlobalLocalMatrix(&m); + bzSetPaletteColor(0, 0, 0); + //bzSetGlobalBlendColor(0xFF); + + +//void bzGfxDrawParticles(BZParticleSimulationID simulation) { + + + // FIXME, allow registering draw lists for particles + +// BZParticleSimulationDrawFunction drawFunction = drawBasicParticle; // FIXME, allow register others +// drawFunction(simulation->maxParticles, simulation->time, simulation->spawnTime, simulation->lifetime, simulation->positionX, simulation->positionY, simulation->size, simulation->color, simulation->alpha); +} + +/*static void drawBasicParticle(size_t particleCount, float time, float spawnTime[], float lifetimes[], float positionX[], float positionY[], float sizes[], float colors[], float alpha[]) { + for (size_t i = 0; i < particleCount; ++i) { + float particleStartTicks = spawnTime[i]; + float lifetime = lifetimes[i]; + if (particleStartTicks + lifetime >= time) { + if (alpha[i] < 0.25f) { + bzFillP(0); + } else if (alpha[i] < 0.50f) { + bzFillP(23130);//bzFillP(23130); + } else if (alpha[i] < 0.75f) { + bzFillP(32125);//bzFillP(32125); + } else { + bzFillP(32735);//bzFillP(0xFF); // FIXME + } + + bzGfxDrawQueueRun(BZDrawQueueID drawQueue); + //bzCircFill(positionX[i], positionY[i], sizes[i], colors[i]); + } + } + bzFillP(0); +}*/ + +//nil,32800.99988,▒,░,32735.99988 \ No newline at end of file diff --git a/src/bz/gfx/particle_drawing.h b/src/bz/gfx/particle_drawing.h new file mode 100644 index 0000000..aef8576 --- /dev/null +++ b/src/bz/gfx/particle_drawing.h @@ -0,0 +1,24 @@ +#ifndef BZ_GFX_PARTICLE_DRAWING_H +#define BZ_GFX_PARTICLE_DRAWING_H + +#include +#include +#include +#include +//#include + +#ifdef __cplusplus +extern "C" { +#endif + +//typedef void (*BZParticleSimulationDrawFunction)(size_t particleCount, float ticks, float spawnTick[], float lifetimes[], float positionsX[], float positionsY[], float sizes[], float colors[], float alpha[]); + +//extern void bzGfxRegisterParticleDrawFunction(BZParticleSimulationDrawFunction function, void *userParameter); + +extern void bzGfxDrawParticles(BZParticleSimulationID simulation, BZDrawQueueID drawQueue); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/tilemap.c b/src/bz/gfx/tilemap.c new file mode 100644 index 0000000..7dac115 --- /dev/null +++ b/src/bz/gfx/tilemap.c @@ -0,0 +1,61 @@ +#include + +#include +#include +#include + +void bzGfxMap(BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, uint32_t mask) { + size_t mapFromX = bzMax(0, tx); + size_t mapToX = bzMin(tilemap->width, tx + mw); + + size_t mapFromY = bzMax(0, ty); + size_t mapToY = bzMin(tilemap->height, ty + mh); + + size_t tilesX = mapToX - mapFromX; + size_t tilesY = mapToY - mapFromY; + + for (size_t mty = 0; mty < tilesY; ++mty) { + for (size_t mtx = 0; mtx < tilesX; ++mtx) { + size_t mx = mapFromX + mtx; + size_t my = mapFromY + mty; + + size_t idx = my * tilemap->width + mx; + BZTilemapTile *tile = &tilemap->tiles[idx]; + BZTilemapTileDefinition *tileDefinition = &tilemap->tileDefinitions[tile->tileDefinitionIdx]; + + //uint8_t tileIdx = tile->spriteIdx; + //if (tileIdx == 0) { + // tileIdx = tileDefinition->spriteIdx; + //} + + if (mask == 0 || (tileDefinition->tagsFlag & mask) > 0) { + uint8_t spriteIdx = bzGameGetTilemapSprite(tilemap, mx, my); + + //if (tileIdx > 0) { + //Tile *tile = &tiles[tileIdx]; + + //bool flagsSet = ignoreMask; + + //if (flagsSet == false) { + // uint8_t flags = tile->flags;// | mapTile->flags; + // flagsSet = (flags & userFlagMask) == userFlagValues; + //} + + //if (flagsSet) { + /*if (mapTile->replaceSprite) { + spriteIdx = mapTile->spriteIdx; + } else if (tile->replaceSprite) { + spriteIdx = tile->spriteIdx; + }*/ + + if (spriteIdx > 0) { + int height = bzMax(tileDefinition->height, 1); + for (int i = 0; i < height; ++i) { + bzSpr(spriteIdx - i * 16, x + mtx * 8, y + mty * 8 - i * 8); // Fixme, just sspr it. + } + } + //} + } + } + } +} \ No newline at end of file diff --git a/src/bz/gfx/tilemap.h b/src/bz/gfx/tilemap.h new file mode 100644 index 0000000..e70eac3 --- /dev/null +++ b/src/bz/gfx/tilemap.h @@ -0,0 +1,18 @@ +#ifndef BZ_GFX_TILEMAP_H +#define BZ_GFX_TILEMAP_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzGfxMap(BZTilemapID tilemap, int tx, int ty, int x, int y, int mw, int mh, uint32_t mask); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/gfx/tilemap_internal.h b/src/bz/gfx/tilemap_internal.h new file mode 100644 index 0000000..d5f2e55 --- /dev/null +++ b/src/bz/gfx/tilemap_internal.h @@ -0,0 +1,18 @@ +#ifndef BZ_GFX_TILEMAP_INTERNAL_H +#define BZ_GFX_TILEMAP_INTERNAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//extern void bzGfxPrepareTilemap(BZMemoryArenaID arena, BZTilemapID tilemap); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/impl.c b/src/bz/impl.c new file mode 100644 index 0000000..674883a --- /dev/null +++ b/src/bz/impl.c @@ -0,0 +1,12 @@ +// This file is exclusively for single header implementations + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_NO_STDIO +#define STBI_ONLY_PNG // STBI_ONLY_ZLIB +#include + +#define STB_SPRINTF_IMPLEMENTATION +#include + +#define STB_TRUETYPE_IMPLEMENTATION +#include diff --git a/src/bz/input/input.c b/src/bz/input/input.c new file mode 100644 index 0000000..e69de29 diff --git a/src/bz/input/input.h b/src/bz/input/input.h new file mode 100644 index 0000000..169578c --- /dev/null +++ b/src/bz/input/input.h @@ -0,0 +1,25 @@ +#ifndef BZ_INPUT_INPUT_H +#define BZ_INPUT_INPUT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzInputProcessFrame(void); + +extern bool bzInputGetBooleanValue(BZInputID inputID); +extern int bzInputGetIntegerValue(BZInputID inputID); +extern float bzInputGetFloatValue(BZInputID inputID); + +// FIXME, remove these +extern bool bzInputBtn(int id); +extern bool bzInputBtnP(int id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/input/input_id.h b/src/bz/input/input_id.h new file mode 100644 index 0000000..dd9f958 --- /dev/null +++ b/src/bz/input/input_id.h @@ -0,0 +1,15 @@ +#ifndef BZ_INPUT_INPUT_ID_H +#define BZ_INPUT_INPUT_ID_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZInput BZInput; +typedef const BZInput * BZInputID; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/input/input_id_internal.h b/src/bz/input/input_id_internal.h new file mode 100644 index 0000000..a0feb17 --- /dev/null +++ b/src/bz/input/input_id_internal.h @@ -0,0 +1,18 @@ +#ifndef BZ_INPUT_INPUT_ID_INTERNAL_H +#define BZ_INPUT_INPUT_ID_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZInput { + unsigned int id; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/input/input_internal.h b/src/bz/input/input_internal.h new file mode 100644 index 0000000..181c3a8 --- /dev/null +++ b/src/bz/input/input_internal.h @@ -0,0 +1,17 @@ +#ifndef BZ_INPUT_INPUT_INTERNAL_H +#define BZ_INPUT_INPUT_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzInputInit(void); +extern void bzInputTeardown(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/input/platform.h b/src/bz/input/platform.h new file mode 100644 index 0000000..315be46 --- /dev/null +++ b/src/bz/input/platform.h @@ -0,0 +1,16 @@ +#ifndef BZ_INPUT_PLATFORM_H +#define BZ_INPUT_PLATFORM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern BZInputID bzQuitInputID; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/math/math.c b/src/bz/math/math.c new file mode 100644 index 0000000..f00ad2c --- /dev/null +++ b/src/bz/math/math.c @@ -0,0 +1,51 @@ +#include + +#include + +float bzSine(float a) { + return sinf(bzLerp(0.0f, 2.0f * M_PI, fmodf(a, 1.0f))); +} + +float bzCosine(float a) { + return cosf(bzLerp(0.0f, 2.0f * M_PI, fmodf(a, 1.0f))); +} + +extern float bzInverseSine(float a) { + return bzUnlerp(0.0f, 2.0f * M_PI, fmodf(asinf(a), 2.0f * M_PI)); +} + +extern float bzInverseCosine(float a) { + return bzUnlerp(0.0f, 2.0f * M_PI, fmodf(acosf(a), 2.0f * M_PI)); +} + +float bzSqrt(float a) { + return sqrtf(a); +} + +float bzAtan2(float x, float y) { + float a = atan2f(y, x); + float ca = bzUnlerp(0.0f, 2.0f * M_PI, a); + return ca; +} + +float bzAngleDelta(float from, float to) { + float delta = fmodf(((to - from) + 2), 1); + if (delta >= 0.5f) { + delta = -(1.0f - delta); + } + return delta; +} + +float bzDistance(float x1, float y1, float x2, float y2) { + return sqrtf(bzDistanceSquared(x1, y1, x2, y2)); +} + +float bzDistanceSquared(float x1, float y1, float x2, float y2) { + float dx = x2 - x1; + float dy = y2 - y1; + return dx * dx + dy * dy; +} + +bool bzDistanceCheck(float x1, float y1, float x2, float y2, float distance) { + return bzDistanceSquared(x1, y1, x2, y2) <= distance * distance; +} diff --git a/src/bz/math/math.h b/src/bz/math/math.h new file mode 100644 index 0000000..f1ebd95 --- /dev/null +++ b/src/bz/math/math.h @@ -0,0 +1,49 @@ +#ifndef BZ_MATH_MATH_H +#define BZ_MATH_MATH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define bzAbs(a) ((a) < 0.0f ? -(a) : (a)) + +#define bzMin(a, b) ((a) < (b) ? (a) : (b)) +#define bzMax(a, b) ((a) > (b) ? (a) : (b)) +#define bzMid(a, b, c) (bzMin(bzMin(bzMax((a), (b)), bzMax((b), (c))), bzMax((a), (c)))) + +#define bzMMin(a, b) (bzAbs(a) < bzAbs(b) ? (a) : (b)) +#define bzMMax(a, b) (bzAbs(a) > bzAbs(b) ? (a) : (b)) + +#define bzClamp(a, b, v) (bzMin(bzMax(v, bzMin((a), (b))), bzMax((a), (b)))) + +#define bzSgn(v) (bzAbs(v) < 0.001f ? 0.0f : ((v) >= 0.0f ? 1.0f : -1.0f)) + +#define bzLerp(a, b, t) ((a) * (1 - (t)) + (b) * (t)) +#define bzUnlerp(a, b, v) (((v) - (a)) / ((b) - (a))) + +#define bzFloor(v) ((float)((int)(v))) +#define bzRound(v) (bzFloor(v + bzSgn(v) * 0.5f)) +#define bzCeil(v) (bzFloor(v) + (bzFloor(v) < (v))) + +extern float bzSine(float a); +extern float bzCosine(float a); + +extern float bzInverseSine(float a); +extern float bzInverseCosine(float a); + +extern float bzSqrt(float a); + +extern float bzAtan2(float x, float y); // To match PICO-8 +extern float bzAngleDelta(float from, float to); + +extern float bzDistance(float x1, float y1, float x2, float y2); +extern float bzDistanceSquared(float x1, float y1, float x2, float y2); +extern bool bzDistanceCheck(float x1, float y1, float x2, float y2, float distance); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/math/matrix.c b/src/bz/math/matrix.c new file mode 100644 index 0000000..0450e7b --- /dev/null +++ b/src/bz/math/matrix.c @@ -0,0 +1,130 @@ +#include + +#include + +void bzMatrixSet(BZMatrix *matrixOut, float a, float b, float c, float d, float x, float y) { + matrixOut->a = a; + matrixOut->b = b; + matrixOut->c = c; + matrixOut->d = d; + matrixOut->x = x; + matrixOut->y = y; +} + +void bzMatrixCopy(BZMatrix *matrixOut, const BZMatrix *matrix) { + float a = matrix->a; + float b = matrix->b; + float c = matrix->c; + float d = matrix->d; + float x = matrix->x; + float y = matrix->y; + bzMatrixSet(matrixOut, a, b, c, d, x, y); +} + +void bzMatrixInverse(BZMatrix *matrixOut, const BZMatrix *matrix) { + float det = 1.0f / (matrix->a * matrix->d - matrix->b * matrix->c); + float a = matrix->d * det; + float b = -(matrix->b) * det; + float c = -(matrix->c) * det; + float d = matrix->a * det; + float x = (matrix->b * matrix->y - matrix->d * matrix->x) * det; + float y = -(matrix->a * matrix->y - matrix->c * matrix->x) * det; + bzMatrixSet(matrixOut, a, b, c, d, x, y); +} + +void bzMatrixMultiply(BZMatrix *matrixOut, const BZMatrix *matrix1, const BZMatrix *matrix2) { + float a = matrix1->a * matrix2->a + matrix1->b * matrix2->c; + float b = matrix1->a * matrix2->b + matrix1->b * matrix2->d; + float c = matrix1->c * matrix2->a + matrix1->d * matrix2->c; + float d = matrix1->c * matrix2->b + matrix1->d * matrix2->d; + float x = matrix1->a * matrix2->x + matrix1->b * matrix2->y + matrix1->x; + float y = matrix1->c * matrix2->x + matrix1->d * matrix2->y + matrix1->y; + bzMatrixSet(matrixOut, a, b, c, d, x, y); +} + +void bzMatrixIdentity(BZMatrix *matrixOut) { + bzMatrixSet(matrixOut, 1, 0, 0, 1, 0, 0); +} + +void bzMatrixRotation(BZMatrix *matrixOut, float angle) { + float ct = bzCosine(angle); + float st = bzSine(angle); + bzMatrixSet(matrixOut, ct, -st, st, ct, 0, 0); +} + +void bzMatrixTranslation(BZMatrix *matrixOut, float translateX, float translateY) { + bzMatrixSet(matrixOut, 1, 0, 0, 1, translateX, translateY); +} + +void bzMatrixScale(BZMatrix *matrixOut, float scaleX, float scaleY) { + bzMatrixSet(matrixOut, scaleX, 0, 0, scaleY, 0, 0); +} + +/*void bzMatrixTRS(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY) { + BZMatrix t, r, s; + bzMatrixTranslation(&t, translateX, translateY); + bzMatrixRotation(&r, angle); + bzMatrixScale(&s, scaleX, scaleY); + bzMatrixMultiply(&r, &r, &s); + bzMatrixMultiply(&t, &t, &r); + bzMatrixCopy(matrixOut, &t); + //float ct = bzCosine(angle); + //float st = bzSine(angle); + //bzMatrixSet(matrixOut, scaleX * ct, scaleX * -st, scaleY * st, scaleY * ct, translateX, translateY); +}*/ + +void bzMatrixSRT(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY) { + float ct = bzCosine(angle); + float st = bzSine(angle); + bzMatrixSet(matrixOut, scaleX * ct, scaleX * -st, scaleY * st, scaleY * ct, translateX, translateY); + +/* BZMatrix s, r, t; + bzMatrixScale(&s, scaleX, scaleY); + bzMatrixRotation(&r, angle); + bzMatrixTranslation(&t, translateX, translateY); + bzMatrixMultiply(&r, &r, &s); + bzMatrixMultiply(&t, &t, &r); + bzMatrixCopy(matrixOut, &t);*/ +// bzMatrixMultiply(&r, &r, &t); +// bzMatrixMultiply(&s, &s, &r); +// bzMatrixCopy(matrixOut, &s); +// float ct = bzCosine(angle); +// float st = bzSine(angle); +// bzMatrixSet(matrixOut, scaleX * ct, scaleX * -st, scaleY * st, scaleY * ct, translateX, translateY); +} + +void bzMatrixShear(BZMatrix *matrixOut, float shearX, float shearY) { + bzMatrixSet(matrixOut, 1, shearX, shearY, 1, 0, 0); +} + +void bzMatrixOrtho(BZMatrix *matrixOut, float left, float top, float right, float bottom) { + float a = 2.0f / (right - left); + float d = 2.0f / (top - bottom); + float x = -(right + left) / (right - left); + float y = -(top + bottom) / (top - bottom); + bzMatrixSet(matrixOut, a, 0, 0, d, x, y); +} + +void bzMatrixScaleVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) { + float x = vector->x * bzSqrt(matrix->a * matrix->a + matrix->b * matrix->b); + float y = vector->y * bzSqrt(matrix->c * matrix->c + matrix->d * matrix->d); + bzVectorSet(vectorOut, x, y); +} + +void bzMatrixScaleRotateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) { + float x = (vector->x * matrix->a) + (vector->y * matrix->b); + float y = (vector->x * matrix->c) + (vector->y * matrix->d); + bzVectorSet(vectorOut, x, y); +} + +void bzMatrixTranslateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) { + float x = vector->x + matrix->x; + float y = vector->y + matrix->y; + bzVectorSet(vectorOut, x, y); +} + +void bzMatrixTransformVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix) { + float x = (vector->x * matrix->a) + (vector->y * matrix->b) + matrix->x; + float y = (vector->x * matrix->c) + (vector->y * matrix->d) + matrix->y; + bzVectorSet(vectorOut, x, y); +} diff --git a/src/bz/math/matrix.h b/src/bz/math/matrix.h new file mode 100644 index 0000000..b49b6e5 --- /dev/null +++ b/src/bz/math/matrix.h @@ -0,0 +1,45 @@ +#ifndef BZ_MATH_MATRIX_H +#define BZ_MATH_MATRIX_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZMatrix { + float a; + float b; + float c; + float d; + float x; + float y; +}; +typedef struct BZMatrix BZMatrix; + + +extern void bzMatrixSet(BZMatrix *matrixOut, float a, float b, float c, float d, float x, float y); +extern void bzMatrixCopy(BZMatrix *matrixOut, const BZMatrix *matrix); + +extern void bzMatrixInverse(BZMatrix *matrixOut, const BZMatrix *matrix); +extern void bzMatrixMultiply(BZMatrix *matrixOut, const BZMatrix *matrix1, const BZMatrix *matrix2); + +extern void bzMatrixIdentity(BZMatrix *matrixOut); +extern void bzMatrixRotation(BZMatrix *matrixOut, float angle); +extern void bzMatrixTranslation(BZMatrix *matrixOut, float translateX, float translateY); +extern void bzMatrixScale(BZMatrix *matrixOut, float scaleX, float scaleY); +//extern void bzMatrixTRS(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY); +extern void bzMatrixSRT(BZMatrix *matrixOut, float translateX, float translateY, float angle, float scaleX, float scaleY); +extern void bzMatrixShear(BZMatrix *matrixOut, float shearX, float shearY); +extern void bzMatrixOrtho(BZMatrix *matrixOut, float left, float top, float right, float bottom); + +//extern void bzMatrixScaleVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix); // NB: Expensive +extern void bzMatrixTranslateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix); +extern void bzMatrixScaleRotateVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix); +extern void bzMatrixTransformVector(BZVector *vectorOut, const BZVector *vector, const BZMatrix *matrix); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/math/random.c b/src/bz/math/random.c new file mode 100644 index 0000000..30f6e6c --- /dev/null +++ b/src/bz/math/random.c @@ -0,0 +1,43 @@ +#include + +#include +#include +#include + +float bzRandomFloat(float max) { + uint32_t v = pcg32_random() % 1000; + float value = (float)v / 1000.0f; + return value * max; + //return ((float)pcg32_random() / (float)UINTMAX_MAX);// * max; +} + +float bzRandomFloatRange(float v1, float v2) { + if (v1 == v2) { + return v1; + } else { + float min = bzMin(v1, v2); + float max = bzMax(v1, v2); + float v = bzRandomFloat(bzAbs(max - min)) * bzSgn(max - min); + return min + v; + } +} + +uint32_t bzRandomInteger(uint32_t max) { + return pcg32_boundedrand(max); +} + +int32_t bzRandomIntegerRange(int32_t v1, int32_t v2) { + if (v1 == v2) { + return v1; + } else { + int32_t min = bzMin(v1, v2); + int32_t max = bzMax(v1, v2); + int32_t v = bzRandomInteger(bzAbs(max - min)) * bzSgn(max - min); + return min + v; + } +} + +void *bzRandomArrayValue(size_t count, size_t arrayTypeSize, void *array) { + uint32_t idx = bzRandomInteger(count); + return array + idx * arrayTypeSize; +} diff --git a/src/bz/math/random.h b/src/bz/math/random.h new file mode 100644 index 0000000..4a3df98 --- /dev/null +++ b/src/bz/math/random.h @@ -0,0 +1,23 @@ +#ifndef BZ_MATH_RANDOM_H +#define BZ_MATH_RANDOM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern float bzRandomFloat(float max); +extern float bzRandomFloatRange(float v1, float v2); +extern uint32_t bzRandomInteger(uint32_t max); +extern int32_t bzRandomIntegerRange(int32_t v1, int32_t v2); +extern void *bzRandomArrayValue(size_t count, size_t arrayTypeSize, void *array); +//extern void *bzRandomArrayValue(pcg32_random_t *rng, size_t count, size_t arrayTypeSize, void *array); + +#define bzRandom(T, ...) (*((T *)bzRandomArrayValue(sizeof((T[]){__VA_ARGS__})/sizeof(T), sizeof(T), (T[]){__VA_ARGS__}))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/math/vector.c b/src/bz/math/vector.c new file mode 100644 index 0000000..665acca --- /dev/null +++ b/src/bz/math/vector.c @@ -0,0 +1,79 @@ +#include + +#include + +void bzVectorCopy(BZVector *vectorOut, const BZVector *vector) { + float x = vector->x; + float y = vector->y; + bzVectorSet(vectorOut, x, y); +} + +void bzVectorMakeAngle(BZVector *vectorOut, float angle) { + float x = bzCosine(angle); + float y = bzSine(angle); + bzVectorSet(vectorOut, x, y); + +} + +void bzVectorInverse(BZVector *vectorOut, const BZVector *vector) { + float x = -(vector->x); + float y = -(vector->y); + bzVectorSet(vectorOut, x, y); +} + +void bzVectorTangent(BZVector *vectorOut, const BZVector *vector) { + float x = -(vector->y); + float y = vector->x; + bzVectorSet(vectorOut, x, y); +} + +void bzVectorAdd(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2) { + float x = vector1->x + vector2->x; + float y = vector1->y + vector2->y; + bzVectorSet(vectorOut, x, y); +} + +void bzVectorSubtract(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2) { + float x = vector1->x - vector2->x; + float y = vector1->y - vector2->y; + bzVectorSet(vectorOut, x, y); +} + +void bzVectorScale(BZVector *vectorOut, const BZVector *vector, float scale) { + float x = vector->x * scale; + float y = vector->y * scale; + bzVectorSet(vectorOut, x, y); +} + +void bzVectorNormalized(BZVector *vectorOut, const BZVector *vector) { + float magnitude = bzVectorMagnitude(vector); + float x = vector->x / magnitude; + float y = vector->y / magnitude; + bzVectorSet(vectorOut, x, y); +} + +float bzVectorMagnitude(const BZVector *vector) { + float sqMagnitude = bzVectorMagnitudeSquared(vector); + float magnitude = bzSqrt(sqMagnitude); + return magnitude; +} + +float bzVectorMagnitudeSquared(const BZVector *vector) { + return bzVectorDot(vector, vector); +} + +float bzVectorDot(const BZVector *vector1, const BZVector *vector2) { + float dot = (vector1->x * vector2->x) + (vector1->y * vector2->y); + return dot; +} + +float bzVectorCross(const BZVector *vector1, const BZVector *vector2) { + float crossZ = (vector1->x * vector2->y) - (vector1->y * vector2->x); + return crossZ; +} + +void bzVectorLerp(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2, float t) { + vectorOut->x = bzLerp(vector1->x, vector2->x, t); + vectorOut->y = bzLerp(vector1->y, vector2->y, t); +} + diff --git a/src/bz/math/vector.h b/src/bz/math/vector.h new file mode 100644 index 0000000..c1d2520 --- /dev/null +++ b/src/bz/math/vector.h @@ -0,0 +1,53 @@ +#ifndef BZ_MATH_VECTOR_H +#define BZ_MATH_VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZVector { + float x; + float y; +}; +typedef struct BZVector BZVector; + +static inline BZVector *bzVectorSet(BZVector *vectorOut, float x, float y) { + vectorOut->x = x; + vectorOut->y = y; + return vectorOut; +} + +extern void bzVectorCopy(BZVector *vectorOut, const BZVector *vector); + +static inline BZVector bzVectorMake(float x, float y) { + BZVector v = { + .x = x, + .y = y, + }; + return v; +} + +extern void bzVectorMakeAngle(BZVector *vectorOut, float angle); + +extern void bzVectorInverse(BZVector *vectorOut, const BZVector *vector); +extern void bzVectorTangent(BZVector *vectorOut, const BZVector *vector); + +extern void bzVectorAdd(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2); +extern void bzVectorSubtract(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2); + +extern void bzVectorScale(BZVector *vectorOut, const BZVector *vector, float scale); +extern void bzVectorNormalized(BZVector *vectorOut, const BZVector *vector); + +extern float bzVectorMagnitude(const BZVector *vector); +extern float bzVectorMagnitudeSquared(const BZVector *vector); +extern float bzVectorDot(const BZVector *vector1, const BZVector *vector2); + +extern float bzVectorCross(const BZVector *vector1, const BZVector *vector2); + +extern void bzVectorLerp(BZVector *vectorOut, const BZVector *vector1, const BZVector *vector2, float t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/memory/allocator.c b/src/bz/memory/allocator.c new file mode 100644 index 0000000..a0516fc --- /dev/null +++ b/src/bz/memory/allocator.c @@ -0,0 +1,2 @@ +#include + diff --git a/src/bz/memory/allocator.h b/src/bz/memory/allocator.h new file mode 100644 index 0000000..8e0fb22 --- /dev/null +++ b/src/bz/memory/allocator.h @@ -0,0 +1,23 @@ +#ifndef BZ_MEMORY_ALLOCATOR_H +#define BZ_MEMORY_ALLOCATOR_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *_bzMemoryAlloc(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber); +extern void *_bzMemoryAllocTmp(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber); + +#define bzMemoryAlloc(arena, size) _bzMemoryAlloc(arena, size, __FILE__, __LINE__) +#define bzMemoryAllocTmp(arena, size) _bzMemoryAlloc(arena, size, __FILE__, __LINE__) + +/* NB: We don't free. Junk the entire arena. */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/memory/arena.c b/src/bz/memory/arena.c new file mode 100644 index 0000000..079bb8f --- /dev/null +++ b/src/bz/memory/arena.c @@ -0,0 +1,143 @@ +#include + +#include +#include +#include +#include // memset... :rolleyes: + +/*struct BZExpandedPointer { + BZMemoryArenaID arena; + uint8_t data[]; +}; +typedef struct BZExpandedPointer BZExpandedPointer;*/ + +//const size_t kMaxUserArenas = 32; +#define kMaxUserArenas 32 // FIXME +static BZMemoryArena userArenas[kMaxUserArenas]; +static size_t nextUserArena = 0; + +BZMemoryArenaID kBZSystemMemoryArena = &userArenas[0]; + +static size_t allocateSize(size_t size) { + size_t alignment = alignof(max_align_t); // FIXME, check this alignment stuff... + size_t alignmentPadding = (alignment - (size % alignment)) % alignment; + size_t fullSize = size + alignmentPadding;// + sizeof(BZExpandedPointer); + return fullSize; +} + +BZMemoryArenaID bzMemoryArenaCreate(BZMemoryArenaID hostArena, size_t size, const char *nameFmt, ...) { + bzAssert(size == allocateSize(size)); + if (nextUserArena < kMaxUserArenas) { + return bzMemoryArenaAllocate(&userArenas[++nextUserArena], hostArena, size); + } else { + bzError("Too many arenas"); + return NULL; + } +} + +void bzMemoryArenaReset(BZMemoryArenaID arena) { + bzMemoryArenaSetup(arena, arena->memory, arena->maxSize, arena->zeroOut); +} + +void bzMemoryArenaResetTmp(BZMemoryArenaID arena) { + if (arena->zeroOut) { + memset(&arena->memory[arena->maxSize - arena->allocationOffsetTop], 0, arena->allocationOffsetTop); + } + arena->allocationOffsetTop = 0; +} + +BZMemoryArenaID bzMemoryArenaSetup(BZMemoryArena *arenaOut, void *memory, size_t size, bool zeroOut) { + arenaOut->maxSize = size; + arenaOut->memory = memory; + arenaOut->numWatermarks = 0; + arenaOut->zeroOut = zeroOut; + + arenaOut->allocationOffsetBottom = 0; + arenaOut->allocationOffsetTop = 0; + + if (arenaOut->zeroOut) { + // We're gonna interop with C++, so we really need to do this. + memset(memory, 0, size); + } + + return arenaOut; +} + +BZMemoryArenaID bzMemoryArenaAllocate(BZMemoryArena *arenaOut, BZMemoryArenaID arenaFrom, size_t size) { + void *memory = bzMemoryAlloc(arenaFrom, size); + return bzMemoryArenaSetup(arenaOut, memory, size, true); +} + +void bzMemoryArenaPushWatermark(BZMemoryArenaID arena) { + if (arena->numWatermarks < MAX_WATERMARKS) { + arena->watermarks[arena->numWatermarks++] = arena->allocationOffsetBottom; + } else { + bzError("Watermarked too many times!!"); + } +} + +void bzMemoryArenaPopWatermark(BZMemoryArenaID arena) { + if (arena->numWatermarks > 0) { + size_t lastAllocation = arena->allocationOffsetBottom; + arena->allocationOffsetBottom = arena->watermarks[--arena->numWatermarks]; + + if (arena->zeroOut) { + // We're gonna interop with C++, so we really need to do this. + // Zero out anything that got used. + memset(&arena->memory[arena->allocationOffsetBottom], 0, lastAllocation - arena->allocationOffsetBottom); + } + } else { + bzError("Watermarked too many times!!"); + } +} + +void *_bzMemoryAlloc(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber) { + bzAssertMessage(arena != NULL, "No memory arena"); + + size_t fullSize = allocateSize(size); + + void *ptr = NULL; + if (arena == kBZSystemMemoryArena) { + ptr = bzSystemAllocate(fullSize); +// if (arena->zeroOut) { + memset(ptr, 0, fullSize); // Just do this one... +// } + } else if (arena->allocationOffsetBottom + fullSize <= arena->maxSize - arena->allocationOffsetTop) { + ptr = &arena->memory[arena->allocationOffsetBottom]; + arena->allocationOffsetBottom += fullSize; + } + + bzAssertMessage(ptr != NULL, "Arena is out of memory, failed %s:%d", filename, lineNumber); + + return ptr; +} + +void *_bzMemoryAllocTmp(BZMemoryArenaID arena, size_t size, const char *filename, size_t lineNumber) { + bzAssertMessage(arena != NULL, "No memory arena"); + + size_t fullSize = allocateSize(size); + + void *ptr = NULL; + if (arena == kBZSystemMemoryArena) { + ptr = bzSystemAllocateStack(fullSize); + } else if (arena->maxSize - arena->allocationOffsetTop - fullSize >= arena->allocationOffsetBottom) { + arena->allocationOffsetTop += fullSize; + ptr = &arena->memory[arena->maxSize - arena->allocationOffsetTop]; + } + + bzAssertMessage(ptr != NULL, "Arena is out of memory, failed %s:%d", filename, lineNumber); + + return ptr; +} // FIXME, this won't work if you pass arena to other places, need more watermarking??.... + + +//void bzMemoryFree(void *p) { +// void *ptr = p; +// ptr -= sizeof(BZExpandedPointer); + +// if (*(BZMemoryArenaID *)ptr == kBZSystemMemoryArena) { +// free(ptr); +// } else { +// bzLog("Asked to free arena memory that cannot be"); +// } +//} diff --git a/src/bz/memory/arena.h b/src/bz/memory/arena.h new file mode 100644 index 0000000..cf61262 --- /dev/null +++ b/src/bz/memory/arena.h @@ -0,0 +1,27 @@ +#ifndef BZ_MEMORY_STADIUM_H +#define BZ_MEMORY_STADIUM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZMemoryArena BZMemoryArena; +typedef BZMemoryArena * BZMemoryArenaID; + +extern BZMemoryArenaID kBZSystemMemoryArena; + +extern BZMemoryArenaID bzMemoryArenaCreate(BZMemoryArenaID hostArena, size_t size, const char *nameFmt, ...); + +extern void bzMemoryArenaReset(BZMemoryArenaID arena); +extern void bzMemoryArenaResetTmp(BZMemoryArenaID arena); // Reset temporary memory only + +extern void bzMemoryArenaPushWatermark(BZMemoryArenaID arena); +extern void bzMemoryArenaPopWatermark(BZMemoryArenaID arena); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/memory/arena_internal.h b/src/bz/memory/arena_internal.h new file mode 100644 index 0000000..8eab3ba --- /dev/null +++ b/src/bz/memory/arena_internal.h @@ -0,0 +1,36 @@ +#ifndef BZ_MEMORY_STADIUM_INTERNAL_H +#define BZ_MEMORY_STADIUM_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_WATERMARKS 16 + +struct BZMemoryArena { + size_t maxSize; + uint8_t *memory; + bool zeroOut; + + size_t allocationOffsetBottom; + size_t allocationOffsetTop; + + size_t numWatermarks; + size_t watermarks[MAX_WATERMARKS]; +}; + +extern BZMemoryArenaID bzMemoryArenaSetup(BZMemoryArena *arenaOut, void *memory, size_t size, bool zeroOut); +extern BZMemoryArenaID bzMemoryArenaAllocate(BZMemoryArena *arenaOut, BZMemoryArenaID arenaFrom, size_t size); + +//extern void bzMemoryFree(void *p); + +extern void *bzSystemAllocate(size_t size); +extern void *bzSystemAllocateStack(size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/renderer/palette.c b/src/bz/renderer/palette.c new file mode 100644 index 0000000..acfbc0b --- /dev/null +++ b/src/bz/renderer/palette.c @@ -0,0 +1,74 @@ +#include + +#include +#include +#include +#include +#include + +BZRendererPaletteID bzRendererLoadPalette(BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZResourceID handle = bzResourcesOpenResource("palettes", "assets/palettes/%s.palette.json", identifier); + size_t length = bzResourcesFileLength(handle); + + char *data = bzMemoryAllocTmp(arena, length); + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *paletteJson = json_parse_string(data); + JSON_Object *paletteJsonObject = json_object(paletteJson); + + JSON_Array *colorNameArray = json_object_get_array(paletteJsonObject, "colors"); + size_t colorCount = json_array_get_count(colorNameArray); + + BZRendererPaletteID palette = bzMemoryAlloc(arena, sizeof(BZRendererPalette) + colorCount * sizeof(uint32_t)); + + const char *asepriteFilename = json_object_get_string(paletteJsonObject, "file"); + + palette->numColors = bzGfxLoadAsepritePalette(palette->colors, colorCount, asepriteFilename); + + json_value_free(paletteJson); + bzMemoryArenaResetTmp(arena); + + return palette; +} + +/* +#define bzPaletteMakeColor(r, g, b) ((r)&0xFF) << 0 | ((g)&0xFF) << 8 | ((b)&0xFF) << 16 | (0xFF) << 24 + + +{ + "file" : "main.aseprite", + "colors" : [ + "black", + "tone-1", + "tone-2", + "tone-3", + "tone-4", + "tone-5", + "tone-6", + "white", + "red-1", + "red-2", + "red-3", + "green-1", + "green-2", + "green-3", + "blue-1", + "blue-2", + "blue-3", + "orange-1", + "orange-2", + "orange-3", + "pink-1", + "pink-2", + "pink-3", + "yellow-1", + "yellow-2", + "yellow-3" + ] +} +*/ + diff --git a/src/bz/renderer/palette.h b/src/bz/renderer/palette.h new file mode 100644 index 0000000..47293fc --- /dev/null +++ b/src/bz/renderer/palette.h @@ -0,0 +1,20 @@ +#ifndef BZ_RENDERER_PALETTE_H +#define BZ_RENDERER_PALETTE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZRendererPalette BZRendererPalette; +typedef BZRendererPalette * BZRendererPaletteID; + +extern BZRendererPaletteID bzRendererLoadPalette(BZMemoryArenaID arena, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/renderer/palette_internal.h b/src/bz/renderer/palette_internal.h new file mode 100644 index 0000000..f386655 --- /dev/null +++ b/src/bz/renderer/palette_internal.h @@ -0,0 +1,21 @@ +#ifndef BZ_RENDERER_PALETTE_INTERNAL_H +#define BZ_RENDERER_PALETTE_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZRendererPalette { + size_t numColors; + uint32_t colors[]; +}; + +#define bzPaletteMakeColor(r, g, b) ((r)&0xFF) << 0 | ((g)&0xFF) << 8 | ((b)&0xFF) << 16 | (0xFF) << 24 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/renderer/render_pass.h b/src/bz/renderer/render_pass.h new file mode 100644 index 0000000..1371f7f --- /dev/null +++ b/src/bz/renderer/render_pass.h @@ -0,0 +1,15 @@ +#ifndef BZ_RENDERER_RENDER_PASS_H +#define BZ_RENDERER_RENDER_PASS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZRenderPass BZRenderPass; +typedef BZRenderPass * BZRenderPassID; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/renderer/render_pass_internal.h b/src/bz/renderer/render_pass_internal.h new file mode 100644 index 0000000..733ee5c --- /dev/null +++ b/src/bz/renderer/render_pass_internal.h @@ -0,0 +1,26 @@ +#ifndef BZ_RENDERER_RENDER_PASS_INTERNAL_H +#define BZ_RENDERER_RENDER_PASS_INTERNAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//, size_t width, size_t height, void *fmt +//typedef void (*BZRenderPassSoftwareRenderer)(uint32_t *output); +//typedef void (*BZRenderPassSetup)(size_t width, size_t height, void *fmt); +typedef void (*BZRenderPassSoftwareRenderer)(BZRendererPaletteID palette, uint32_t *output, size_t width, size_t height, size_t pixelWidth, size_t pixelHeight); + +struct BZRenderPass { +// BZRenderPassSetup setupHook; + BZRenderPassSoftwareRenderer softwareHook; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/renderer/renderer.c b/src/bz/renderer/renderer.c new file mode 100644 index 0000000..e69de29 diff --git a/src/bz/renderer/renderer.h b/src/bz/renderer/renderer.h new file mode 100644 index 0000000..d796f04 --- /dev/null +++ b/src/bz/renderer/renderer.h @@ -0,0 +1,42 @@ +#ifndef BZ_RENDERER_RENDERER_H +#define BZ_RENDERER_RENDERER_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZRendererConfiguration { + size_t outputWidth; + size_t outputHeight; + size_t rendererWidth; + size_t rendererHeight; + size_t gameWidth; + size_t gameHeight; + BZRendererPaletteID palette; +}; +typedef struct BZRendererConfiguration BZRendererConfiguration; + +typedef struct BZRenderer BZRenderer; +typedef BZRenderer * BZRendererID; + +typedef struct BZRendererPass BZRendererPass; +typedef BZRendererPass * BZRendererPassID; + +extern BZRendererID bzRendererInit(BZMemoryArenaID arena, const BZRendererConfiguration *configuration, const char *title); +extern void bzRendererTeardown(BZRendererID renderer); + +BZRendererPassID bzRendererRegisterPass(BZRendererID renderer, BZRenderPassID renderPass); +extern void bzRendererUnregisterPass(BZRendererPassID rendererPass); + +extern void bzRendererBlit(BZRendererID renderer); +extern void bzRendererVBlank(BZRendererID renderer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/renderer/renderer_internal.h b/src/bz/renderer/renderer_internal.h new file mode 100644 index 0000000..ac378ef --- /dev/null +++ b/src/bz/renderer/renderer_internal.h @@ -0,0 +1,16 @@ +#ifndef BZ_RENDERER_RENDERER_INTERNAL_H +#define BZ_RENDERER_RENDERER_INTERNAL_H + +#include +//#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/resources/resource.c b/src/bz/resources/resource.c new file mode 100644 index 0000000..e69de29 diff --git a/src/bz/resources/resource.h b/src/bz/resources/resource.h new file mode 100644 index 0000000..d72a5c8 --- /dev/null +++ b/src/bz/resources/resource.h @@ -0,0 +1,27 @@ +#ifndef BZ_RESOURCES_RESOURCE_H +#define BZ_RESOURCES_RESOURCE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BZResource BZResource; +typedef BZResource * BZResourceID; + +extern BZResourceID bzResourcesOpenResource(const char *type, const char *identifierFmt, ...); +extern void bzResourcesCloseResource(BZResourceID resource); + +// FIXME, these should really be resource handles or something. +extern size_t bzResourcesFileLength(BZResourceID resource); +extern size_t bzResourcesTell(BZResourceID resource); +extern size_t bzResourcesSeek(BZResourceID resource, size_t position); +extern size_t bzResourcesReadBytes(BZResourceID resource, void *outputBuffer, size_t numBytes); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/bindings.cpp b/src/bz/scripting/bindings.cpp new file mode 100644 index 0000000..2a88b12 --- /dev/null +++ b/src/bz/scripting/bindings.cpp @@ -0,0 +1,749 @@ +#include + +#include +#include +#include +//#include +#include +#include +#include +#include + +#include + +#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 +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 +#include +static void native_public(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + + if (parameterCount >= 2) { + bzScriptingSetEnvironmentPublic(_CONTEXT_PARAMETER(scene), stack[1].pointerLiteral, stack[2]); + stack[0] = stack[2]; + } else { + if (bzScriptingGetEnvironmentPublic(&stack[0], _CONTEXT_PARAMETER(scene), stack[1].pointerLiteral) == false) { + stack[0].__raw = 0; + } + } +} + +static void native_btn(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + switch (stack[1].pointerLiteral) { + case COMPILE_TIME_CRC32_STR("up"): + stack[0].__raw = bzInputBtn(1) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("down"): + stack[0].__raw = bzInputBtn(2) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("left"): + stack[0].__raw = bzInputBtn(3) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("right"): + stack[0].__raw = bzInputBtn(4) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("action1"): + stack[0].__raw = bzInputBtn(5) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("action2"): + stack[0].__raw = bzInputBtn(6) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-up"): + stack[0].__raw = bzInputBtn(7) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-down"): + stack[0].__raw = bzInputBtn(8) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-left"): + stack[0].__raw = bzInputBtn(9) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-right"): + stack[0].__raw = bzInputBtn(10) ? 1 : 0; + break; + + default: + // Warn about button?? + stack[0].__raw = 0; + break; + } +} + +static void native_btnp(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + switch (stack[1].pointerLiteral) { + case COMPILE_TIME_CRC32_STR("up"): + stack[0].__raw = bzInputBtnP(1) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("down"): + stack[0].__raw = bzInputBtnP(2) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("left"): + stack[0].__raw = bzInputBtnP(3) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("right"): + stack[0].__raw = bzInputBtnP(4) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("action1"): + stack[0].__raw = bzInputBtnP(5) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("action2"): + stack[0].__raw = bzInputBtnP(6) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-up"): + stack[0].__raw = bzInputBtnP(7) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-down"): + stack[0].__raw = bzInputBtnP(8) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-left"): + stack[0].__raw = bzInputBtnP(9) ? 1 : 0; + break; + + case COMPILE_TIME_CRC32_STR("alpha-right"): + stack[0].__raw = bzInputBtnP(10) ? 1 : 0; + break; + + default: + // Warn about button?? + stack[0].__raw = 0; + break; + } +} + +static void native_depth(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + _CONTEXT_PARAMETER(sortIdx) = FIXED_TO_INT(stack[1].integerLiteral); +} + +static void native_layer(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + _CONTEXT_PARAMETER(layer) = stack[1].pointerLiteral; + _CONTEXT_PARAMETER(drawQueue) = bzGameGetSceneLayerDrawQueue(_CONTEXT_PARAMETER(scene), _CONTEXT_PARAMETER(layer)); +} + +static void native_resetMatrix(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + resetTransform(metadata); + //bzMatrixIdentity(&_CONTEXT_PARAMETER(matrix)); +} + +static void native_translate(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + _CONTEXT_PARAMETER(position).x += _FLOAT_PARAMETER(1); + _CONTEXT_PARAMETER(position).y += _FLOAT_PARAMETER(2); + calculateTransformMatrix(metadata); + +// BZMatrix m; +// bzMatrixTranslation(&m, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2)); +// bzMatrixMultiply(&_CONTEXT_PARAMETER(matrix), &_CONTEXT_PARAMETER(matrix), &m); +} + +static void native_rotate(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + _CONTEXT_PARAMETER(angle) += _FLOAT_PARAMETER(1); + calculateTransformMatrix(metadata); +// BZMatrix m; +// bzMatrixRotation(&m, _FLOAT_PARAMETER(1)); +// bzMatrixMultiply(&_CONTEXT_PARAMETER(matrix), &_CONTEXT_PARAMETER(matrix), &m); +} + +static void native_scale(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + _CONTEXT_PARAMETER(scale).x *= _FLOAT_PARAMETER(1); + if (parameterCount == 1) { + _CONTEXT_PARAMETER(scale).y *= _FLOAT_PARAMETER(1); + } else { + _CONTEXT_PARAMETER(scale).y *= _FLOAT_PARAMETER(2); + } + calculateTransformMatrix(metadata); + + //BZMatrix m; + //if (parameterCount == 1) { + // bzMatrixScale(&m, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(1)); + //} else { + // bzMatrixScale(&m, _FLOAT_PARAMETER(1), _FLOAT_PARAMETER(2)); + //} + //bzMatrixMultiply(&_CONTEXT_PARAMETER(matrix), &_CONTEXT_PARAMETER(matrix), &m); +} + +static void native_event(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + + if (parameterCount == 1) { + bzGameScenePostEvent(_CONTEXT_PARAMETER(scene), _RAW_PARAMETER(1), NULL); + } else if (parameterCount >= 2) { + BZSceneEventData data; + data.integerValue = _RAW_PARAMETER(2); + bzGameScenePostEvent(_CONTEXT_PARAMETER(scene), _RAW_PARAMETER(1), &data); + } +} + +static void native_eventCheck(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + BZSceneEventData data; + bool found = bzGameSceneQueryEvent(&data, _CONTEXT_PARAMETER(scene), _RAW_PARAMETER(1)); + if (found && parameterCount >= 2) { + found = (data.integerValue == _RAW_PARAMETER(2)); + } + stack[0].__raw = found ? 1 : 0; +} + +/*static void native_agent(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + BZAgentSimulationID simulation = bzGameGetAgentSimulation(metadata->scene); + + BZAgentDetails details = { + .angle = metadata->angle, + .mass = bzAgentDefaultMass, + .maxForce = bzAgentDefaultMaxForce, + .maxSpeed = bzAgentDefaultMaxSpeed, + .position = metadata->position, + }; + + if (parameterCount >= 1) { + details.position.x = _FLOAT_PARAMETER(1); + } + if (parameterCount >= 2) { + details.position.y = _FLOAT_PARAMETER(2); + } + if (parameterCount >= 3) { + details.angle = _FLOAT_PARAMETER(3); + } + if (parameterCount >= 4) { + details.mass = _FLOAT_PARAMETER(4); + } + if (parameterCount >= 5) { + details.maxForce = _FLOAT_PARAMETER(5); + } + if (parameterCount >= 6) { + details.maxSpeed = _FLOAT_PARAMETER(6); + } + + metadata->agent = bzFXAgentSimulationAddAgent(simulation, &details); +} + +static void native_agentSync(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + BZAgentSimulationID simulation = bzGameGetAgentSimulation(metadata->scene); + + BZAgentDetails details; + bzFXAgentSimulationGetAgentDetails(&details, simulation, metadata->agent); + + metadata->angle = details.angle; + metadata->position = details.position; + + calculateTransformMatrix(metadata); +}*/ + +static void native_scene(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + BZSceneEventData data = { + .stringValue = _STRING_PARAMETER(1), + }; + bzGameScenePostEvent(_CONTEXT_PARAMETER(scene), bzSceneChangeEventIdentifier, &data); + +// BZActorID actor = bzGameSceneAddActor(_CONTEXT_PARAMETER(scene), "%s", svmGetString(instance, stack[1].pointerLiteral)); +// for (size_t i = 2, j = 1; i <= parameterCount; ++i, ++j) { +// actor->parameters[j] = stack[i].__raw; +// } +} + +static void native_id(SVMModuleInstance *instance, int32_t parameterCount, SVMOperand stack[]) { + BZScriptBindingMetadata *metadata = (BZScriptBindingMetadata *)svmGetModuleInstanceUserParameter(instance); + stack[0].__raw = metadata->uuid; +} + +SVMFunctionCallback bzScriptingBindingsLookupNativeFunction(uint32_t nameCRC, void *userParam) { + switch (nameCRC) { + FUNCTION_BIND(log) + FUNCTION_BIND(logf) + + FUNCTION_BIND(btn) + FUNCTION_BIND(btnp) + + FUNCTION_BIND(min) + FUNCTION_BIND(max) + FUNCTION_BIND(mid) + FUNCTION_BIND(sgn) + FUNCTION_BIND(abs) + FUNCTION_BIND(floor) + FUNCTION_BIND(ceil) + FUNCTION_BIND(adelta) + FUNCTION_BIND(rnd) + FUNCTION_BIND(lerp) + FUNCTION_BIND(dot) + + FUNCTION_BIND(time) + + FUNCTION_BIND(sqrt) + FUNCTION_BIND(sin) + FUNCTION_BIND(cos) + FUNCTION_BIND(atan2) + + FUNCTION_BIND(distance) + FUNCTION_BIND(distanceCheck) + + FUNCTION_BIND(layer) + FUNCTION_BIND(depth) + // FUNCTION_BIND(pset) + // FUNCTION_BIND(sget) + FUNCTION_BIND(cls) + FUNCTION_BIND(camera) + FUNCTION_BIND(circ) + FUNCTION_BIND(circfill) + FUNCTION_BIND(line) + FUNCTION_BIND(rect) + FUNCTION_BIND(rectfill) + FUNCTION_BIND(triangle) + FUNCTION_BIND(spr) + FUNCTION_BIND(sspr) + FUNCTION_BIND(print) + FUNCTION_BIND(printf) + FUNCTION_BIND(fillp) + FUNCTION_BIND(palette) + FUNCTION_BIND(output) + FUNCTION_BIND(map) + FUNCTION_BIND(mget) + FUNCTION_BIND(mset) + FUNCTION_BIND(mfget) + + FUNCTION_BIND(resetMatrix) + FUNCTION_BIND(translate) + FUNCTION_BIND(rotate) + FUNCTION_BIND(scale) + + FUNCTION_BIND(particle) + + FUNCTION_BIND(sound) + FUNCTION_BIND(speech) + FUNCTION_BIND(speechf) + + FUNCTION_BIND(collision) + FUNCTION_BIND(collisionCheck) + + FUNCTION_BIND(actor) + FUNCTION_BIND(find) + FUNCTION_BIND(parameter) + FUNCTION_BIND(public) + + FUNCTION_BIND(event) + FUNCTION_BIND(eventCheck) + +// FUNCTION_BIND(agent) +// FUNCTION_BIND(agentSync) + + FUNCTION_BIND(scene) + + FUNCTION_BIND(id) + + default: + bzLog("Warning: Could not find function %d", nameCRC); + return NULL; + } +} diff --git a/src/bz/scripting/bindings.h b/src/bz/scripting/bindings.h new file mode 100644 index 0000000..59fbafb --- /dev/null +++ b/src/bz/scripting/bindings.h @@ -0,0 +1,12 @@ +#ifndef BZ_SCRIPTING_BINDINGS_H +#define BZ_SCRIPTING_BINDINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/bindings_internal.h b/src/bz/scripting/bindings_internal.h new file mode 100644 index 0000000..1165d0c --- /dev/null +++ b/src/bz/scripting/bindings_internal.h @@ -0,0 +1,18 @@ +#ifndef BZ_SCRIPTING_BINDINGS_INTERNAL_H +#define BZ_SCRIPTING_BINDINGS_INTERNAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern SVMFunctionCallback bzScriptingBindingsLookupNativeFunction(uint32_t nameCRC, void *userParam); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/environment.c b/src/bz/scripting/environment.c new file mode 100644 index 0000000..53ebaa9 --- /dev/null +++ b/src/bz/scripting/environment.c @@ -0,0 +1,48 @@ +#include + +#include +#include +#include +#include + +/*BZScriptingEnvironmentID bzScriptingMakeEnvironment(BZMemoryArenaID arena, void *lookupUserParam, const char *identifierFmt, ...) { + BZScriptingEnvironment *environment = bzMemoryAlloc(arena, sizeof(BZScriptingEnvironment)); + environment->arena = arena; + bzInsertIdentifier(environment->identifier, identifierFmt); + + SVMVMConfiguration config = { + .malloc = malloc, + .free = free, // fixme + .lookup = bzScriptingBindingsLookupNativeFunction, + .lookupUserParam = lookupUserParam, + }; + + environment->vm = svmCreateVM(&config); + + return environment; +} + +bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash) { + for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) { + if (environment->publics[i].identifierHash == identifierHash) { + if (out != NULL) { + out->__raw = environment->publics[i].value.__raw; + } + return true; + } + } + return false; +} + +void bzScriptingSetEnvironmentPublic(BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash, SVMOperand value) { + for (size_t i = 0; i < kBZScriptingEnvironmentPublicCount; ++i) { + if (environment->publics[i].identifierHash == 0) { + environment->publics[i].identifierHash = identifierHash; + } + if (environment->publics[i].identifierHash == identifierHash) { + environment->publics[i].value.__raw = value.__raw; + return; + } + } + bzError("Out of globals!!"); +}*/ diff --git a/src/bz/scripting/environment.h b/src/bz/scripting/environment.h new file mode 100644 index 0000000..1821363 --- /dev/null +++ b/src/bz/scripting/environment.h @@ -0,0 +1,19 @@ +#ifndef BZ_SCRIPTING_ENVIRONMENT_H +#define BZ_SCRIPTING_ENVIRONMENT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//typedef struct BZScriptingEnvironment BZScriptingEnvironment; +//typedef BZScriptingEnvironment * BZScriptingEnvironmentID; + +//extern BZScriptingEnvironmentID bzScriptingMakeEnvironment(BZMemoryArenaID arena, void *lookupUserParam, const char *identifierFmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/environment_internal.h b/src/bz/scripting/environment_internal.h new file mode 100644 index 0000000..3d90b98 --- /dev/null +++ b/src/bz/scripting/environment_internal.h @@ -0,0 +1,41 @@ +#ifndef BZ_SCRIPTING_ENVIRONMENT_INTERNAL_H +#define BZ_SCRIPTING_ENVIRONMENT_INTERNAL_H + +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*#define kBZScriptingEnvironmentScriptCount 256 +#define kBZScriptingEnvironmentScriptInstanceCount 256 +#define kBZScriptingEnvironmentPublicCount 256 + +struct BZScriptingEnvironmentPublic { + BZIdentifierHash identifierHash; + SVMOperand value; +}; +typedef struct BZScriptingEnvironmentPublic BZScriptingEnvironmentPublic; + +struct BZScriptingEnvironment { + BZMemoryArenaID arena; + char identifier[kBZMaxIdentifierLength]; + SVMVM *vm; + BZScript scripts[kBZScriptingEnvironmentScriptCount]; + BZScriptInstance instances[kBZScriptingEnvironmentScriptInstanceCount]; + BZScriptingEnvironmentPublic publics[kBZScriptingEnvironmentPublicCount]; +}; + +extern bool bzScriptingGetEnvironmentPublic(SVMOperand *out, BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash); +extern void bzScriptingSetEnvironmentPublic(BZScriptingEnvironmentID environment, BZIdentifierHash identifierHash, SVMOperand value);*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/script.c b/src/bz/scripting/script.c new file mode 100644 index 0000000..efff766 --- /dev/null +++ b/src/bz/scripting/script.c @@ -0,0 +1,134 @@ +#include + +#include +#include +//#include +//#include +#include +#include +#include + +#define kMaxIncludeDepth 8 + +struct BZScriptReadContext { + size_t currentHandleIdx; + BZResourceID handles[kMaxIncludeDepth]; + bool handlePreprocessor; + bool pastFirstCharacter; +}; +typedef struct BZScriptReadContext BZScriptReadContext; + +static size_t readSource(char *out, size_t outSize, void *userParam) { + BZScriptReadContext *context = userParam; + + BZResourceID currentHandle = context->handles[context->currentHandleIdx]; + size_t readStartPosition = bzResourcesTell(currentHandle); + +process: + if (context->handlePreprocessor) { + // Extract the current line... + + size_t preprocessorLength = 0; + while (bzResourcesReadBytes(currentHandle, out, 1) > 0) { + if (*out == '\n') { + break; + } + preprocessorLength += 1; + } + + char *preprocessorInstruction = alloca(preprocessorLength + 1); + preprocessorInstruction[preprocessorLength] = '\0'; + bzResourcesSeek(currentHandle, readStartPosition); + bzResourcesReadBytes(currentHandle, preprocessorInstruction, preprocessorLength); + + if (strncmp(preprocessorInstruction, "#include ", 9) == 0) { + context->currentHandleIdx++; + bzAssert(context->currentHandleIdx < kMaxIncludeDepth); + preprocessorInstruction += 9; + context->handles[context->currentHandleIdx] = bzResourcesOpenResource("script", "assets/scripts/%s", preprocessorInstruction); + bzAssertMessage(context->handles[context->currentHandleIdx] != NULL, "Invalid include '%s'", preprocessorInstruction); + } + } + + size_t readSize = 0; + while (readSize == 0) { + currentHandle = context->handles[context->currentHandleIdx]; + readStartPosition = bzResourcesTell(currentHandle); + readSize = bzResourcesReadBytes(currentHandle, out, outSize); + + if (readSize == 0) { + if (context->currentHandleIdx > 0) { + bzResourcesCloseResource(currentHandle); + --context->currentHandleIdx; + } else { + // 0th level is closed in main code + break; + } + } + } + + for (size_t i = 0; i < readSize; ++i) { + switch (out[i]) { + case '#': + if (context->pastFirstCharacter == false) { + // need to handle preprocessor, set flag and return only the read characters + bzResourcesSeek(currentHandle, readStartPosition + i); + context->handlePreprocessor = true; + + if (i > 0) { + return i; // don't return the '#' character + } else { + goto process; // I know this is horrible, don't @ me. + } + } + break; + + case '\n': + context->pastFirstCharacter = false; + break; + + case ' ': + case '\t': + // These still count as 'first character' techically... + break; + + default: + context->pastFirstCharacter = true; + break; + } + } + + return readSize; +} + +static void *sourceMalloc(void *memory, size_t size) { + return alloca(size); +} + +SVMModule *bzScriptingLoadScriptModule(BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZScriptReadContext context = { + .handles = { bzResourcesOpenResource("script", "assets/scripts/%s.sun", identifier), }, + .currentHandleIdx = 0, + .handlePreprocessor = false, + .pastFirstCharacter = false, + }; + bzAssertMessage(context.handles[0] != NULL, "Invalid script '%s'", identifier); + + SLCCompileConfiguration config = { + .sourceReadCallback = readSource, + .sourceUserParam = &context, + .realloc = sourceMalloc, // fixme + .free = NULL, // fixme + }; + + SVMModule *module = slcCompileSource(&config); + bzResourcesCloseResource(context.handles[0]); + + if (module == NULL) { + bzError("Could not load script %s", identifier); + } + + return module; +} diff --git a/src/bz/scripting/script.h b/src/bz/scripting/script.h new file mode 100644 index 0000000..4e52d04 --- /dev/null +++ b/src/bz/scripting/script.h @@ -0,0 +1,30 @@ +#ifndef BZ_SCRIPTING_SCRIPT_H +#define BZ_SCRIPTING_SCRIPT_H + +#include +//#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//typedef struct BZScript BZScript; +//typedef BZScript * BZScriptID; + +//typedef struct BZScriptInstance BZScriptInstance; +//typedef BZScriptInstance * BZScriptInstanceID; + +extern SVMModule *bzScriptingLoadScriptModule(BZMemoryArenaID arena, const char *identifierFmt, ...); + + + +//extern BZScriptInstanceID bzScriptingNewInstance(BZScriptID script, void *userParam, const char *identifierFmt, ...); + +//extern bool bzScriptingUpdateInstance(BZScriptInstanceID instance); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/script_internal.h b/src/bz/scripting/script_internal.h new file mode 100644 index 0000000..d3f3c5c --- /dev/null +++ b/src/bz/scripting/script_internal.h @@ -0,0 +1,27 @@ +#ifndef BZ_SCRIPTING_SCRIPT_INTERNAL_H +#define BZ_SCRIPTING_SCRIPT_INTERNAL_H + +#include + +//#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*struct BZScriptInstance { + BZScriptingEnvironmentID environment; + //BZScriptState state; + SVMModuleInstance *instance; +}; + +struct BZScript { + BZScriptingEnvironmentID environment; + SVMModule *module; +};*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/scripting/static_crc32.hpp b/src/bz/scripting/static_crc32.hpp new file mode 100644 index 0000000..18703c4 --- /dev/null +++ b/src/bz/scripting/static_crc32.hpp @@ -0,0 +1,75 @@ +#ifndef BZ_SCRIPTING_STATIC_CRC32_HPP +#define BZ_SCRIPTING_STATIC_CRC32_HPP + +#include + +#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 +struct MM { + static constexpr uint32_t crc32(const char * str, uint32_t prev_crc = 0xFFFFFFFF) { + return MM::crc32(str, (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF] ); + } +}; + +template +struct MM { + static constexpr uint32_t crc32(const char * str, uint32_t prev_crc = 0xFFFFFFFF) { + return prev_crc ^ 0xFFFFFFFF; + } +}; + +#define COMPILE_TIME_CRC32_STR(x) ((MM::crc32(x))) + +#endif diff --git a/src/bz/types/common.h b/src/bz/types/common.h new file mode 100644 index 0000000..d02a5fe --- /dev/null +++ b/src/bz/types/common.h @@ -0,0 +1,16 @@ +#ifndef BZ_TYPES_COMMON_H +#define BZ_TYPES_COMMON_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/types/identifier.c b/src/bz/types/identifier.c new file mode 100644 index 0000000..537a4e2 --- /dev/null +++ b/src/bz/types/identifier.c @@ -0,0 +1,9 @@ +#include + +#include +#include + +BZIdentifierHash bzIdentifierHashFromIdentifier(BZIdentifier identifier) { + BZIdentifierHash hash = crc32buf(identifier, strlen(identifier)); + return hash; +} diff --git a/src/bz/types/identifier.h b/src/bz/types/identifier.h new file mode 100644 index 0000000..a7556d7 --- /dev/null +++ b/src/bz/types/identifier.h @@ -0,0 +1,22 @@ +#ifndef BZ_TYPES_IDENTIFIER_H +#define BZ_TYPES_IDENTIFIER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define kBZMaxIdentifierLength 256 + +typedef const char * BZIdentifier; +typedef uint32_t BZIdentifierHash; + +extern BZIdentifierHash bzIdentifierHashFromIdentifier(BZIdentifier string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/types/identifier_internal.h b/src/bz/types/identifier_internal.h new file mode 100644 index 0000000..4b9bfb0 --- /dev/null +++ b/src/bz/types/identifier_internal.h @@ -0,0 +1,28 @@ +#ifndef BZ_TYPES_IDENTIFIER_INTERNAL_H +#define BZ_TYPES_IDENTIFIER_INTERNAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define bzInsertIdentifier(dst, fmt) \ + { \ + va_list args; \ + va_start(args, fmt); \ + stbsp_vsnprintf(dst, kBZMaxIdentifierLength, fmt, args); \ + va_end(args); \ + } + +#define bzMakeIdentifier(out, fmt) \ + static char out[kBZMaxIdentifierLength]; \ + bzInsertIdentifier(out, fmt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/types/point.h b/src/bz/types/point.h new file mode 100644 index 0000000..1763a91 --- /dev/null +++ b/src/bz/types/point.h @@ -0,0 +1,24 @@ +#ifndef BZ_TYPES_POINT_H +#define BZ_TYPES_POINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZPoint { + float x; + float y; +}; +typedef struct BZPoint BZPoint; + +static inline BZPoint bzPointMake(float x, float y) { + return (BZPoint){ .x = x, .y = y }; +} + +//#define bzPointMake(x, y) ((BZPoint){ .x = x, .y = y }) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/types/rect.h b/src/bz/types/rect.h new file mode 100644 index 0000000..3d53844 --- /dev/null +++ b/src/bz/types/rect.h @@ -0,0 +1,51 @@ +#ifndef BZ_TYPES_RECT_H +#define BZ_TYPES_RECT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZRect { + BZPoint origin; + BZSize size; +}; +typedef struct BZRect BZRect; + +static inline BZRect bzRectMake(float x, float y, float w, float h) { + return (BZRect){ .origin = bzPointMake(x, y), .size = bzSizeMake(w, h) }; +} + +static inline float bzRectMinX(BZRect rect) { + return rect.origin.x - rect.size.width / 2.0f; +} + +static inline float bzRectMidX(BZRect rect) { + return rect.origin.x; +} + +static inline float bzRectMaxX(BZRect rect) { + return rect.origin.x + rect.size.width / 2.0f; +} + +static inline float bzRectMinY(BZRect rect) { + return rect.origin.y - rect.size.height / 2.0f; +} + +static inline float bzRectMidY(BZRect rect) { + return rect.origin.y; +} + +static inline float bzRectMaxY(BZRect rect) { + return rect.origin.y + rect.size.height / 2.0f; +} + +//#define bzRectMake(x, y, w, h) ((BZRect){ .origin = {.x = (x), .y = (y)}, .size = {.width = (w), .height = (h)} }) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/types/size.h b/src/bz/types/size.h new file mode 100644 index 0000000..d7e3fb9 --- /dev/null +++ b/src/bz/types/size.h @@ -0,0 +1,24 @@ +#ifndef BZ_TYPES_SIZE_H +#define BZ_TYPES_SIZE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct BZSize { + float width; + float height; +}; +typedef struct BZSize BZSize; + +static inline BZSize bzSizeMake(float w, float h) { + return (BZSize){ .width = w, .height = h }; +} + +//#define bzSizeMake(w, h) ((BZSize){ .width = w, .height = h }) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bz/types/user_parameter.h b/src/bz/types/user_parameter.h new file mode 100644 index 0000000..abe5e80 --- /dev/null +++ b/src/bz/types/user_parameter.h @@ -0,0 +1,41 @@ +#ifndef BZ_TYPES_USER_PARAMETER_H +#define BZ_TYPES_USER_PARAMETER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*enum BZUserParameterType { + BZUserParameterTypeNone, + + BZUserParameterTypeBoolean, + BZUserParameterTypeInteger, + BZUserParameterTypeFloat, + BZUserParameterTypePointer, +}; +typedef enum BZUserParameterType BZUserParameterType; + +struct BZUserParameter { + BZUserParameterType type; + union { + bool booleanValue; + uint32_t integerValue; + float floatValue; + void *pointerValue; + }; +}; +typedef struct BZUserParameter BZUserParameter; + +#define bzEmptyUserParameter() ((BZUserParameter) { .type = BZUserParameterTypeNone }) +#define bzBooleanUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypeBoolean, .booleanValue = v }) +#define bzIntegerUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypeInteger, .integerValue = v }) +#define bzFloatUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypeFloat, .floatValue = v }) +#define bzPointerUserParameter(v) ((BZUserParameter) { .type = BZUserParameterTypePointer, .pointerValue = v })*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src_platform/playdate/bz/debug/assert.c b/src_platform/playdate/bz/debug/assert.c new file mode 100644 index 0000000..e46aa23 --- /dev/null +++ b/src_platform/playdate/bz/debug/assert.c @@ -0,0 +1,7 @@ +#include + +#include + +void bzAssertExit() { + //exit(1); +} diff --git a/src_platform/playdate/bz/debug/log.c b/src_platform/playdate/bz/debug/log.c new file mode 100644 index 0000000..487fe11 --- /dev/null +++ b/src_platform/playdate/bz/debug/log.c @@ -0,0 +1,29 @@ +#include + +#include +#include + +#define kSprintfBufferLength 1024 +static char buffer[kSprintfBufferLength]; + +void _bzLog(const char *filename, unsigned long lineNumber, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + stbsp_vsnprintf(buffer, kSprintfBufferLength, fmt, args); + va_end(args); + playdate->system->logToConsole("LOG: %s\n", buffer); +} + +void _bzError(const char *filename, unsigned long lineNumber, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + stbsp_vsnprintf(buffer, kSprintfBufferLength, fmt, args); + va_end(args); + playdate->system->error("LOG: %s\n", buffer); +} + +void bzLogInit(const char *outputFilePath) { +} + +void bzLogTeardown() { +} diff --git a/src_platform/playdate/bz/debug/perfgraph.c b/src_platform/playdate/bz/debug/perfgraph.c new file mode 100644 index 0000000..e97680a --- /dev/null +++ b/src_platform/playdate/bz/debug/perfgraph.c @@ -0,0 +1,9 @@ +#include + +void resetSystemTimer(void) { + playdate->system->resetElapsedTime(); +} + +float getSystemTimer(void) { + return playdate->system->getElapsedTime(); +} diff --git a/src_platform/playdate/bz/gfx/gfx_platform.c b/src_platform/playdate/bz/gfx/gfx_platform.c new file mode 100644 index 0000000..e89e392 --- /dev/null +++ b/src_platform/playdate/bz/gfx/gfx_platform.c @@ -0,0 +1,852 @@ +#include + +#include +#include +#include + +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 + +#include +#include +#include + +#include + +// 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)= 0 && (y)= 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 diff --git a/src_platform/playdate/bz/gfx/gfx_platform.h b/src_platform/playdate/bz/gfx/gfx_platform.h new file mode 100644 index 0000000..e69de29 diff --git a/src_platform/playdate/bz/input/input.c b/src_platform/playdate/bz/input/input.c new file mode 100644 index 0000000..ca21ae1 --- /dev/null +++ b/src_platform/playdate/bz/input/input.c @@ -0,0 +1,138 @@ +#include +#include + +#include +#include + +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; +} + diff --git a/src_platform/playdate/bz/memory/arena.c b/src_platform/playdate/bz/memory/arena.c new file mode 100644 index 0000000..9b0b191 --- /dev/null +++ b/src_platform/playdate/bz/memory/arena.c @@ -0,0 +1,9 @@ +#include + +#include + +void *bzSystemAllocate(size_t size) { + void *mem = playdate->system->realloc(NULL, size); + bzAssertMessage(mem != NULL, "Allocation failed"); + return mem; +} diff --git a/src_platform/playdate/bz/renderer/renderer.c b/src_platform/playdate/bz/renderer/renderer.c new file mode 100644 index 0000000..cde9c61 --- /dev/null +++ b/src_platform/playdate/bz/renderer/renderer.c @@ -0,0 +1,84 @@ +#include + +#include +#include +#include +#include +#include + +struct BZRendererPass { + size_t idx; + BZRenderPassID pass; +}; + +/*static SDL_Window *window; +static SDL_Renderer *renderer; +static SDL_Texture *renderTexture; +static SDL_Texture *renderTextureIntermediate; +static size_t intermediateWidth, intermediateHeight; +static int outputWidth, outputHeight;*/ +//static SDL_Surface *windowSurface; +//static SDL_Surface *drawSurface; + +//const static size_t kMaxRenderPasses = 8; +//static size_t numRenderPasses = 0; +#define kMaxRenderPasses 1 // FIXME +static BZRendererPass rendererPasses[kMaxRenderPasses]; + +#define GAME_WIDTH 200 // 256 //128 //320 //128 // 400 // 128 +#define GAME_HEIGHT 120 // 128 //128 //180 //128 // 240 // 128 + +//#define RENDERER_WIDTH 256 //128 //320 //128 // 400 // 128 +//#define RENDERER_HEIGHT 128 //128 //180 //128 // 240 // 128 + +#define RENDERER_WIDTH 400 // 1280 +#define RENDERER_HEIGHT 240 // 720 + +#define SCREEN_WIDTH 400 * 1 // 1280 +#define SCREEN_HEIGHT 240 * 1 // 720 + +//#define SCREEN_SCALE 4 + +void bzRendererInit() {//const BZRendererConfiguration *configuration, const char *title) { + + for (size_t i = 0; i < kMaxRenderPasses; ++i) { + rendererPasses[i].pass = NULL; + } + + prepare palettes + bzGfxPrepareCanvasBuffer(kBZSystemMemoryArena, GAME_WIDTH, GAME_HEIGHT); +} + +void bzRendererTeardown() { +} + +BZRendererPassID bzRendererRegisterPass(BZRenderPassID renderPass) { + for (size_t i = 0; i < kMaxRenderPasses; ++i) { + if (rendererPasses[i].pass == NULL) { + rendererPasses[i].pass = renderPass; + return &rendererPasses[i]; + } + } + return NULL; +} + +void bzRendererUnregisterPass(BZRendererPassID rendererPass) { + rendererPass->pass = NULL; +} + +void bzRendererBlit() { + uint8_t *pixels = playdate->graphics->getFrame(); + for (size_t i = 0; i < kMaxRenderPasses; ++i) { + BZRenderPass *renderPass = rendererPasses[i].pass; + if (renderPass != NULL) { + if (renderPass->softwareHook != NULL) { + renderPass->softwareHook((uint32_t *)pixels/*drawSurface->pixels*/, 2, 2); + } + } + } +} + +void bzRendererVBlank() { + playdate->system->drawFPS(0,0); + playdate->graphics->markUpdatedRows(0, 240); // FIXME +} diff --git a/src_platform/playdate/bz/resources/resource.c b/src_platform/playdate/bz/resources/resource.c new file mode 100644 index 0000000..cc1e733 --- /dev/null +++ b/src_platform/playdate/bz/resources/resource.c @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include + +struct BZResource { SDFile *unused; }; + +BZResourceID bzResourcesOpenResource(const char *type, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + bzLog("Trying to load: %s", identifier); + + BZResourceID resource = (BZResourceID)playdate->file->open(identifier, kFileRead); + return resource; +} + +extern void bzResourcesCloseResource(BZResourceID resource) { + SDFile *handle = (SDFile *)resource; + playdate->file->close(handle); +} + +size_t bzResourcesFileLength(BZResourceID resource) { + SDFile *handle = (SDFile *)resource; + + int reset, size; + + reset = playdate->file->tell(handle); + playdate->file->seek(handle, 0, SEEK_END); + size = playdate->file->tell(handle); + playdate->file->seek(handle, reset, SEEK_SET); + + return size; +} + +size_t bzResourcesTell(BZResourceID resource) { + SDFile *handle = (SDFile *)resource; + return playdate->file->tell(handle); +} + +size_t bzResourcesSeek(BZResourceID resource, size_t position) { + SDFile *handle = (SDFile *)resource; + return playdate->file->seek(handle, position, SEEK_SET); +} + +size_t bzResourcesReadBytes(BZResourceID resource, void *outputBuffer, size_t numBytes) { + SDFile *handle = (SDFile *)resource; + return playdate->file->read(handle, outputBuffer, numBytes); +} diff --git a/src_platform/playdate/playdate/entrypoint.c b/src_platform/playdate/playdate/entrypoint.c new file mode 100644 index 0000000..03e29b8 --- /dev/null +++ b/src_platform/playdate/playdate/entrypoint.c @@ -0,0 +1,64 @@ +//#include +//#include + +#include + +#include +#include +#include +#include +//#include + +extern void bzpGameInit(void); +extern bool bzpGameTick(void); +extern void bzpGameTeardown(void); + +static int update(void *userdata); + +PlaydateAPI *playdate; + +int eventHandler(PlaydateAPI *pd, PDSystemEvent event, uint32_t arg) { + playdate = pd; + + bzLog("Booted to event handler."); + + switch (event) { + case kEventInit: + //bzFileSystemInit(argv[0], SDL_GetBasePath()); + bzpGameInit(); + pd->system->setUpdateCallback(update, pd); + break; + + default: + // ... + break; + } + + return 0; +} + +static int update(void *userdata) { + bzPerfReset(); + bzpGameTick(); + bzRendererVBlank(); // FIXME + + return 1; +} + + +// This is some... interesting stuff from https://wiki.osdev.org/C%2B%2B#GCC + +void *__dso_handle; + +int __cxa_atexit(void (*destructor) (void *), void *arg, void *dso) { + // This is called to register a destructor to be called on program terminate. I'll probably just not bother. + return 0; +} + +void __cxa_finalize(void *f) { + // This is called to actually call the things I just said I won't bother doing. +} + +// Literally do not ask about these. Why do I need exception unwinding defines when exceptions are disabled???? +void *__exidx_start; +void *__exidx_end; diff --git a/src_platform/playdate/playdate/entrypoint.h b/src_platform/playdate/playdate/entrypoint.h new file mode 100644 index 0000000..3cb3381 --- /dev/null +++ b/src_platform/playdate/playdate/entrypoint.h @@ -0,0 +1,8 @@ +#ifndef BTSPLY_PLAYDATE_ENTRYPOINT_H +#define BTSPLY_PLAYDATE_ENTRYPOINT_H + +#include + +extern PlaydateAPI *playdate; + +#endif diff --git a/src_platform/sdl/bz/audio/playback.c b/src_platform/sdl/bz/audio/playback.c new file mode 100644 index 0000000..1a6c22c --- /dev/null +++ b/src_platform/sdl/bz/audio/playback.c @@ -0,0 +1,704 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// FIXME +#define CHANNELS 2 +#define SAMPLE_RATE 48000 +#define SAMPLE_COUNT 512 + +//#define STREAM_DATA_SIZE 16*4096 // FIXME + +enum BZAudioVariableType { + BZAudioVariableTypeConstant, + BZAudioVariableTypeParameter, +}; +typedef enum BZAudioVariableType BZAudioVariableType; + +struct BZAudioVariable { + BZAudioVariableType type; + union { + float constantValue; + BZIdentifierHash parameterIdentifier; + }; +}; +typedef struct BZAudioVariable BZAudioVariable; + +struct BZAudioPlaybackEngineBus { + BZIdentifierHash identifier; + BZAudioVariable volume; + uint8_t *dataBuffer; + BZIdentifierHash targetBusIdentifier; +}; +typedef struct BZAudioPlaybackEngineBus BZAudioPlaybackEngineBus; + +struct BZAudioPlaybackEngineParameter { + BZIdentifierHash identifier; + float value; +}; +typedef struct BZAudioPlaybackEngineParameter BZAudioPlaybackEngineParameter; + +enum BZAudioPlaybackEngineSourceState { + BZAudioPlaybackEngineSourceStateFree, + BZAudioPlaybackEngineSourceStateEnqueued, + BZAudioPlaybackEngineSourceStatePlaying, +}; +typedef enum BZAudioPlaybackEngineSourceState BZAudioPlaybackEngineSourceState; + +typedef void (*BZAudioPlaybackEngineSourceScheduleCallback)(BZAudioPlaybackEngineID engine, void *sourceData, va_list args); +typedef size_t (*BZAudioPlaybackEngineSourceDataCallback)(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData); + +struct BZAudioPlaybackEngineSource { + BZIdentifierHash identifier; + BZAudioVariable volume; + BZIdentifierHash targetBusIdentifier; + + BZAudioPlaybackEngineSourceState state; + + BZAudioPlaybackEngineSourceScheduleCallback scheduleCallback; + BZAudioPlaybackEngineSourceDataCallback dataCallback; + + void *sourceData; +}; +typedef struct BZAudioPlaybackEngineSource BZAudioPlaybackEngineSource; + +//enum BZAudioSoundbankEventType { +// BZAudioSoundbankEventTypeStop, +// BZAudioSoundbankEventTypeSound, +// BZAudioSoundbankEventTypeStream, +// BZAudioSoundbankEventTypeVoice, +//}; +//typedef enum BZAudioSoundbankEventType BZAudioSoundbankEventType; + +struct BZAudioPlaybackEngineEvent { + BZIdentifierHash identifier; + size_t sourceCount; + BZAudioPlaybackEngineSource **sources; +}; +typedef struct BZAudioPlaybackEngineEvent BZAudioPlaybackEngineEvent; + +#define kParameterCount 32 + +struct BZAudioPlaybackEngine { + SDL_AudioDeviceID audioDevice; + SDL_AudioFormat audioDeviceFormat; + + uint8_t audioDeviceChannels; + uint32_t audioDeviceRate; + uint8_t silenceValue; + + size_t dataBufferSize; + uint8_t *tmpDataBuffer; + + size_t busCount; + BZAudioPlaybackEngineBus *buses; + + SDL_mutex *schedulingMutex; + + BZAudioPlaybackEngineParameter parameters[kParameterCount]; + + size_t sourceCount; + BZAudioPlaybackEngineSource *sources; + + size_t eventCount; + BZAudioPlaybackEngineEvent *events; +}; + +static void audioVariableFromJSON(BZAudioVariable *variableOut, void *obj, const char *key, float defaultValue) { + JSON_Object *jsonObject = (JSON_Object *)obj; + + if (json_object_has_value_of_type(jsonObject, key, JSONNumber)) { + variableOut->type = BZAudioVariableTypeConstant; + variableOut->constantValue = json_object_get_number(jsonObject, key); + } else if (json_object_has_value_of_type(jsonObject, key, JSONString)) { + variableOut->type = BZAudioVariableTypeParameter; + const char *parameterIdentifier = json_object_get_string(jsonObject, key); + variableOut->parameterIdentifier = bzIdentifierHashFromIdentifier(parameterIdentifier); + } else { + variableOut->type = BZAudioVariableTypeConstant; + variableOut->constantValue = defaultValue; + } +} + +static float audioPlaybackResolveParameter(BZAudioPlaybackEngineID engine, const BZAudioVariable *variable) { + switch (variable->type) { + case BZAudioVariableTypeConstant: + return variable->constantValue; + + case BZAudioVariableTypeParameter: { + for (size_t i = 0; i < kParameterCount; ++i) { + if (engine->parameters[i].identifier == variable->parameterIdentifier) { + return engine->parameters[i].value; + } + } + return 0.0f; + } + } +} + +static void audioDataCallback(void *userdata, uint8_t *stream, int len) { + BZAudioPlaybackEngineID engine = (BZAudioPlaybackEngine *)userdata; + bzAssert(len <= engine->dataBufferSize); + + SDL_LockMutex(engine->schedulingMutex); + + SDL_memset(engine->buses[0].dataBuffer, engine->silenceValue, engine->dataBufferSize); + + for (size_t i = engine->busCount - 1; i < engine->busCount; --i) { + BZAudioPlaybackEngineBus *bus = &engine->buses[i]; + + SDL_memset(bus->dataBuffer, engine->silenceValue, engine->dataBufferSize); + + for (size_t j = 0; j < engine->sourceCount; ++j) { + BZAudioPlaybackEngineSource *source = &engine->sources[j]; + if (source->state != BZAudioPlaybackEngineSourceStateFree && source->targetBusIdentifier == bus->identifier) { + if (source->state == BZAudioPlaybackEngineSourceStateEnqueued) { + source->state = BZAudioPlaybackEngineSourceStatePlaying; + } + + size_t dataOut = source->dataCallback(engine->tmpDataBuffer, engine->dataBufferSize, engine, source->sourceData); + float volume = audioPlaybackResolveParameter(engine, &source->volume); + SDL_MixAudioFormat(bus->dataBuffer, engine->tmpDataBuffer, engine->audioDeviceFormat, dataOut, bzLerp(0, 128, volume)); // FIXME, log2? + + if (dataOut < engine->dataBufferSize) { + source->state = BZAudioPlaybackEngineSourceStateFree; + } + } + } + + for (size_t j = engine->busCount - 1; j > i; --j) { + BZAudioPlaybackEngineBus *mixBus = &engine->buses[j]; + if (mixBus->targetBusIdentifier == bus->identifier) { + float busVolume = audioPlaybackResolveParameter(engine, &mixBus->volume); // FIXME, log2? + SDL_MixAudioFormat(bus->dataBuffer, mixBus->dataBuffer, engine->audioDeviceFormat, len, bzLerp(0, 128, busVolume)); + } + } + } + + SDL_memset(stream, engine->silenceValue, engine->dataBufferSize); + SDL_MixAudioFormat(stream, engine->buses[0].dataBuffer, engine->audioDeviceFormat, len, bzLerp(0, 128, 1.0f)); // FIXME, volume... + + SDL_UnlockMutex(engine->schedulingMutex); +} + +BZAudioPlaybackEngineID bzAudioPlaybackInit(BZMemoryArenaID arena, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + + BZAudioPlaybackEngineID engine = bzMemoryAlloc(arena, sizeof(BZAudioPlaybackEngine)); + + SDL_AudioSpec desiredSpec; + SDL_AudioSpec obtainedSpec; + + SDL_memset(&desiredSpec, 0, sizeof(SDL_AudioSpec)); + desiredSpec.freq = SAMPLE_RATE; + desiredSpec.format = AUDIO_F32; + desiredSpec.channels = CHANNELS; + desiredSpec.samples = SAMPLE_COUNT; + desiredSpec.callback = audioDataCallback; + desiredSpec.userdata = engine; + + engine->audioDevice = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, SDL_AUDIO_ALLOW_ANY_CHANGE); + if (engine->audioDevice == 0) { + bzError("Failed to initialize audio device"); + } + + engine->audioDeviceFormat = obtainedSpec.format; + engine->audioDeviceChannels = obtainedSpec.channels; + engine->audioDeviceRate = obtainedSpec.freq; + engine->silenceValue = obtainedSpec.silence; + + size_t byteSize = SDL_AUDIO_BITSIZE(obtainedSpec.format) >> 2; + engine->dataBufferSize = obtainedSpec.samples * byteSize * sizeof(uint8_t); + + engine->tmpDataBuffer = bzMemoryAlloc(arena, engine->dataBufferSize); + + BZResourceID handle = bzResourcesOpenResource("audio", "assets/audio/%s.config.json", identifier); + size_t length = bzResourcesFileLength(handle); + + char *data = bzMemoryAllocTmp(arena, length); + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *configJson = json_parse_string(data); + JSON_Object *configJsonObject = json_object(configJson); + + JSON_Array *busesArray = json_object_get_array(configJsonObject, "buses"); + engine->busCount = json_array_get_count(busesArray) + 1;// + 1; // 0 is implicit and main bus + engine->buses = bzMemoryAlloc(arena, engine->busCount * sizeof(BZAudioPlaybackEngineBus)); + for (size_t i = 0; i < engine->busCount; ++i) { + BZAudioPlaybackEngineBus *bus = &engine->buses[i]; + + size_t queueSize = 1; + + if (i > 0) { + JSON_Object *busObject = json_array_get_object(busesArray, i - 1); + const char *identifier = json_object_get_string(busObject, "identifier"); + bus->identifier = bzIdentifierHashFromIdentifier(identifier); + audioVariableFromJSON(&bus->volume, busObject, "volume", 1.0f); + + size_t configQueueSize = json_object_get_number(busObject, "queue_size"); + if (configQueueSize > 0) { + queueSize = configQueueSize; + } + } + + bus->dataBuffer = bzMemoryAlloc(arena, engine->dataBufferSize); + } + + engine->schedulingMutex = SDL_CreateMutex(); + + json_value_free(configJson); + bzMemoryArenaResetTmp(arena); + + // Run + SDL_PauseAudioDevice(engine->audioDevice, 0); + + return engine; +} + +void bzAudioPlaybackTeardown(BZAudioPlaybackEngineID engine) { + SDL_CloseAudioDevice(engine->audioDevice); +} + +void bzAudioPlaybackSetParameter(BZAudioPlaybackEngineID engine, BZIdentifierHash parameterIdentifier, float value) { + for (size_t i = 0; i < kParameterCount; ++i) { + BZAudioPlaybackEngineParameter *parameter = &engine->parameters[i]; + if (parameter->identifier == parameterIdentifier || parameter->identifier == 0) { + parameter->identifier = parameterIdentifier; + parameter->value = value; + return; + } + } +} + +void bzAudioPlaybackPostEvent(BZAudioPlaybackEngineID engine, BZIdentifierHash eventIdentifier, ...) { + SDL_LockMutex(engine->schedulingMutex); + + va_list args; + + for (size_t i = 0; i < engine->eventCount; ++i) { + BZAudioPlaybackEngineEvent *event = &engine->events[i]; + if (event->identifier == eventIdentifier) { + size_t idx = bzRandomInteger(event->sourceCount); + BZAudioPlaybackEngineSource *source = event->sources[idx]; + source->state = BZAudioPlaybackEngineSourceStateEnqueued; + + va_start(args, eventIdentifier); + source->scheduleCallback(engine, source->sourceData, args); + va_end(args); + } + } + + SDL_UnlockMutex(engine->schedulingMutex); +} + +size_t bzAudioCalculateMemorySizeForConversion(BZAudioPlaybackEngineID engine, uint32_t rate, uint8_t channels, uint16_t bps, bool isFloat, bool isSigned, size_t dataSize) { + SDL_AudioFormat format = 0; + + switch (bps) { + case 8: + format = isSigned ? AUDIO_S8 : AUDIO_U8; + break; + + case 16: + format = isSigned ? AUDIO_S16 : AUDIO_U16; + break; + + case 32: + format = isFloat ? AUDIO_F32 : AUDIO_S32; + break; + + default: + bzError("Unhandled sound format"); + break; + } + + SDL_AudioCVT cvt; + SDL_BuildAudioCVT(&cvt, format, channels, rate, engine->audioDeviceFormat, engine->audioDeviceChannels, engine->audioDeviceRate); + + cvt.len = dataSize; + + return cvt.len * cvt.len_mult; +} + +size_t bzAudioPlaybackConvertSample(BZAudioPlaybackEngineID engine, uint32_t rate, uint8_t channels, uint16_t bps, bool isFloat, bool isSigned, void *dst, size_t dataSize, void *src) { + SDL_AudioFormat format = 0; + + switch (bps) { + case 8: + format = isSigned ? AUDIO_S8 : AUDIO_U8; + break; + + case 16: + format = isSigned ? AUDIO_S16 : AUDIO_U16; + break; + + case 32: + format = isFloat ? AUDIO_F32 : AUDIO_S32; + break; + + default: + bzError("Unhandled sound format"); + break; + } + + SDL_AudioCVT cvt; + SDL_BuildAudioCVT(&cvt, format, channels, rate, engine->audioDeviceFormat, engine->audioDeviceChannels, engine->audioDeviceRate); + + cvt.len = dataSize; + cvt.buf = dst;//bzMemoryAlloc(arena, cvt.len * cvt.len_mult); + + if (src != cvt.buf) { + SDL_memcpy(cvt.buf, src, dataSize); + } + + int r = SDL_ConvertAudio(&cvt); + bzAssert(r == 0); + + return cvt.len_cvt; +} + +/////////// + +struct BZAudioSoundSource { + size_t readPosition; + size_t soundSize; + uint8_t *soundData; +}; +typedef struct BZAudioSoundSource BZAudioSoundSource; + +static void soundScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) { + BZAudioSoundSource *source = sourceData; + source->readPosition = 0; +} + +static size_t soundDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) { + BZAudioSoundSource *source = sourceData; + + size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition); + SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize); + source->readPosition += copySize; + + return copySize; +} + +static size_t audioPlaybackCountSounds(JSON_Array *soundsArray) { + size_t baseSoundCount = json_array_get_count(soundsArray); + + size_t soundCount = 0; + for (size_t i = 0; i < baseSoundCount; ++i) { + ++soundCount; // FIXME, count the number of concurrent sounds + } + + return soundCount; +} + +// NB: Sounds. +// You can have multiple sounds playing at each time. +// We create a n sources for each sound, where n is the number of times that sound can be played +// However, we only load the data once, and all sounds reference the same data. +static void audioPlaybackLoadSounds(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *soundsArray) { + size_t soundCount = audioPlaybackCountSounds(soundsArray); + for (size_t i = 0; i < soundCount; ++i) { + BZAudioPlaybackEngineSource *source = &sources[i]; + + JSON_Object *soundObject = json_array_get_object(soundsArray, i); + + const char *identifier = json_object_get_string(soundObject, "identifier"); + source->identifier = bzIdentifierHashFromIdentifier(identifier); + + audioVariableFromJSON(&source->volume, soundObject, "volume", 1.0f); + + const char *targetBusIdentifier = json_object_get_string(soundObject, "bus"); + if (targetBusIdentifier != NULL) { + source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier); + } + + source->state = BZAudioPlaybackEngineSourceStateFree; + + source->scheduleCallback = soundScheduleCallback; + source->dataCallback = soundDataCallback; + BZAudioSoundSource *soundSource = bzMemoryAlloc(arena, sizeof(BZAudioSoundSource)); + source->sourceData = soundSource; + + const char *filename = json_object_get_string(soundObject, "file"); + + size_t size; + uint32_t rate; + uint16_t channels; + uint16_t bps; + void *wavData = bzAudioLoadWAV(&size, arena, &rate, &channels, &bps, filename); // FIXME, temporary memory... + + size_t bufferSize = bzAudioCalculateMemorySizeForConversion(engine, rate, channels, bps, false, bps > 8, size); + soundSource->soundData = bzMemoryAlloc(arena, bufferSize); + soundSource->soundSize = bzAudioPlaybackConvertSample(engine, rate, channels, bps, false, bps > 8, soundSource->soundData, size, wavData); + + // FIXME, Wavedata shouldn't be used. + } +} + +struct BZAudioStreamSource { + BZAudioOGGStream *oggStream; + + size_t readPosition; + + size_t channels; + size_t rate; + size_t oggFrameSize; + + size_t soundSize; + uint8_t *soundData; +}; +typedef struct BZAudioStreamSource BZAudioStreamSource; + +static void streamScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) { + BZAudioStreamSource *source = sourceData; + source->readPosition = 0; + + size_t readPosition; + size_t soundSize; + uint8_t *soundData; +} + +static size_t streamDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) { + BZAudioStreamSource *source = sourceData; + + size_t copiedSize = 0; + for (;;) { + if (source->readPosition == source->soundSize) { + size_t decodedDataSize = 0; + + while (decodedDataSize == 0) { + decodedDataSize = bzAudioQueryOGGData(source->oggStream, source->oggFrameSize, (float *)source->soundData); + if (decodedDataSize == 0) { + bzAudioResetOGGData(source->oggStream); + } + } + source->soundSize = bzAudioPlaybackConvertSample(engine, source->rate, source->channels, 32, true, true, source->soundData, decodedDataSize, source->soundData); + source->readPosition = 0; + } + + size_t copySize = bzMin(dataOutSize - copiedSize, source->soundSize - source->readPosition); + SDL_memcpy(&dataOut[copiedSize], &source->soundData[source->readPosition], copySize); + copiedSize += copySize; + source->readPosition += copySize; + + if (copiedSize == dataOutSize) { + break; + } + } + + return copiedSize; +} + +static size_t audioPlaybackCountStreams(JSON_Array *streamsArray) { + return json_array_get_count(streamsArray); +} + +// NB: Streams. +// You can only have one instance of each stream playing at any time. +// We create one source for each stream and then allocate data required to convert it. +static void audioPlaybackLoadStreams(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *streamsArray) { + size_t streamCount = audioPlaybackCountStreams(streamsArray); + for (size_t i = 0; i < streamCount; ++i) { + BZAudioPlaybackEngineSource *source = &sources[i]; + + JSON_Object *streamObject = json_array_get_object(streamsArray, i); + + const char *identifier = json_object_get_string(streamObject, "identifier"); + source->identifier = bzIdentifierHashFromIdentifier(identifier); + + audioVariableFromJSON(&source->volume, streamObject, "volume", 1.0f); + + const char *targetBusIdentifier = json_object_get_string(streamObject, "bus"); + if (targetBusIdentifier != NULL) { + source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier); + } + + source->state = BZAudioPlaybackEngineSourceStateFree; + + source->scheduleCallback = streamScheduleCallback; + source->dataCallback = streamDataCallback; + BZAudioStreamSource *streamSource = bzMemoryAlloc(arena, sizeof(BZAudioStreamSource)); + source->sourceData = streamSource; + + const char *filename = json_object_get_string(streamObject, "file"); + + BZAudioOGGStreamDetails oggStreamDetails; + streamSource->oggStream = bzAudioOpenOGGStream(&oggStreamDetails, arena, filename); + + streamSource->channels = oggStreamDetails.channels; + streamSource->rate = oggStreamDetails.rate; + streamSource->oggFrameSize = oggStreamDetails.channels * oggStreamDetails.maxSamples * sizeof(float); + size_t soundMaxSize = bzAudioCalculateMemorySizeForConversion(engine, oggStreamDetails.rate, oggStreamDetails.channels, 32, true, true, streamSource->oggFrameSize); + + size_t decodeMemorySize = bzMax(streamSource->oggFrameSize, soundMaxSize); + streamSource->soundData = bzMemoryAlloc(arena, decodeMemorySize); + } +} + +struct BZAudioVoiceSource { + BZAudioSoundSource soundSource; + BZAudioVoice *voice; + size_t voiceMaxSize; + size_t channels; + size_t rate; +}; +typedef struct BZAudioVoiceSource BZAudioVoiceSource; + +static void voiceScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) { + BZAudioVoiceSource *source = sourceData; + + const char *speechText = va_arg(args, const char *); + + size_t dataSize = bzAudioGenerateSpeech(source->voice, (short *)source->soundSource.soundData, source->voiceMaxSize, speechText); + + source->soundSource.soundSize = bzAudioPlaybackConvertSample(engine, source->rate, source->channels, 16, false, true, source->soundSource.soundData, dataSize, source->soundSource.soundData); + source->soundSource.readPosition = 0; +} + +/*static size_t voiceDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) { + BZAudioVoiceSource *source = sourceData; + + size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition); + SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize); + source->readPosition += copySize; + + return copySize; +}*/ + +static size_t audioPlaybackCountVoices(JSON_Array *voicesArray) { + return json_array_get_count(voicesArray); +} + +// NB: Voices. +// You can only have one instance of each voice playing at any time. +// We create one source for each voice and then allocate data required to synthesize. +// Events can be enqueued(??) +static void audioPlaybackLoadVoices(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *voicesArray) { + size_t voiceCount = audioPlaybackCountVoices(voicesArray); + for (size_t i = 0; i < voiceCount; ++i) { + BZAudioPlaybackEngineSource *source = &sources[i]; + + JSON_Object *voiceObject = json_array_get_object(voicesArray, i); + + const char *identifier = json_object_get_string(voiceObject, "identifier"); + source->identifier = bzIdentifierHashFromIdentifier(identifier); + + audioVariableFromJSON(&source->volume, voiceObject, "volume", 1.0f); + + const char *targetBusIdentifier = json_object_get_string(voiceObject, "bus"); + if (targetBusIdentifier != NULL) { + source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier); + } + + source->state = BZAudioPlaybackEngineSourceStateFree; + + source->scheduleCallback = voiceScheduleCallback; + source->dataCallback = soundDataCallback; + BZAudioVoiceSource *voiceSource = bzMemoryAlloc(arena, sizeof(BZAudioVoiceSource)); + source->sourceData = voiceSource; + + BZAudioVoiceDetails voiceDetails; + voiceSource->voice = bzAudioLoadSpeechVoice(&voiceDetails, arena, "%s", identifier); + + voiceSource->channels = voiceDetails.channels; + voiceSource->rate = voiceDetails.rate; + + voiceSource->voiceMaxSize = voiceDetails.channels * voiceDetails.maxSamples * sizeof(short); + size_t soundMaxSize = bzAudioCalculateMemorySizeForConversion(engine, voiceDetails.rate, voiceDetails.channels, 16, false, true, voiceSource->voiceMaxSize); + + size_t decodeMemorySize = bzMax(voiceSource->voiceMaxSize, soundMaxSize); + voiceSource->soundSource.soundData = bzMemoryAlloc(arena, decodeMemorySize); + } +} + +static void audioPlaybackLoadEvents(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *eventsArray) { + engine->eventCount = json_array_get_count(eventsArray); + engine->events = bzMemoryAlloc(arena, engine->eventCount * sizeof(BZAudioPlaybackEngineEvent)); + for (size_t i = 0; i < engine->eventCount; ++i) { + BZAudioPlaybackEngineEvent *event = &engine->events[i]; + + JSON_Object *eventObject = json_array_get_object(eventsArray, i); + + const char *identifier = json_object_get_string(eventObject, "identifier"); + event->identifier = bzIdentifierHashFromIdentifier(identifier); + + const char *sourceIdentifier = NULL; + JSON_Array *sourcesArray = NULL; + + if (json_object_has_value_of_type(eventObject, "source", JSONArray)) { + sourcesArray = json_object_get_array(eventObject, "source"); + event->sourceCount = json_array_get_count(sourcesArray); + } else if (json_object_has_value_of_type(eventObject, "source", JSONString)) { + event->sourceCount = 1; + sourceIdentifier = json_object_get_string(eventObject, "source"); + } else { + // FIXME, stop event + } + + event->sources = bzMemoryAlloc(arena, event->sourceCount * sizeof(BZAudioPlaybackEngineSource *)); + for (size_t j = 0; j < event->sourceCount; ++j) { + if (sourcesArray != NULL) { + sourceIdentifier = json_array_get_string(sourcesArray, j); + } + + BZIdentifierHash sourceIdentifierHash = bzIdentifierHashFromIdentifier(sourceIdentifier); + for (size_t k = 0; k < engine->sourceCount; ++k) { + BZAudioPlaybackEngineSource *source = &engine->sources[k]; + if (source->identifier == sourceIdentifierHash) { + event->sources[j] = source; + break; + } + } + + bzAssertMessage(event->sources[j] != NULL, "Could not find source for event '%s': %s", identifier, sourceIdentifier); + } + } +} + +void bzAudioPlaybackUseSoundbank(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, const char *identifierFmt, ...) { +//FIXME, reset + + bzMakeIdentifier(identifier, identifierFmt); + + BZResourceID handle = bzResourcesOpenResource("soundbanks", "assets/soundbanks/%s.soundbank.json", identifier); + size_t length = bzResourcesFileLength(handle); + + char *data = bzMemoryAllocTmp(arena, length); + bzResourcesReadBytes(handle, data, length); + bzResourcesCloseResource(handle); + + //json_set_allocation_functions + JSON_Value *soundbankJson = json_parse_string(data); + JSON_Object *soundbankJsonObject = json_object(soundbankJson); + + JSON_Array *soundsArray = json_object_get_array(soundbankJsonObject, "sounds"); + JSON_Array *streamsArray = json_object_get_array(soundbankJsonObject, "streams"); + JSON_Array *voicesArray = json_object_get_array(soundbankJsonObject, "voices"); + + size_t soundsCount = audioPlaybackCountSounds(soundsArray); + size_t streamsCount = audioPlaybackCountStreams(streamsArray); + engine->sourceCount = soundsCount + streamsCount + audioPlaybackCountVoices(voicesArray); + engine->sources = bzMemoryAlloc(arena, engine->sourceCount * sizeof(BZAudioPlaybackEngineSource)); + + audioPlaybackLoadSounds(&engine->sources[0], engine, arena, soundsArray); + audioPlaybackLoadStreams(&engine->sources[soundsCount], engine, arena, streamsArray); + audioPlaybackLoadVoices(&engine->sources[soundsCount + streamsCount], engine, arena, voicesArray); + + JSON_Array *eventsArray = json_object_get_array(soundbankJsonObject, "events"); + audioPlaybackLoadEvents(engine, arena, eventsArray); + + json_value_free(soundbankJson); + bzMemoryArenaResetTmp(arena); +} diff --git a/src_platform/sdl/bz/debug/assert.c b/src_platform/sdl/bz/debug/assert.c new file mode 100644 index 0000000..30e8392 --- /dev/null +++ b/src_platform/sdl/bz/debug/assert.c @@ -0,0 +1,7 @@ +#include + +#include + +void bzAssertExit() { + exit(1); +} diff --git a/src_platform/sdl/bz/debug/log.c b/src_platform/sdl/bz/debug/log.c new file mode 100644 index 0000000..cfb2ff5 --- /dev/null +++ b/src_platform/sdl/bz/debug/log.c @@ -0,0 +1,62 @@ +#include + +#include + +#include +#include +#include +#include +#include + +#define _OUTPUT_PRELUDE(dst, name, filename, lineNumber) { \ + time_t t; \ + time(&t); \ + struct tm *timeinfo = localtime(&t); \ + output(dst, name",%02d:%02d:%02d,\"%s\"@%d,", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, filename, lineNumber); \ + } + +static char buffer[STB_SPRINTF_MIN]; + +static int errorFD = STDERR_FILENO; +static int outputFD = STDOUT_FILENO; + +static char *sprintfCallback(char const *buf, void *user, int len) { + int fd = (int)((long)user); + write(fd, buf, (size_t)len); + return buffer; +} + +static void output(int fd, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + stbsp_vsprintfcb(sprintfCallback, (void *)((long)fd), buffer, fmt, args); + va_end(args); +} + +void _bzLog(const char *filename, unsigned long lineNumber, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + _OUTPUT_PRELUDE(outputFD, "LOG", filename, lineNumber); + stbsp_vsprintfcb(sprintfCallback, (void *)((long)outputFD), buffer, fmt, args); + output(outputFD, "\n"); + va_end(args); +} + +void _bzError(const char *filename, unsigned long lineNumber, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + _OUTPUT_PRELUDE(errorFD, "ERROR", filename, lineNumber); + stbsp_vsprintfcb(sprintfCallback, (void *)((long)errorFD), buffer, fmt, args); + output(errorFD, "\n"); + va_end(args); +} + +void bzLogInit(const char *outputFilePath) { + if (outputFilePath != NULL) { + outputFD = errorFD = open(outputFilePath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + } +} + +void bzLogTeardown() { + //close(); +} diff --git a/src_platform/sdl/bz/debug/perfgraph.c b/src_platform/sdl/bz/debug/perfgraph.c new file mode 100644 index 0000000..0075351 --- /dev/null +++ b/src_platform/sdl/bz/debug/perfgraph.c @@ -0,0 +1,12 @@ +#include + +static Uint64 baseTime; + +void resetSystemTimer(void) { + baseTime = SDL_GetTicks64(); +} + +float getSystemTimer(void) { + Uint64 delta = SDL_GetTicks64() - baseTime; + return delta / 1000.0f; +} diff --git a/src_platform/sdl/bz/gfx/gfx_platform.c b/src_platform/sdl/bz/gfx/gfx_platform.c new file mode 100644 index 0000000..2067c6c --- /dev/null +++ b/src_platform/sdl/bz/gfx/gfx_platform.c @@ -0,0 +1,853 @@ +#include + +#include +#include +#include +#include + +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 + +#include +#include +#include + +#include + +// 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)= 0 && (y)= 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 diff --git a/src_platform/sdl/bz/gfx/gfx_platform.h b/src_platform/sdl/bz/gfx/gfx_platform.h new file mode 100644 index 0000000..e69de29 diff --git a/src_platform/sdl/bz/input/input.c b/src_platform/sdl/bz/input/input.c new file mode 100644 index 0000000..24186ef --- /dev/null +++ b/src_platform/sdl/bz/input/input.c @@ -0,0 +1,139 @@ +#include +#include + +#include +#include + +typedef enum BZInputCode { + BZInputCodeQuit = 1, +} BZInputCode; +const BZInputCode BZInputCodeMax = BZInputCodeQuit; + +typedef union BZInputTableEntry { + bool booleanValue; + int integerValue; + float floatValue; +} BZInputTableEntry; + +static BZInput bzQuitInput = { .id = BZInputCodeQuit, }; +BZInputID bzQuitInputID = &bzQuitInput; + +const size_t inputTableSize = BZInputCodeMax + 1; +static BZInputTableEntry inputTable[inputTableSize]; + +typedef struct { + bool down; + bool pressed; +} Button; + +#define kButtonCount 12 + +static Button buttons[kButtonCount]; + +static SDL_Joystick *joy; + +int buttonIdx(SDL_KeyCode keyCode) { + switch (keyCode) { + case SDLK_UP: + return 1; + + case SDLK_DOWN: + return 2; + + case SDLK_LEFT: + return 3; + + case SDLK_RIGHT: + return 4; + + case 'z': + return 5; + + case 'x': + return 6; + + case 'w': + return 7; + + case 's': + return 8; + + case 'a': + return 9; + + case 'd': + return 10; + + default: + return 0; + } +} + +void bzInputInit() { + for (size_t i = 0; i < kButtonCount; ++i) { + buttons[i].down = false; + } + + for (size_t i = 0, c = SDL_NumJoysticks(); i < c; ++i) { + bzLog("Joystick: %s", SDL_JoystickName(i)); + + } + + if (SDL_NumJoysticks() > 0) { + joy = SDL_JoystickOpen(0); + } +} + +void bzInputTeardown() { + +} + +void bzInputProcessFrame() { + for (size_t i = 0; i < inputTableSize; ++i) { + inputTable[i].integerValue = 0; // FIXME, max size and whatever. + } + + for (size_t i = 0; i < kButtonCount; ++i) { + buttons[i].down = buttons[i].down || buttons[i].pressed; + buttons[i].pressed = false; + } + + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + inputTable[BZInputCodeQuit].booleanValue = true; + break; + + case SDL_KEYDOWN: + buttons[buttonIdx(event.key.keysym.sym)].pressed = true; + break; + + case SDL_KEYUP: + buttons[buttonIdx(event.key.keysym.sym)].pressed = false; + buttons[buttonIdx(event.key.keysym.sym)].down = false; + break; + } + } +} + +bool bzInputGetBooleanValue(BZInputID inputID) { + return inputTable[inputID->id].booleanValue; +} + +int bzInputGetIntegerValue(BZInputID inputID) { + return inputTable[inputID->id].integerValue; +} + +float bzInputGetFloatValue(BZInputID inputID) { + return inputTable[inputID->id].floatValue; +} + +bool bzInputBtn(int id) { + return buttons[id].down || buttons[id].pressed; +} + +bool bzInputBtnP(int id) { + return buttons[id].pressed; +} + diff --git a/src_platform/sdl/bz/memory/arena.c b/src_platform/sdl/bz/memory/arena.c new file mode 100644 index 0000000..c129c0c --- /dev/null +++ b/src_platform/sdl/bz/memory/arena.c @@ -0,0 +1,11 @@ +#include + +#include + +void *bzSystemAllocate(size_t size) { + return malloc(size); +} + +void *bzSystemAllocateStack(size_t size) { + return alloca(size); +} diff --git a/src_platform/sdl/bz/renderer/renderer.c b/src_platform/sdl/bz/renderer/renderer.c new file mode 100644 index 0000000..916e73d --- /dev/null +++ b/src_platform/sdl/bz/renderer/renderer.c @@ -0,0 +1,140 @@ +#include + +#include +#include +#include +#include +#include +#include + +struct BZRendererPass { + size_t idx; + BZRenderPassID pass; +}; + +const static size_t kMaxRenderPasses = 8; + +struct BZRenderer { + BZRendererConfiguration configuration; + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Texture *renderTexture; + SDL_Texture *renderTextureIntermediate; + size_t intermediateWidth, intermediateHeight; + int outputWidth, outputHeight; + //static size_t numRenderPasses = 0; + BZRendererPass rendererPasses[kMaxRenderPasses]; +}; + +BZRendererID bzRendererInit(BZMemoryArenaID arena, const BZRendererConfiguration *configuration, const char *title) { + BZRendererID renderer = (BZRenderer *)bzMemoryAlloc(arena, sizeof(BZRenderer)); + + renderer->configuration = *configuration; + + renderer->window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, configuration->outputWidth, configuration->outputHeight, SDL_WINDOW_ALLOW_HIGHDPI); + if (renderer->window == NULL) { + bzError("Could not make window: SDL_Error='%s'", SDL_GetError()); + return NULL; + } + + renderer->renderer = SDL_CreateRenderer(renderer->window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED/*SDL_RENDERER_ACCELERATED*/); + + //SDL_Surface *windowSurface = SDL_GetWindowSurface(window); + SDL_GetWindowSizeInPixels(renderer->window, &renderer->outputWidth, &renderer->outputHeight); + + renderer->renderTexture = SDL_CreateTexture(renderer->renderer, SDL_PIXELFORMAT_ABGR8888/*SDL_PIXELFORMAT_RBZA8888*/, SDL_TEXTUREACCESS_STREAMING, configuration->rendererWidth, configuration->rendererHeight); + + renderer->intermediateWidth = configuration->rendererWidth * bzFloor((renderer->outputWidth / configuration->rendererWidth) + 1); + renderer->intermediateHeight = configuration->rendererHeight * bzFloor((renderer->outputHeight / configuration->rendererHeight) + 1); + renderer->renderTextureIntermediate = SDL_CreateTexture(renderer->renderer, SDL_PIXELFORMAT_ABGR8888/*SDL_PIXELFORMAT_RBZA8888*/, SDL_TEXTUREACCESS_TARGET, renderer->intermediateWidth, renderer->intermediateHeight); + SDL_SetTextureScaleMode(renderer->renderTextureIntermediate, SDL_ScaleModeBest); + +// drawSurface/*tmpSurface*/ = SDL_CreateRBZSurface(0, RENDERER_WIDTH, RENDERER_HEIGHT, windowSurface->format->BytesPerPixel * 8, windowSurface->format->Rmask, windowSurface->format->Gmask, windowSurface->format->Bmask, windowSurface->format->Amask); + //drawSurface = SDL_CreateRBZSurface(0, RENDERER_WIDTH, RENDERER_HEIGHT, 8, 0, 0, 0, 0); + + for (size_t i = 0; i < kMaxRenderPasses; ++i) { + renderer->rendererPasses[i].pass = NULL; + } + + SDL_ShowCursor(SDL_DISABLE); + + bzGfxPreparePalettes(arena, 8, configuration->palette->numColors); + bzGfxPrepareCanvasBuffer(arena, configuration->gameWidth, configuration->gameHeight); + + return renderer; +} + +void bzRendererTeardown(BZRendererID renderer) { + SDL_DestroyWindow(renderer->window); +} + +BZRendererPassID bzRendererRegisterPass(BZRendererID renderer, BZRenderPassID renderPass) { + for (size_t i = 0; i < kMaxRenderPasses; ++i) { + if (renderer->rendererPasses[i].pass == NULL) { + renderer->rendererPasses[i].pass = renderPass; + return &renderer->rendererPasses[i]; + } + } + return NULL; +} + +void bzRendererUnregisterPass(BZRendererPassID rendererPass) { + rendererPass->pass = NULL; +} + +void bzRendererBlit(BZRendererID renderer) { + //SDL_LockSurface(drawSurface); + + //SDL_Rect srcRect = { .x = 0, .y = 0, .w = drawSurface->w, .h = drawSurface->h }; + void *pixels; + int pitch; + + SDL_LockTexture(renderer->renderTexture, NULL, &pixels, &pitch); + + for (size_t i = 0; i < kMaxRenderPasses; ++i) { + BZRenderPass *renderPass = renderer->rendererPasses[i].pass; + if (renderPass != NULL) { + if (renderPass->softwareHook != NULL) { + renderPass->softwareHook(renderer->configuration.palette, (uint32_t *)pixels/*drawSurface->pixels*/, renderer->configuration.gameWidth, renderer->configuration.gameHeight, renderer->configuration.rendererWidth / renderer->configuration.gameWidth, renderer->configuration.rendererHeight / renderer->configuration.gameHeight); + } + } + } + + //SDL_UnlockSurface(drawSurface); + + SDL_UnlockTexture(renderer->renderTexture); + + //SDL_Rect srcRect = { .x = 0, .y = 0, .w = drawSurface->w, .h = drawSurface->h }; + //SDL_Rect dstRect = { .x = 0, .y = 0, .w = windowSurface->w, .h = windowSurface->h}; + //SDL_FillRect(windowSurface, &dstRect, 100); +// int zis = SDL_BlitSurface(drawSurface, &srcRect, tmpSurface, &srcRect); +// int zi = SDL_BlitScaled(drawSurface, &srcRect, windowSurface, &dstRect); + + //SDL_SetRenderTarget(renderer, NULL); + //SDL_RenderClear(renderer); + + SDL_SetRenderTarget(renderer->renderer, renderer->renderTextureIntermediate); + //renderTextureIntermediate + SDL_RenderCopyEx(renderer->renderer, renderer->renderTexture, NULL, NULL, 0, NULL, 0); + SDL_SetRenderTarget(renderer->renderer, NULL); + + + //SDL_RenderCopyEx(renderer, renderTextureIntermediate, NULL, NULL, 0, NULL, 0); + + +// SDL_RenderPresent(renderer); + //SDL_RenderPresent(renderer); + //SDL_SetRenderTarget(renderer, renderTexture); + + //SDL_UpdateWindowSurface(window); + //SDL_UpdateWindowSurface(window); + //SDL_UpdateWindowSurface(window); +} + +void bzRendererVBlank(BZRendererID renderer) { + SDL_RenderClear(renderer->renderer); + size_t height = (size_t)((float)renderer->outputWidth * (float)renderer->configuration.rendererHeight / (float)renderer->configuration.rendererWidth); + SDL_Rect dstRect = { .x = 0, .y = (renderer->outputHeight - height) / 2, .w = renderer->outputWidth, .h = height }; + SDL_RenderCopyEx(renderer->renderer, renderer->renderTextureIntermediate, NULL, &dstRect, 0, NULL, 0); + SDL_RenderPresent(renderer->renderer); +} diff --git a/src_platform/sdl/bz/resources/file_system_internal.h b/src_platform/sdl/bz/resources/file_system_internal.h new file mode 100644 index 0000000..8ccf7d8 --- /dev/null +++ b/src_platform/sdl/bz/resources/file_system_internal.h @@ -0,0 +1,15 @@ +#ifndef BZ_RESOURCES_FILE_SYSTEM_INTERNAL_H +#define BZ_RESOURCES_FILE_SYSTEM_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void bzFileSystemInit(const char *argv0, const char *dataPath); +extern void bzFileSystemTeardown(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src_platform/sdl/bz/resources/resource.c b/src_platform/sdl/bz/resources/resource.c new file mode 100644 index 0000000..d5a46fb --- /dev/null +++ b/src_platform/sdl/bz/resources/resource.c @@ -0,0 +1,74 @@ +#include + +#include +#include +#include +#include + +struct BZResource { PHYSFS_File unused; }; + +// This is FOR DEBUG ONLY! +char *gBZDataDebugDirectory = NULL; + +void bzFileSystemInit(const char *argv0, const char *dataPath) { + PHYSFS_init(argv0); + + if (gBZDataDebugDirectory != NULL) { + bzLog("!!! A debug data directory was found !!!"); + bzLog("!!! DO NOT SHIP IF THIS IS DISPLAYED !!!"); + int fsr3 = PHYSFS_mount(gBZDataDebugDirectory, NULL, 0); + bzAssert(fsr3 != 0); + bzLog("Trying to load: %s", gBZDataDebugDirectory); + } else { + char tmp[1024]; + stbsp_snprintf(tmp, 1024, "%s%s", dataPath, "assets.pak"); + int fsr2 = PHYSFS_mount(tmp, NULL, 0); + bzAssert(fsr2 != 0); + bzLog("Trying to load: %s", tmp); + } +} + +void bzFileSystemTeardown() { + PHYSFS_deinit(); +} + +BZResourceID bzResourcesOpenResource(const char *type, const char *identifierFmt, ...) { + bzMakeIdentifier(identifier, identifierFmt); + bzLog("Trying to load: %s", identifier); + + BZResourceID resource = (BZResourceID)PHYSFS_openRead(identifier); + bzAssertMessage(resource != NULL, "Error opening resource '%s': %s", identifier, PHYSFS_getLastError()); + + return resource; +} + +extern void bzResourcesCloseResource(BZResourceID resource) { + PHYSFS_File *handle = (PHYSFS_File *)resource; + PHYSFS_close(handle); +} + +size_t bzResourcesFileLength(BZResourceID resource) { + PHYSFS_File *handle = (PHYSFS_File *)resource; + return PHYSFS_fileLength(handle); +} + +size_t bzResourcesTell(BZResourceID resource) { + PHYSFS_File *handle = (PHYSFS_File *)resource; + return PHYSFS_tell(handle); +} + +size_t bzResourcesSeek(BZResourceID resource, size_t position) { + PHYSFS_File *handle = (PHYSFS_File *)resource; + return PHYSFS_seek(handle, position); +} + +size_t bzResourcesReadBytes(BZResourceID resource, void *outputBuffer, size_t numBytes) { + PHYSFS_File *handle = (PHYSFS_File *)resource; + PHYSFS_sint64 r = PHYSFS_readBytes(handle, outputBuffer, numBytes); + if (r == -1) { + // FIXME, handle error? + return 0; + } else { + return (size_t)r; + } +} diff --git a/src_platform/sdl/main.c b/src_platform/sdl/main.c new file mode 100644 index 0000000..b821d97 --- /dev/null +++ b/src_platform/sdl/main.c @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +extern void bzpGameLaunch(void); +extern void bzpGameInit(void); +extern bool bzpGameTick(void); +extern void bzpGameTeardown(void); + +int main(int argc, char *argv[]) { + int returnCode = 0; + + bzpGameLaunch(); + + gSystemTicksPerSecond = 60; + + SDL_Init(SDL_INIT_EVERYTHING); + bzFileSystemInit(argv[0], SDL_GetBasePath()); + bzInputInit(); + + bzpGameInit(); + + //uint64_t targetUpdateTime = 1000 / 60; + //uint64_t nextUpdateTicks = 0; + + for (;;) { +// bzInputProcessFrame(); + + // FIXME, input stuff... + + //uint64_t ticks = SDL_GetTicks64(); + + //if (ticks >= nextUpdateTicks) { + // nextUpdateTicks = ticks + targetUpdateTime; + bzPerfReset(); + if (bzpGameTick() == false) goto game_loop_exit; // FIXME + if (bzInputGetBooleanValue(bzQuitInputID)) goto game_loop_exit; // FIXME + //} + } +game_loop_exit: + + bzpGameTeardown(); + bzInputTeardown(); + bzFileSystemTeardown(); + bzLogTeardown(); + SDL_Quit(); + + return returnCode; +} diff --git a/third_party/parson b/third_party/parson new file mode 160000 index 0000000..ba29f4e --- /dev/null +++ b/third_party/parson @@ -0,0 +1 @@ +Subproject commit ba29f4eda9ea7703a9f6a9cf2b0532a2605723c3 diff --git a/third_party/pcg-c-basic b/third_party/pcg-c-basic new file mode 160000 index 0000000..bc39cd7 --- /dev/null +++ b/third_party/pcg-c-basic @@ -0,0 +1 @@ +Subproject commit bc39cd76ac3d541e618606bcc6e1e5ba5e5e6aa3 diff --git a/third_party/physfs b/third_party/physfs new file mode 160000 index 0000000..d1ca6e1 --- /dev/null +++ b/third_party/physfs @@ -0,0 +1 @@ +Subproject commit d1ca6e14aa904363706a0f5712d8b8e6e13c5fa6 diff --git a/third_party/sdl b/third_party/sdl new file mode 160000 index 0000000..efaa587 --- /dev/null +++ b/third_party/sdl @@ -0,0 +1 @@ +Subproject commit efaa58732abb3faf3900b2d93a166b955fbd8ed0 diff --git a/third_party/snippets_org_crc/crc.c b/third_party/snippets_org_crc/crc.c new file mode 100644 index 0000000..531e837 --- /dev/null +++ b/third_party/snippets_org_crc/crc.c @@ -0,0 +1,126 @@ +/* Crc - 32 BIT ANSI X3.66 CRC checksum files */ + +#include "crc.h" + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, +0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, +0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, +0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, +0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, +0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, +0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, +0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, +0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, +0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, +0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, +0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, +0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, +0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, +0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, +0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, +0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, +0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, +0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, +0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, +0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, +0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, +0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, +0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, +0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, +0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, +0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, +0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, +0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, +0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, +0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, +0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, +0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, +0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, +0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, +0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, +0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, +0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, +0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, +0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, +0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, +0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, +0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) + +uint32_t crc32buf(const char *buf, size_t len) +{ + register uint32_t oldcrc32; + + oldcrc32 = 0xFFFFFFFF; + + for ( ; len; --len, ++buf) + { + oldcrc32 = UPDC32(*buf, oldcrc32); + } + + return ~oldcrc32; + +} + +// Adapted from http://web.archive.org/web/20080303102530/http://c.snippets.org/snip_lister.php?fname=crc_32.c diff --git a/third_party/snippets_org_crc/crc.h b/third_party/snippets_org_crc/crc.h new file mode 100644 index 0000000..f818654 --- /dev/null +++ b/third_party/snippets_org_crc/crc.h @@ -0,0 +1,22 @@ + +/* +** CRC.H - header file for SNIPPETS CRC and checksum functions +*/ + +#ifndef CRC__H +#define CRC__H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t crc32buf(const char *buf, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* CRC__H */ \ No newline at end of file diff --git a/third_party/soloud_speech/Elements.def b/third_party/soloud_speech/Elements.def new file mode 100644 index 0000000..cdeabbd --- /dev/null +++ b/third_party/soloud_speech/Elements.def @@ -0,0 +1,1659 @@ +/*mName, mRK, mDU, mUD, mFont, mDict, mIpa, mFeat, interpolators*/ +/* (mSteady, mFixed, mProportion, mExtDelay, mIntDelay) */ +{"END", 31, 5, 5,0x00,NULL,NULL,0, + { + { 270, 135, 50, 3, 3}, /* ELM_FN 0 */ + { 490, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 1480, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, -10.5, 100, 3, 0}, /* ELM_AN -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A1 -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A2 -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A3 -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A4 -10.5 */ + { -30, 0, 100, 3, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 3, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 3, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"Q", 29, 6, 6,0x00,NULL,NULL,0, + { + { 270, 135, 50, 3, 3}, /* ELM_FN 0 */ + { 490, 0, 100, 3, 3}, /* ELM_F1 0 */ + { 1480, 0, 100, 3, 3}, /* ELM_F2 0 */ + { 2500, 0, 100, 3, 3}, /* ELM_F3 0 */ + { 60, 0, 100, 3, 3}, /* ELM_B1 0 */ + { 90, 0, 100, 3, 3}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 3}, /* ELM_B3 0 */ + { -30, -10.5, 100, 3, 0}, /* ELM_AN -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A1 -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A2 -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A3 -10.5 */ + { -30, -10.5, 100, 3, 0}, /* ELM_A4 -10.5 */ + { -30, 0, 100, 3, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 3, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 3, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"P", 23, 8, 8,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 760, 350, 50, 2, 2}, /* ELM_F2 -30 */ + { 2500, 0, 100, 0, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"PY", 29, 1, 1,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 760, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 49, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 43.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"PZ", 23, 2, 2,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 760, 350, 50, 2, 2}, /* ELM_F2 -30 */ + { 2500, 0, 100, 2, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 2, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 33.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"T", 23, 6, 6,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */ + { 2680, 2680, 0, 0, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 150, 0, 0, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"TY", 29, 1, 1,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 1780, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2680, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 50.75, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"TZ", 23, 2, 2,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 1}, /* ELM_F1 15 */ + { 1780, 950, 50, 2, 1}, /* ELM_F2 60 */ + { 2680, 2680, 0, 2, 0}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 1}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 1}, /* ELM_B2 0 */ + { 150, 150, 0, 2, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"K", 23, 8, 8,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 3, 3}, /* ELM_F1 15 */ + { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */ + { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */ + { 60, 30, 50, 3, 3}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 3}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 3}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"KY", 29, 1, 1,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 1480, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2620, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 50.75, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 50.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"KZ", 23, 4, 4,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 3, 3}, /* ELM_F1 15 */ + { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */ + { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */ + { 60, 30, 50, 3, 3}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 3}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 3}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 19.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"B", 26,12,12,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 760, 350, 50, 2, 2}, /* ELM_F2 -30 */ + { 2500, 0, 100, 0, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"BY", 29, 1, 1,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 760, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 49, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 43.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"BZ", 26, 0, 0,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 0}, /* ELM_F1 15 */ + { 760, 350, 50, 2, 0}, /* ELM_F2 -30 */ + { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 0}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"D", 26, 8, 8,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */ + { 2680, 2680, 0, 2, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 150, 0, 2, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"DY", 29, 1, 1,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 1780, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2680, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 35, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 45.5, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"DZ", 26, 1, 1,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 0}, /* ELM_F1 15 */ + { 1780, 950, 50, 2, 0}, /* ELM_F2 60 */ + { 2680, 2680, 0, 2, 0}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 0}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 0}, /* ELM_B2 0 */ + { 150, 150, 0, 2, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 38.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 35, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + + +{"G", 26,12,12,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 3, 3}, /* ELM_F1 15 */ + { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */ + { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */ + { 60, 30, 50, 3, 3}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 3}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 3}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 35, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"GY", 29, 1, 1,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 1480, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2620, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 35, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 45.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"GZ", 26, 2, 2,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 3, 2}, /* ELM_F1 15 */ + { 1480, 1550, 50, 3, 2}, /* ELM_F2 810 */ + { 2620, 1580, 50, 3, 2}, /* ELM_F3 270 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 35, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 35, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 14, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"M", 15, 8, 8,0x6D,"m","m",ELM_FEATURE_BLB|ELM_FEATURE_NAS, + { + { 360, 360, 0, 3, 0}, /* ELM_FN 0 */ + { 480, 480, 0, 3, 0}, /* ELM_F1 0 */ + { 1000, 350, 50, 3, 0}, /* ELM_F2 -150 */ + { 2200, 0, 100, 5, 0}, /* ELM_F3 0 */ + { 40, 20, 50, 3, 0}, /* ELM_B1 0 */ + { 175, 87, 50, 3, 0}, /* ELM_B2 -0.5 */ + { 120, 0, 100, 5, 0}, /* ELM_B3 0 */ + { 42, 21, 50, 3, 0}, /* ELM_AN 0 */ + { 26, -10, 100, 3, 0}, /* ELM_A1 -10 */ + { 30, -10, 100, 3, 0}, /* ELM_A2 -10 */ + { 33, -10, 100, 3, 0}, /* ELM_A3 -10 */ + { -30, -10, 100, 3, 0}, /* ELM_A4 -10 */ + { -30, 0, 100, 3, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 3, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 3, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 2, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 2, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 2, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 2, 0} /* ELM_AF 0 */ + } +}, + +{"N", 15, 8, 8,0x6E,"n","n",ELM_FEATURE_ALV|ELM_FEATURE_NAS, + { + { 450, 450, 0, 3, 0}, /* ELM_FN 0 */ + { 480, 480, 0, 3, 0}, /* ELM_F1 0 */ + { 1780, 950, 50, 3, 3}, /* ELM_F2 60 */ + { 2620, 2680, 0, 3, 0}, /* ELM_F3 60 */ + { 40, 20, 50, 3, 0}, /* ELM_B1 0 */ + { 300, 150, 50, 3, 3}, /* ELM_B2 0 */ + { 260, 130, 50, 3, 0}, /* ELM_B3 0 */ + { 42, 21, 50, 3, 0}, /* ELM_AN 0 */ + { 35, -10, 100, 3, 0}, /* ELM_A1 -10 */ + { 35, -10, 100, 3, 0}, /* ELM_A2 -10 */ + { 35, -10, 100, 3, 0}, /* ELM_A3 -10 */ + { 20, -10, 100, 3, 0}, /* ELM_A4 -10 */ + { -30, 0, 100, 3, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 3, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 3, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 2, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 2, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 2, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 2, 0} /* ELM_AF 0 */ + } +}, + +{"NG", 15, 8, 8,0x4E,"N","N",ELM_FEATURE_NAS|ELM_FEATURE_VEL, + { + { 360, 360, 0, 3, 0}, /* ELM_FN 0 */ + { 480, 480, 0, 3, 0}, /* ELM_F1 0 */ + { 820, 1550, 50, 5, 3}, /* ELM_F2 1140 */ + { 2800, 1580, 50, 3, 3}, /* ELM_F3 180 */ + { 160, 80, 0, 5, 0}, /* ELM_B1 -80 */ + { 150, 75, 50, 5, 3}, /* ELM_B2 0 */ + { 100, 50, 50, 3, 0}, /* ELM_B3 0 */ + { 42, 21, 50, 3, 3}, /* ELM_AN 0 */ + { 20, 0, 100, 3, 0}, /* ELM_A1 0 */ + { 30, 0, 100, 3, 0}, /* ELM_A2 0 */ + { 35, 0, 100, 3, 0}, /* ELM_A3 0 */ + { 0, 0, 100, 3, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 3, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 3, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 3, 0}, /* ELM_AB 0 */ + { 52, 26, 50, 2, 0}, /* ELM_AV 0 */ + { 56, 28, 50, 2, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 2, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 2, 0} /* ELM_AF 0 */ + } +}, + +{"F", 18,12,12,0x66,"f","f",ELM_FEATURE_FRC|ELM_FEATURE_LBD|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */ + { 1420, 350, 50, 3, 2}, /* ELM_F2 -360 */ + { 2560, 980, 50, 3, 2}, /* ELM_F3 -300 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 0, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 0, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { 54, 27, 50, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 32, 16, 50, 0, 0}, /* ELM_ASP 0 */ + { 54, 30, 50, 0, 0} /* ELM_AF 3 */ + } +}, + +{"TH", 18,15,15,0x54,"T","T",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */ + { 1780, 1190, 50, 3, 2}, /* ELM_F2 300 */ + { 2680, 2680, 0, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 150, 0, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 22.75, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"S", 18,12,12,0x73,"s","s",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */ + { 1720, 950, 50, 3, 2}, /* ELM_F2 90 */ + { 2620, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 200, 100, 50, 3, 2}, /* ELM_B1 0 */ + { 96, 48, 50, 3, 2}, /* ELM_B2 0 */ + { 220, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 32, 16, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"SH", 18,12,12,0x53,"S","S",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */ + { 2200, 1190, 50, 3, 2}, /* ELM_F2 90 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 42, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"X", 18,12,12,0x78,"x","x",ELM_FEATURE_FRC|ELM_FEATURE_VEL|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 3, 3}, /* ELM_F1 15 */ + { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */ + { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */ + { 60, 30, 50, 3, 3}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 3}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 3}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 19.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"H", 9,10,10,0x68,"h","h",ELM_FEATURE_APR|ELM_FEATURE_GLT, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 0, 100, 0, 7}, /* ELM_F1 0 */ + { 1480, 0, 100, 0, 7}, /* ELM_F2 0 */ + { 2500, 0, 100, 0, 7}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 7}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 7}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 7}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 7}, /* ELM_AN 0 */ + { 35, -14, 100, 0, 7}, /* ELM_A1 -14 */ + { 36.75, -14, 100, 0, 7}, /* ELM_A2 -14 */ + { 26.25, -7, 100, 0, 7}, /* ELM_A3 -7 */ + { 22.75, -3.5, 100, 0, 7}, /* ELM_A4 -3.5 */ + { -30, 0, 100, 0, 7}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 7}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 7}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"V", 20, 4, 4,0x76,"v","v",ELM_FEATURE_FRC|ELM_FEATURE_LBD|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 1420, 350, 50, 3, 2}, /* ELM_F2 -360 */ + { 2560, 980, 50, 3, 2}, /* ELM_F3 -300 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 75, 50, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 33.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"QQ", 30, 0, 0,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 0, 100, 0, 0}, /* ELM_F1 0 */ + { 1420, 0, 100, 0, 0}, /* ELM_F2 0 */ + { 2560, 0, 100, 0, 0}, /* ELM_F3 0 */ + { 60, 0, 100, 0, 0}, /* ELM_B1 0 */ + { 90, 0, 100, 0, 0}, /* ELM_B2 0 */ + { 150, 0, 100, 0, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 33.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"DH", 20, 4, 4,0x54,"D","D",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 1600, 1190, 50, 3, 2}, /* ELM_F2 390 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { 54, 27, 50, 0, 0}, /* ELM_AB 0 */ + { 36, 18, 50, 0, 0}, /* ELM_AV 0 */ + { 54, 27, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"DI", 20, 4, 4,0x54,"D","D",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 1600, 1190, 50, 3, 2}, /* ELM_F2 390 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 28, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"Z", 20, 4, 4,0x7A,"z","z",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 1720, 950, 50, 3, 2}, /* ELM_F2 90 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 40, 20, 50, 0, 0}, /* ELM_AV 0 */ + { 54, 27, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"ZZ", 20, 4, 4,0x7A,"z","z",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 1720, 950, 50, 3, 2}, /* ELM_F2 90 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 24.5, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"ZH", 20, 4, 4,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 2020, 1190, 50, 3, 2}, /* ELM_F2 180 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"CH", 23, 4, 4,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */ + { 2680, 2680, 0, 2, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 150, 0, 2, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"CI", 18, 8, 8,0x53,"S","S",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VLS, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */ + { 2020, 1190, 50, 3, 2}, /* ELM_F2 180 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 42, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AV 0 */ + { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */ + { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */ + { 60, 30, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"J", 26, 4, 4,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 110, 50, 2, 2}, /* ELM_F1 15 */ + { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */ + { 2680, 2680, 0, 2, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 2, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 2, 2}, /* ELM_B2 0 */ + { 150, 150, 0, 2, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 31.5, 0, 100, 0, 0}, /* ELM_A1 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A2 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"JY", 20, 3, 3,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 280, 170, 50, 3, 2}, /* ELM_F1 30 */ + { 2020, 1190, 50, 3, 2}, /* ELM_F2 180 */ + { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */ + { 60, 30, 50, 3, 2}, /* ELM_B1 0 */ + { 90, 45, 50, 3, 2}, /* ELM_B2 0 */ + { 150, 0, 100, 3, 2}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"L", 11, 8, 8,0x6C,"l","l",ELM_FEATURE_ALV|ELM_FEATURE_LAT|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 460, 230, 50, 6, 0}, /* ELM_F1 0 */ + { 1480, 710, 50, 6, 0}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 6, 0}, /* ELM_F3 -30 */ + { 60, 30, 50, 6, 0}, /* ELM_B1 0 */ + { 90, 45, 50, 6, 0}, /* ELM_B2 0 */ + { 150, 75, 50, 6, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 21, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"LL", 11, 8, 8,0x6C,"l","l",ELM_FEATURE_ALV|ELM_FEATURE_LAT|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 460, 230, 50, 6, 0}, /* ELM_F1 0 */ + { 940, 470, 50, 6, 0}, /* ELM_F2 0 */ + { 2500, 1220, 50, 6, 0}, /* ELM_F3 -30 */ + { 60, 30, 50, 6, 0}, /* ELM_B1 0 */ + { 90, 45, 50, 6, 0}, /* ELM_B2 0 */ + { 150, 75, 50, 6, 0}, /* ELM_B3 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AN 0 */ + { 36.75, 0, 100, 0, 0}, /* ELM_A1 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */ + { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */ + { 21, 0, 100, 0, 0}, /* ELM_A4 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A5 0 */ + { -30, 0, 100, 0, 0}, /* ELM_A6 0 */ + { -30, 0, 100, 0, 0}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"RX", 10,10,10,0xD5,"R","",ELM_FEATURE_RZD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 0, 100, 0, 5}, /* ELM_F1 0 */ + { 1180, 0, 100, 0, 5}, /* ELM_F2 0 */ + { 1600, 1600, 0, 5, 5}, /* ELM_F3 0 */ + { 60, 30, 50, 0, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 70, 35, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 42, 21, 50, 5, 5}, /* ELM_A1 0 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A2 0 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */ + { -30, 0, 50, 5, 5}, /* ELM_A4 15 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 50, 25, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"R", 10,11,11,0xA8,"r","r",ELM_FEATURE_ALV|ELM_FEATURE_APR, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 0, 100, 0, 5}, /* ELM_F1 0 */ + { 1180, 590, 50, 5, 5}, /* ELM_F2 0 */ + { 1600, 740, 50, 5, 5}, /* ELM_F3 -60 */ + { 60, 0, 100, 0, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 42, 21, 50, 5, 5}, /* ELM_A1 0 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A2 0 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */ + { -30, 0, 50, 5, 5}, /* ELM_A4 15 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"W", 10, 8, 8,0x77,"w","w",ELM_FEATURE_APR|ELM_FEATURE_LBV|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 190, 50, 50, 4, 4}, /* ELM_F1 -45 */ + { 760, 350, 50, 4, 4}, /* ELM_F2 -30 */ + { 2020, 980, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 43.75, 21, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 28, 14, 50, 4, 4}, /* ELM_A2 0 */ + { 21, 10.5, 50, 4, 4}, /* ELM_A3 0 */ + { -30, 0, 50, 4, 4}, /* ELM_A4 15 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"Y", 10, 7, 7,0x6A,"j","j",ELM_FEATURE_APR|ELM_FEATURE_PAL|ELM_FEATURE_VCD, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 250, 110, 50, 4, 4}, /* ELM_F1 -15 */ + { 2500, 1190, 50, 4, 4}, /* ELM_F2 -60 */ + { 2980, 1460, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 33.25, 17.5, 50, 4, 4}, /* ELM_A2 0.875 */ + { 38.5, 17.5, 50, 4, 4}, /* ELM_A3 -1.75 */ + { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"I", 2, 8, 6,0x49,"I","I",ELM_FEATURE_FNT|ELM_FEATURE_SMH|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 400, 170, 50, 4, 4}, /* ELM_F1 -30 */ + { 2080, 1070, 50, 4, 4}, /* ELM_F2 30 */ + { 2560, 1340, 50, 4, 4}, /* ELM_F3 60 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 36.75, 17.5, 50, 4, 4}, /* ELM_A2 -0.875 */ + { 35, 17.5, 50, 4, 4}, /* ELM_A3 0 */ + { 29.75, 14, 50, 4, 4}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"E", 2, 8, 4,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 640, 350, 50, 4, 4}, /* ELM_F1 30 */ + { 2020, 1070, 50, 4, 4}, /* ELM_F2 60 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 42, 21, 50, 4, 4}, /* ELM_A2 0 */ + { 38.5, 17.5, 50, 4, 4}, /* ELM_A3 -1.75 */ + { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"AA", 2,10, 5,0x51,"&","&",ELM_FEATURE_FNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 790, 410, 50, 4, 4}, /* ELM_F1 15 */ + { 1780, 950, 50, 4, 4}, /* ELM_F2 60 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 130, 65, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 47.25, 24.5, 50, 4, 4}, /* ELM_A2 0.875 */ + { 38.5, 17.5, 50, 4, 4}, /* ELM_A3 -1.75 */ + { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"U", 2, 9, 6,0xC3,"V","V",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 700, 350, 50, 4, 4}, /* ELM_F1 0 */ + { 1360, 710, 50, 4, 4}, /* ELM_F2 30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 43.75, 21, 50, 4, 4}, /* ELM_A2 -0.875 */ + { 31.5, 14, 50, 4, 4}, /* ELM_A3 -1.75 */ + { 24.5, 10.5, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"O", 2, 9, 6,0x81,"0","A.",ELM_FEATURE_BCK|ELM_FEATURE_LOW|ELM_FEATURE_RND|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 610, 290, 50, 4, 4}, /* ELM_F1 -15 */ + { 880, 470, 50, 4, 4}, /* ELM_F2 30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 47.25, 24.5, 50, 4, 4}, /* ELM_A2 0.875 */ + { 22.75, 10.5, 50, 4, 4}, /* ELM_A3 -0.875 */ + { 15.75, 7, 50, 4, 4}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OO", 2, 6, 4,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 370, 170, 50, 4, 4}, /* ELM_F1 -15 */ + { 1000, 470, 50, 4, 4}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 42, 21, 50, 4, 4}, /* ELM_A2 0 */ + { 28, 14, 50, 4, 4}, /* ELM_A3 0 */ + { 22.75, 10.5, 50, 4, 4}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"A", 2, 4, 4,0xAB,"@","@",ELM_FEATURE_CNT|ELM_FEATURE_MDL|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 230, 50, 4, 4}, /* ELM_F1 -15 */ + { 1480, 710, 50, 4, 4}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A2 -0.875 */ + { 33.25, 17.5, 50, 4, 4}, /* ELM_A3 0.875 */ + { 26.25, 14, 50, 4, 4}, /* ELM_A4 0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"EE", 2,11, 7,0x69,"i","i",ELM_FEATURE_FNT|ELM_FEATURE_HGH|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 250, 110, 50, 4, 4}, /* ELM_F1 -15 */ + { 2320, 1190, 50, 4, 4}, /* ELM_F2 30 */ + { 3200, 1580, 50, 4, 4}, /* ELM_F3 -20 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 33.25, 17.5, 50, 4, 4}, /* ELM_A2 0.875 */ + { 36.75, 17.5, 50, 4, 4}, /* ELM_A3 -0.875 */ + { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"ER", 2,16,16,0xCE,"3","V\"",ELM_FEATURE_CNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 580, 290, 50, 4, 4}, /* ELM_F1 0 */ + { 1420, 710, 50, 4, 4}, /* ELM_F2 0 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 45.5, 21, 50, 4, 4}, /* ELM_A2 -1.75 */ + { 33.25, 17.5, 50, 4, 4}, /* ELM_A3 0.875 */ + { 26.25, 14, 50, 4, 4}, /* ELM_A4 0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"AR", 2,15,15,0x41,"A","A",ELM_FEATURE_BCK|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 790, 410, 50, 4, 4}, /* ELM_F1 15 */ + { 880, 470, 50, 4, 4}, /* ELM_F2 30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 49, 24.5, 50, 4, 4}, /* ELM_A2 0 */ + { 29.75, 14, 50, 4, 4}, /* ELM_A3 -0.875 */ + { 22.75, 10.5, 50, 4, 4}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"AW", 2,16,10,0x8D,"O","O",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_RND|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 230, 50, 4, 4}, /* ELM_F1 -15 */ + { 820, 470, 50, 4, 4}, /* ELM_F2 60 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 45.5, 21, 50, 4, 4}, /* ELM_A2 -1.75 */ + { 22.75, 10.5, 50, 4, 4}, /* ELM_A3 -0.875 */ + { 17.5, 7, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"UU", 2,14, 9,0x75,"u","u",ELM_FEATURE_BCK|ELM_FEATURE_HGH|ELM_FEATURE_RND|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 250, 110, 50, 4, 4}, /* ELM_F1 -15 */ + { 880, 470, 50, 4, 4}, /* ELM_F2 30 */ + { 2200, 1100, 50, 4, 4}, /* ELM_F3 0 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 38.5, 17.5, 50, 4, 4}, /* ELM_A2 -1.75 */ + { 17.5, 7, 50, 4, 4}, /* ELM_A3 -1.75 */ + { 10.5, 3.5, 50, 4, 4}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"AI", 2, 9, 6,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 640, 290, 50, 5, 5}, /* ELM_F1 -30 */ + { 1600, 830, 50, 5, 5}, /* ELM_F2 30 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 45.5, 21, 50, 5, 5}, /* ELM_A2 -1.75 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */ + { 29.75, 14, 50, 5, 5}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"IE", 2, 9, 6,0x61,"a","a",ELM_FEATURE_CNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 790, 410, 50, 5, 5}, /* ELM_F1 15 */ + { 880, 470, 50, 5, 5}, /* ELM_F2 30 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 49, 24.5, 50, 5, 5}, /* ELM_A2 0 */ + { 29.75, 14, 50, 5, 5}, /* ELM_A3 -0.875 */ + { 22.75, 10.5, 50, 5, 5}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OI", 2, 9, 6,0x6F,"o","o",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_UMD|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 230, 50, 5, 5}, /* ELM_F1 -15 */ + { 820, 350, 50, 5, 5}, /* ELM_F2 -60 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 45.5, 21, 50, 5, 5}, /* ELM_A2 -1.75 */ + { 22.75, 10.5, 50, 5, 5}, /* ELM_A3 -0.875 */ + { 17.5, 7, 50, 5, 5}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OU", 2, 9, 6,0x61,"a","a",ELM_FEATURE_CNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 790, 410, 50, 5, 5}, /* ELM_F1 15 */ + { 1300, 590, 50, 5, 5}, /* ELM_F2 -60 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 47.25, 24.5, 50, 5, 5}, /* ELM_A2 0.875 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */ + { 28, 14, 50, 5, 5}, /* ELM_A4 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OV", 2, 8, 6,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 370, 170, 50, 4, 4}, /* ELM_F1 -15 */ + { 1000, 470, 50, 4, 4}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 42, 21, 50, 4, 4}, /* ELM_A2 0 */ + { 28, 14, 50, 4, 4}, /* ELM_A3 0 */ + { 22.75, 10.5, 50, 4, 4}, /* ELM_A4 -0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OA", 2, 9, 6,0xAB,"@","@",ELM_FEATURE_CNT|ELM_FEATURE_MDL|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 230, 50, 5, 5}, /* ELM_F1 -15 */ + { 1480, 710, 50, 5, 5}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A2 -0.875 */ + { 33.25, 17.5, 50, 5, 5}, /* ELM_A3 0.875 */ + { 26.25, 14, 50, 5, 5}, /* ELM_A4 0.875 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"IA", 2, 9, 6,0x49,"I","I",ELM_FEATURE_FNT|ELM_FEATURE_SMH|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 310, 170, 50, 5, 5}, /* ELM_F1 15 */ + { 2200, 1070, 50, 5, 5}, /* ELM_F2 -30 */ + { 2920, 1460, 50, 5, 5}, /* ELM_F3 0 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 35, 17.5, 50, 5, 5}, /* ELM_A2 0 */ + { 36.75, 17.5, 50, 5, 5}, /* ELM_A3 -0.875 */ + { 31.5, 14, 50, 5, 5}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"IB", 2, 8, 6,0x51,"@","@",ELM_FEATURE_FNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 230, 50, 4, 4}, /* ELM_F1 -15 */ + { 1480, 710, 50, 4, 4}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */ + { 60, 30, 50, 4, 4}, /* ELM_B1 0 */ + { 90, 45, 50, 4, 4}, /* ELM_B2 0 */ + { 150, 75, 50, 4, 4}, /* ELM_B3 0 */ + { -30, 0, 100, 4, 4}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */ + { 50.75, 24.5, 50, 4, 4}, /* ELM_A2 -0.875 */ + { 33.25, 17.5, 50, 4, 4}, /* ELM_A3 0.875 */ + { 26.25, 14, 50, 4, 4}, /* ELM_A4 0.875 */ + { -30, -15, 50, 4, 4}, /* ELM_A5 0 */ + { -30, -15, 50, 4, 4}, /* ELM_A6 0 */ + { -30, -15, 50, 4, 4}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"AIR", 2, 9, 6,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 640, 350, 50, 5, 5}, /* ELM_F1 30 */ + { 2020, 1070, 50, 5, 5}, /* ELM_F2 60 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 42, 21, 50, 5, 5}, /* ELM_A2 0 */ + { 38.5, 17.5, 50, 5, 5}, /* ELM_A3 -1.75 */ + { 31.5, 14, 50, 5, 5}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OOR", 2, 9, 6,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 370, 170, 50, 5, 5}, /* ELM_F1 -15 */ + { 1000, 470, 50, 5, 5}, /* ELM_F2 -30 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 42, 21, 50, 5, 5}, /* ELM_A2 0 */ + { 28, 14, 50, 5, 5}, /* ELM_A3 0 */ + { 22.75, 7, 50, 5, 5}, /* ELM_A4 -4.375 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +}, + +{"OR", 2, 9, 6,0x8D,"O","O",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_RND|ELM_FEATURE_VWL, + { + { 270, 135, 50, 0, 0}, /* ELM_FN 0 */ + { 490, 230, 50, 5, 5}, /* ELM_F1 -15 */ + { 820, 470, 50, 5, 5}, /* ELM_F2 60 */ + { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */ + { 60, 30, 50, 5, 5}, /* ELM_B1 0 */ + { 90, 45, 50, 5, 5}, /* ELM_B2 0 */ + { 150, 75, 50, 5, 5}, /* ELM_B3 0 */ + { -30, 0, 100, 5, 5}, /* ELM_AN 0 */ + { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */ + { 45.5, 21, 50, 5, 5}, /* ELM_A2 -1.75 */ + { 22.75, 10.5, 50, 5, 5}, /* ELM_A3 -0.875 */ + { 17.5, 7, 50, 5, 5}, /* ELM_A4 -1.75 */ + { -30, -15, 50, 5, 5}, /* ELM_A5 0 */ + { -30, -15, 50, 5, 5}, /* ELM_A6 0 */ + { -30, -15, 50, 5, 5}, /* ELM_AB 0 */ + { 62, 31, 50, 0, 0}, /* ELM_AV 0 */ + { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */ + { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */ + { 0, 0, 50, 0, 0} /* ELM_AF 0 */ + } +} + diff --git a/third_party/soloud_speech/darray.cpp b/third_party/soloud_speech/darray.cpp new file mode 100644 index 0000000..cd816fa --- /dev/null +++ b/third_party/soloud_speech/darray.cpp @@ -0,0 +1,70 @@ +#include +#include +#include "darray.h" + +darray::darray() +{ + mAllocChunk = 128; + mAllocated = mUsed = 0; + mData = NULL; +} + +void darray::clear() +{ + free(mData); + mAllocChunk = 128; + mAllocated = mUsed = 0; + mData = NULL; +} + +darray::~darray() +{ + clear(); +} + +char * darray::getDataInPos(int aPosition) +{ + if (aPosition < mAllocated && aPosition < mUsed) + return mData + aPosition; + + if (aPosition >= mAllocated) + { + int newsize = mAllocated; + + while (newsize <= aPosition) + { + newsize += mAllocChunk; + mAllocChunk *= 2; + } + + char *newdata = (char*)realloc(mData, newsize); + if (!newdata) + { + free(mData); + mData = NULL; + mAllocated = mUsed = 0; + return NULL; + } + else + { + memset(newdata + mAllocated, 0, newsize - mAllocated); + } + + mData = newdata; + mAllocated = newsize; + } + + if (aPosition >= mUsed) + { + mUsed = aPosition + 1; + } + + return mData + aPosition; +} + +void darray::put(int aData) +{ + char *s = getDataInPos(mUsed); + + *s = (char)aData; +} diff --git a/third_party/soloud_speech/darray.h b/third_party/soloud_speech/darray.h new file mode 100644 index 0000000..d3251fb --- /dev/null +++ b/third_party/soloud_speech/darray.h @@ -0,0 +1,22 @@ +#if !defined(DARRAY_H) +#define DARRAY_H + +class darray +{ +protected: + char *mData; + int mUsed; + int mAllocated; + int mAllocChunk; +public: + darray(); + ~darray(); + void clear(); + char *getDataInPos(int aPosition); + void put(int aData); + int getSize() const { return mUsed; } + char *getData() { return mData; } +}; + +#endif + diff --git a/third_party/soloud_speech/klatt.cpp b/third_party/soloud_speech/klatt.cpp new file mode 100644 index 0000000..c9572f0 --- /dev/null +++ b/third_party/soloud_speech/klatt.cpp @@ -0,0 +1,1074 @@ +#include +#include +#include "klatt.h" +#include "darray.h" +#include "resonator.h" + +#ifndef PI +#define PI 3.1415926535897932384626433832795f +#endif + +#ifndef NULL +#define NULL 0 +#endif + +class Interp +{ +public: + float mSteady; + float mFixed; + char mProportion; + char mExtDelay; + char mIntDelay; +}; + + +enum Eparm_e +{ + ELM_FN, ELM_F1, ELM_F2, ELM_F3, + ELM_B1, ELM_B2, ELM_B3, ELM_AN, + ELM_A1, ELM_A2, ELM_A3, ELM_A4, + ELM_A5, ELM_A6, ELM_AB, ELM_AV, + ELM_AVC, ELM_ASP, ELM_AF, + ELM_COUNT +}; + +class Element +{ +public: + const char *mName; // unused + const char mRK; + const char mDU; + const char mUD; + unsigned char mFont; // unused + const char *mDict; // unused + const char *mIpa; // unused + int mFeat; // only ELM_FEATURE_VWL + Interp mInterpolator[ELM_COUNT]; + }; + +enum ELEMENT_FEATURES +{ + ELM_FEATURE_ALV = 0x00000001, + ELM_FEATURE_APR = 0x00000002, + ELM_FEATURE_BCK = 0x00000004, + ELM_FEATURE_BLB = 0x00000008, + ELM_FEATURE_CNT = 0x00000010, + ELM_FEATURE_DNT = 0x00000020, + ELM_FEATURE_FNT = 0x00000040, + ELM_FEATURE_FRC = 0x00000080, + ELM_FEATURE_GLT = 0x00000100, + ELM_FEATURE_HGH = 0x00000200, + ELM_FEATURE_LAT = 0x00000400, + ELM_FEATURE_LBD = 0x00000800, + ELM_FEATURE_LBV = 0x00001000, + ELM_FEATURE_LMD = 0x00002000, + ELM_FEATURE_LOW = 0x00004000, + ELM_FEATURE_MDL = 0x00008000, + ELM_FEATURE_NAS = 0x00010000, + ELM_FEATURE_PAL = 0x00020000, + ELM_FEATURE_PLA = 0x00040000, + ELM_FEATURE_RND = 0x00080000, + ELM_FEATURE_RZD = 0x00100000, + ELM_FEATURE_SMH = 0x00200000, + ELM_FEATURE_STP = 0x00400000, + ELM_FEATURE_UMD = 0x00800000, + ELM_FEATURE_UNR = 0x01000000, + ELM_FEATURE_VCD = 0x02000000, + ELM_FEATURE_VEL = 0x04000000, + ELM_FEATURE_VLS = 0x08000000, + ELM_FEATURE_VWL = 0x10000000 +}; + +enum ELEMENTS +{ + ELM_END = 0, + ELM_Q, ELM_P, ELM_PY, ELM_PZ, ELM_T, ELM_TY, + ELM_TZ, ELM_K, ELM_KY, ELM_KZ, ELM_B, ELM_BY, ELM_BZ, + ELM_D, ELM_DY, ELM_DZ, ELM_G, ELM_GY, ELM_GZ, ELM_M, + ELM_N, ELM_NG, ELM_F, ELM_TH, ELM_S, ELM_SH, ELM_X, + ELM_H, ELM_V, ELM_QQ, ELM_DH, ELM_DI, ELM_Z, ELM_ZZ, + ELM_ZH, ELM_CH, ELM_CI, ELM_J, ELM_JY, ELM_L, ELM_LL, + ELM_RX, ELM_R, ELM_W, ELM_Y, ELM_I, ELM_E, ELM_AA, + ELM_U, ELM_O, ELM_OO, ELM_A, ELM_EE, ELM_ER, ELM_AR, + ELM_AW, ELM_UU, ELM_AI, ELM_IE, ELM_OI, ELM_OU, ELM_OV, + ELM_OA, ELM_IA, ELM_IB, ELM_AIR,ELM_OOR,ELM_OR +}; + +#define PHONEME_COUNT 53 +#define AMP_ADJ 14 +#define StressDur(e,s) (s,((e->mDU + e->mUD)/2)) + + + + +class PhonemeToElements +{ +public: + int mKey; + char mData[8]; +}; + +/* Order is important - 2 byte phonemes first, otherwise + the search function will fail*/ +static PhonemeToElements phoneme_to_elements[PHONEME_COUNT] = +{ + /* mKey, count, 0-7 elements */ +/* tS */ 0x5374, 2, ELM_CH, ELM_CI, 0, 0, 0, 0, 0, +/* dZ */ 0x5a64, 4, ELM_J, ELM_JY, ELM_QQ, ELM_JY, 0, 0, 0, +/* rr */ 0x7272, 3, ELM_R, ELM_QQ, ELM_R, 0, 0, 0, 0, +/* eI */ 0x4965, 2, ELM_AI, ELM_I, 0, 0, 0, 0, 0, +/* aI */ 0x4961, 2, ELM_IE, ELM_I, 0, 0, 0, 0, 0, +/* oI */ 0x496f, 2, ELM_OI, ELM_I, 0, 0, 0, 0, 0, +/* aU */ 0x5561, 2, ELM_OU, ELM_OV, 0, 0, 0, 0, 0, +/* @U */ 0x5540, 2, ELM_OA, ELM_OV, 0, 0, 0, 0, 0, +/* I@ */ 0x4049, 2, ELM_IA, ELM_IB, 0, 0, 0, 0, 0, +/* e@ */ 0x4065, 2, ELM_AIR, ELM_IB, 0, 0, 0, 0, 0, +/* U@ */ 0x4055, 2, ELM_OOR, ELM_IB, 0, 0, 0, 0, 0, +/* O@ */ 0x404f, 2, ELM_OR, ELM_IB, 0, 0, 0, 0, 0, +/* oU */ 0x556f, 2, ELM_OI, ELM_OV, 0, 0, 0, 0, 0, +/* */ 0x0020, 1, ELM_Q, 0, 0, 0, 0, 0, 0, +/* p */ 0x0070, 3, ELM_P, ELM_PY, ELM_PZ, 0, 0, 0, 0, +/* t */ 0x0074, 3, ELM_T, ELM_TY, ELM_TZ, 0, 0, 0, 0, +/* k */ 0x006b, 3, ELM_K, ELM_KY, ELM_KZ, 0, 0, 0, 0, +/* b */ 0x0062, 3, ELM_B, ELM_BY, ELM_BZ, 0, 0, 0, 0, +/* d */ 0x0064, 3, ELM_D, ELM_DY, ELM_DZ, 0, 0, 0, 0, +/* g */ 0x0067, 3, ELM_G, ELM_GY, ELM_GZ, 0, 0, 0, 0, +/* m */ 0x006d, 1, ELM_M, 0, 0, 0, 0, 0, 0, +/* n */ 0x006e, 1, ELM_N, 0, 0, 0, 0, 0, 0, +/* N */ 0x004e, 1, ELM_NG, 0, 0, 0, 0, 0, 0, +/* f */ 0x0066, 1, ELM_F, 0, 0, 0, 0, 0, 0, +/* T */ 0x0054, 1, ELM_TH, 0, 0, 0, 0, 0, 0, +/* s */ 0x0073, 1, ELM_S, 0, 0, 0, 0, 0, 0, +/* S */ 0x0053, 1, ELM_SH, 0, 0, 0, 0, 0, 0, +/* h */ 0x0068, 1, ELM_H, 0, 0, 0, 0, 0, 0, +/* v */ 0x0076, 3, ELM_V, ELM_QQ, ELM_V, 0, 0, 0, 0, +/* D */ 0x0044, 3, ELM_DH, ELM_QQ, ELM_DI, 0, 0, 0, 0, +/* z */ 0x007a, 3, ELM_Z, ELM_QQ, ELM_ZZ, 0, 0, 0, 0, +/* Z */ 0x005a, 3, ELM_ZH, ELM_QQ, ELM_ZH, 0, 0, 0, 0, +/* l */ 0x006c, 1, ELM_L, 0, 0, 0, 0, 0, 0, +/* r */ 0x0072, 1, ELM_R, 0, 0, 0, 0, 0, 0, +/* R */ 0x0052, 1, ELM_RX, 0, 0, 0, 0, 0, 0, +/* w */ 0x0077, 1, ELM_W, 0, 0, 0, 0, 0, 0, +/* x */ 0x0078, 1, ELM_X, 0, 0, 0, 0, 0, 0, +/* % */ 0x0025, 1, ELM_QQ, 0, 0, 0, 0, 0, 0, +/* j */ 0x006a, 1, ELM_Y, 0, 0, 0, 0, 0, 0, +/* I */ 0x0049, 1, ELM_I, 0, 0, 0, 0, 0, 0, +/* e */ 0x0065, 1, ELM_E, 0, 0, 0, 0, 0, 0, +/* & */ 0x0026, 1, ELM_AA, 0, 0, 0, 0, 0, 0, +/* V */ 0x0056, 1, ELM_U, 0, 0, 0, 0, 0, 0, +/* 0 */ 0x0030, 1, ELM_O, 0, 0, 0, 0, 0, 0, +/* U */ 0x0055, 1, ELM_OO, 0, 0, 0, 0, 0, 0, +/* @ */ 0x0040, 1, ELM_A, 0, 0, 0, 0, 0, 0, +/* i */ 0x0069, 1, ELM_EE, 0, 0, 0, 0, 0, 0, +/* 3 */ 0x0033, 1, ELM_ER, 0, 0, 0, 0, 0, 0, +/* A */ 0x0041, 1, ELM_AR, 0, 0, 0, 0, 0, 0, +/* O */ 0x004f, 1, ELM_AW, 0, 0, 0, 0, 0, 0, +/* u */ 0x0075, 1, ELM_UU, 0, 0, 0, 0, 0, 0, +/* o */ 0x006f, 1, ELM_OI, 0, 0, 0, 0, 0, 0, +/* . */ 0x002e, 1, ELM_END,0, 0, 0, 0, 0, 0, +}; + +static Element gElement[] = +{ +#include "Elements.def" +}; + +static short clip(float input) +{ + int temp = (int)input; + /* clip on boundaries of 16-bit word */ + + if (temp < -32767) + { + //assert? + temp = -32767; + } + else + if (temp > 32767) + { + //assert? + temp = 32767; + } + + return (short)(temp); +} + +/* Convert from decibels to a linear scale factor */ +static float DBtoLIN(int dB) +{ + /* + * Convertion table, db to linear, 87 dB --> 32767 + * 86 dB --> 29491 (1 dB down = 0.5**1/6) + * ... + * 81 dB --> 16384 (6 dB down = 0.5) + * ... + * 0 dB --> 0 + * + * The just noticeable difference for a change in intensity of a vowel + * is approximately 1 dB. Thus all amplitudes are quantized to 1 dB + * steps. + */ + + static const float amptable[88] = + { + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 6.0, 7.0, + 8.0, 9.0, 10.0, 11.0, 13.0, + 14.0, 16.0, 18.0, 20.0, 22.0, + 25.0, 28.0, 32.0, 35.0, 40.0, + 45.0, 51.0, 57.0, 64.0, 71.0, + 80.0, 90.0, 101.0, 114.0, 128.0, + 142.0, 159.0, 179.0, 202.0, 227.0, + 256.0, 284.0, 318.0, 359.0, 405.0, + 455.0, 512.0, 568.0, 638.0, 719.0, + 811.0, 911.0, 1024.0, 1137.0, 1276.0, + 1438.0, 1622.0, 1823.0, 2048.0, 2273.0, + 2552.0, 2875.0, 3244.0, 3645.0, 4096.0, + 4547.0, 5104.0, 5751.0, 6488.0, 7291.0, + 8192.0, 9093.0, 10207.0, 11502.0, 12976.0, + 14582.0, 16384.0, 18350.0, 20644.0, 23429.0, + 26214.0, 29491.0, 32767.0 + }; + + // Check limits or argument (can be removed in final product) + if (dB < 0) + { + dB = 0; + } + else + if (dB >= 88) + { + dB = 87; + } + + return amptable[dB] * 0.001f; +} + + + +klatt_frame::klatt_frame() : + mF0FundamentalFreq(1330), mVoicingAmpdb(60), mFormant1Freq(500), + mFormant1Bandwidth(60), mFormant2Freq(1500), mFormant2Bandwidth(90), + mFormant3Freq(2800), mFormant3Bandwidth(150), mFormant4Freq(3250), + mFormant4Bandwidth(200), mFormant5Freq(3700), mFormant5Bandwidth(200), + mFormant6Freq(4990), mFormant6Bandwidth(500), mNasalZeroFreq(270), + mNasalZeroBandwidth(100), mNasalPoleFreq(270), mNasalPoleBandwidth(100), + mAspirationAmpdb(0), mNoSamplesInOpenPeriod(30), mVoicingBreathiness(0), + mVoicingSpectralTiltdb(10), mFricationAmpdb(0), mSkewnessOfAlternatePeriods(0), + mFormant1Ampdb(0), mFormant1ParallelBandwidth(80), mFormant2Ampdb(0), + mFormant2ParallelBandwidth(200), mFormant3Ampdb(0), mFormant3ParallelBandwidth(350), + mFormant4Ampdb(0), mFormant4ParallelBandwidth(500), mFormant5Ampdb(0), + mFormant5ParallelBandwidth(600), mFormant6Ampdb(0), mFormant6ParallelBandwidth(800), + mParallelNasalPoleAmpdb(0), mBypassFricationAmpdb(0), mPalallelVoicingAmpdb(0), + mOverallGaindb(62) +{ +}; + + +klatt::klatt() : + mBaseF0(1330), + mBaseSpeed(10.0f), + mBaseDeclination(0.5f), + mBaseWaveform(KW_SAW), + mF0Flutter(0), + mSampleRate(0), + mNspFr(0), + mF0FundamentalFreq(0), + mVoicingAmpdb(0), + mSkewnessOfAlternatePeriods(0), + mTimeCount(0), + mNPer(0), + mT0(0), + mNOpen(0), + mNMod(0), + mAmpVoice(0), + mAmpBypas(0), + mAmpAspir(0), + mAmpFrica(0), + mAmpBreth(0), + mSkew(0), + mVLast(0), + mNLast(0), + mGlotLast(0), + mDecay(0), + mOneMd(0), + mSeed(5), + mElementCount(0), + mElement(0), + mElementIndex(0), + mLastElement(0), + mTStress(0), + mNTStress(0), + mTop(0) +{ +} + +/* +function FLUTTER + +This function adds F0 flutter, as specified in: + +"Analysis, synthesis and perception of voice quality variations among +female and male talkers" D.H. Klatt and L.C. Klatt JASA 87(2) February 1990. +Flutter is added by applying a quasi-random element constructed from three +slowly varying sine waves. +*/ +void klatt::flutter() +{ + int original_f0 = mFrame.mF0FundamentalFreq / 10; + float fla = (float) mF0Flutter / 50; + float flb = (float) original_f0 / 100; + float flc = (float)sin(2 * PI * 12.7 * mTimeCount); + float fld = (float)sin(2 * PI * 7.1 * mTimeCount); + float fle = (float)sin(2 * PI * 4.7 * mTimeCount); + float delta_f0 = fla * flb * (flc + fld + fle) * 10; + mF0FundamentalFreq += (int) delta_f0; +} + +/* Vwave is the differentiated glottal flow waveform, there is a weak +spectral zero around 800 Hz, magic constants a,b reset pitch-synch +*/ + +float klatt::natural_source(int aNper) +{ + // See if glottis open + if (aNper < mNOpen) + { + switch (mBaseWaveform) + { + case KW_TRIANGLE: + return ((aNper % 200) - 100) * 81.92f; // triangle + case KW_SIN: + return (float)(sin(aNper * 0.0314) * 8192); // sin + case KW_SQUARE: + return ((aNper % 200) - 100) > 0 ? 8192.0f : -8192.0f; // square + case KW_PULSE: + return ((aNper % 200) - 100) > 50 ? 8192.0f : -8192.0f; // pulse + case KW_NOISE: + return (int)mNLast & 1 ? -8192.0f : 8192.0f; + case KW_WARBLE: + return (int)mNLast & 7 ? -8192.0f : 8192.0f; + case KW_SAW: // fallthrough + default: + return (abs((aNper % 200) - 100) - 50) * 163.84f; // saw + } + } + else + { + // Glottis closed + return (0.0); + } + +} + +/* Reset selected parameters pitch-synchronously */ + +void klatt::pitch_synch_par_reset(int ns) +{ + if (mF0FundamentalFreq > 0) + { + mT0 = (40 * mSampleRate) / mF0FundamentalFreq; + + /* Period in samp*4 */ + mAmpVoice = DBtoLIN(mVoicingAmpdb); + + /* Duration of period before amplitude modulation */ + mNMod = mT0; + + if (mVoicingAmpdb > 0) + { + mNMod >>= 1; + } + + /* Breathiness of voicing waveform */ + + mAmpBreth = DBtoLIN(mFrame.mVoicingBreathiness) * 0.1f; + + /* Set open phase of glottal period */ + /* where 40 <= open phase <= 263 */ + + mNOpen = 4 * mFrame.mNoSamplesInOpenPeriod; + + if (mNOpen >= (mT0 - 1)) + { + mNOpen = mT0 - 2; + } + + if (mNOpen < 40) + { + mNOpen = 40; /* F0 max = 1000 Hz */ + } + + int temp; + float temp1; + + temp = mSampleRate / mNOpen; + mCritDampedGlotLowPassFilter.initResonator(0L, temp, mSampleRate); + + /* Make gain at F1 about constant */ + + temp1 = mNOpen * .00833f; + mCritDampedGlotLowPassFilter.setGain(temp1 * temp1); + + /* Truncate skewness so as not to exceed duration of closed phase + of glottal period */ + + temp = mT0 - mNOpen; + + if (mSkewnessOfAlternatePeriods > temp) + { + mSkewnessOfAlternatePeriods = temp; + } + + if (mSkew >= 0) + { + mSkew = mSkewnessOfAlternatePeriods; /* Reset mSkew to requested mSkewnessOfAlternatePeriods */ + } + else + { + mSkew = -mSkewnessOfAlternatePeriods; + } + + /* Add skewness to closed portion of voicing period */ + + mT0 = mT0 + mSkew; + mSkew = -mSkew; + } + else + { + mT0 = 4; /* Default for f0 undefined */ + mAmpVoice = 0.0; + mNMod = mT0; + mAmpBreth = 0.0; + } + + /* Reset these pars pitch synchronously or at update rate if f0=0 */ + + if ((mT0 != 4) || (ns == 0)) + { + /* Set one-pole ELM_FEATURE_LOW-pass filter that tilts glottal source */ + mDecay = (0.033f * mFrame.mVoicingSpectralTiltdb); /* Function of samp_rate ? */ + + if (mDecay > 0.0f) + { + mOneMd = 1.0f - mDecay; + } + else + { + mOneMd = 1.0f; + } + } +} + + +/* Get variable parameters from host computer, +initially also get definition of fixed pars +*/ + +void klatt::frame_init() +{ + int mOverallGaindb; /* Overall gain, 60 dB is unity 0 to 60 */ + float amp_parF1; /* mFormant1Ampdb converted to linear gain */ + float amp_parFN; /* mParallelNasalPoleAmpdb converted to linear gain */ + float amp_parF2; /* mFormant2Ampdb converted to linear gain */ + float amp_parF3; /* mFormant3Ampdb converted to linear gain */ + float amp_parF4; /* mFormant4Ampdb converted to linear gain */ + float amp_parF5; /* mFormant5Ampdb converted to linear gain */ + float amp_parF6; /* mFormant6Ampdb converted to linear gain */ + + /* Read speech frame definition into temp store + and move some parameters into active use immediately + (voice-excited ones are updated pitch synchronously + to avoid waveform glitches). + */ + + mF0FundamentalFreq = mFrame.mF0FundamentalFreq; + mVoicingAmpdb = mFrame.mVoicingAmpdb - 7; + + if (mVoicingAmpdb < 0) mVoicingAmpdb = 0; + + mAmpAspir = DBtoLIN(mFrame.mAspirationAmpdb) * .05f; + mAmpFrica = DBtoLIN(mFrame.mFricationAmpdb) * 0.25f; + mSkewnessOfAlternatePeriods = mFrame.mSkewnessOfAlternatePeriods; + + /* Fudge factors (which comprehend affects of formants on each other?) + with these in place ALL_PARALLEL should sound as close as + possible to CASCADE_PARALLEL. + Possible problem feeding in Holmes's amplitudes given this. + */ + amp_parF1 = DBtoLIN(mFrame.mFormant1Ampdb) * 0.4f; /* -7.96 dB */ + amp_parF2 = DBtoLIN(mFrame.mFormant2Ampdb) * 0.15f; /* -16.5 dB */ + amp_parF3 = DBtoLIN(mFrame.mFormant3Ampdb) * 0.06f; /* -24.4 dB */ + amp_parF4 = DBtoLIN(mFrame.mFormant4Ampdb) * 0.04f; /* -28.0 dB */ + amp_parF5 = DBtoLIN(mFrame.mFormant5Ampdb) * 0.022f; /* -33.2 dB */ + amp_parF6 = DBtoLIN(mFrame.mFormant6Ampdb) * 0.03f; /* -30.5 dB */ + amp_parFN = DBtoLIN(mFrame.mParallelNasalPoleAmpdb) * 0.6f; /* -4.44 dB */ + mAmpBypas = DBtoLIN(mFrame.mBypassFricationAmpdb) * 0.05f; /* -26.0 db */ + + // Set coeficients of nasal resonator and zero antiresonator + mNasalPole.initResonator(mFrame.mNasalPoleFreq, mFrame.mNasalPoleBandwidth, mSampleRate); + + mNasalZero.initAntiresonator(mFrame.mNasalZeroFreq, mFrame.mNasalZeroBandwidth, mSampleRate); + + // Set coefficients of parallel resonators, and amplitude of outputs + mParallelFormant1.initResonator(mFrame.mFormant1Freq, mFrame.mFormant1ParallelBandwidth, mSampleRate); + mParallelFormant1.setGain(amp_parF1); + + mParallelResoNasalPole.initResonator(mFrame.mNasalPoleFreq, mFrame.mNasalPoleBandwidth, mSampleRate); + mParallelResoNasalPole.setGain(amp_parFN); + + mParallelFormant2.initResonator(mFrame.mFormant2Freq, mFrame.mFormant2ParallelBandwidth, mSampleRate); + mParallelFormant2.setGain(amp_parF2); + + mParallelFormant3.initResonator(mFrame.mFormant3Freq, mFrame.mFormant3ParallelBandwidth, mSampleRate); + mParallelFormant3.setGain(amp_parF3); + + mParallelFormant4.initResonator(mFrame.mFormant4Freq, mFrame.mFormant4ParallelBandwidth, mSampleRate); + mParallelFormant4.setGain(amp_parF4); + + mParallelFormant5.initResonator(mFrame.mFormant5Freq, mFrame.mFormant5ParallelBandwidth, mSampleRate); + mParallelFormant5.setGain(amp_parF5); + + mParallelFormant6.initResonator(mFrame.mFormant6Freq, mFrame.mFormant6ParallelBandwidth, mSampleRate); + mParallelFormant6.setGain(amp_parF6); + + + /* fold overall gain into output resonator */ + mOverallGaindb = mFrame.mOverallGaindb - 3; + + if (mOverallGaindb <= 0) + mOverallGaindb = 57; + + /* output ELM_FEATURE_LOW-pass filter - resonator with freq 0 and BW = globals->mSampleRate + Thus 3db point is globals->mSampleRate/2 i.e. Nyquist limit. + Only 3db down seems rather mild... + */ + mOutputLowPassFilter.initResonator(0L, (int)mSampleRate, mSampleRate); + mOutputLowPassFilter.setGain(DBtoLIN(mOverallGaindb)); +} + +/* +function PARWAV + +CONVERT FRAME OF PARAMETER DATA TO A WAVEFORM CHUNK +Synthesize globals->mNspFr samples of waveform and store in jwave[]. +*/ + +void klatt::parwave(short int *jwave) +{ + /* Output of cascade branch, also final output */ + + /* Initialize synthesizer and get specification for current speech + frame from host microcomputer */ + + frame_init(); + + if (mF0Flutter != 0) + { + mTimeCount++; /* used for f0 flutter */ + flutter(); /* add f0 flutter */ + } + + /* MAIN LOOP, for each output sample of current frame: */ + + int ns; + for (ns = 0; ns < mNspFr; ns++) + { + float noise; + int n4; + float sourc; /* Sound source if all-parallel config used */ + float glotout; /* Output of glottal sound source */ + float par_glotout; /* Output of parallelglottal sound sourc */ + float voice = 0; /* Current sample of voicing waveform */ + float frics; /* Frication sound source */ + float aspiration; /* Aspiration sound source */ + int nrand; /* Varible used by random number generator */ + + /* Our own code like rand(), but portable + whole upper 31 bits of seed random + assumes 32-bit unsigned arithmetic + with untested code to handle larger. + */ + mSeed = mSeed * 1664525 + 1; + + mSeed &= 0xFFFFFFFF; + + /* Shift top bits of seed up to top of int then back down to LS 14 bits */ + /* Assumes 8 bits per sizeof unit i.e. a "byte" */ + nrand = (((int) mSeed) << (8 * sizeof(int) - 32)) >> (8 * sizeof(int) - 14); + + /* Tilt down noise spectrum by soft ELM_FEATURE_LOW-pass filter having + * a pole near the origin in the z-plane, i.e. + * output = input + (0.75 * lastoutput) */ + + noise = nrand + (0.75f * mNLast); /* Function of samp_rate ? */ + + mNLast = noise; + + /* Amplitude modulate noise (reduce noise amplitude during + second half of glottal period) if voicing simultaneously present + */ + + if (mNPer > mNMod) + { + noise *= 0.5f; + } + + /* Compute frication noise */ + sourc = frics = mAmpFrica * noise; + + /* Compute voicing waveform : (run glottal source simulation at + 4 times normal sample rate to minimize quantization noise in + period of female voice) + */ + + for (n4 = 0; n4 < 4; n4++) + { + /* use a more-natural-shaped source waveform with excitation + occurring both upon opening and upon closure, stronest at closure */ + voice = natural_source(mNPer); + + /* Reset period when counter 'mNPer' reaches mT0 */ + + if (mNPer >= mT0) + { + mNPer = 0; + pitch_synch_par_reset(ns); + } + + /* Low-pass filter voicing waveform before downsampling from 4*globals->mSampleRate */ + /* to globals->mSampleRate samples/sec. Resonator f=.09*globals->mSampleRate, bw=.06*globals->mSampleRate */ + + voice = mDownSampLowPassFilter.resonate(voice); /* in=voice, out=voice */ + + /* Increment counter that keeps track of 4*globals->mSampleRate samples/sec */ + mNPer++; + } + + /* Tilt spectrum of voicing source down by soft ELM_FEATURE_LOW-pass filtering, amount + of tilt determined by mVoicingSpectralTiltdb + */ + voice = (voice * mOneMd) + (mVLast * mDecay); + + mVLast = voice; + + /* Add breathiness during glottal open phase */ + if (mNPer < mNOpen) + { + /* Amount of breathiness determined by parameter mVoicingBreathiness */ + /* Use nrand rather than noise because noise is ELM_FEATURE_LOW-passed */ + voice += mAmpBreth * nrand; + } + + /* Set amplitude of voicing */ + glotout = mAmpVoice * voice; + + /* Compute aspiration amplitude and add to voicing source */ + aspiration = mAmpAspir * noise; + + glotout += aspiration; + + par_glotout = glotout; + + /* NIS - rsynth "hack" + As Holmes' scheme is weak at nasals and (physically) nasal cavity + is "back near glottis" feed glottal source through nasal resonators + Don't think this is quite right, but improves things a bit + */ + par_glotout = mNasalZero.antiresonate(par_glotout); + par_glotout = mNasalPole.resonate(par_glotout); + /* And just use mParallelFormant1 NOT mParallelResoNasalPole */ + float out = mParallelFormant1.resonate(par_glotout); + /* Sound sourc for other parallel resonators is frication + plus first difference of voicing waveform. + */ + sourc += (par_glotout - mGlotLast); + mGlotLast = par_glotout; + + /* Standard parallel vocal tract + Formants F6,F5,F4,F3,F2, outputs added with alternating sign + */ + out = mParallelFormant6.resonate(sourc) - out; + out = mParallelFormant5.resonate(sourc) - out; + out = mParallelFormant4.resonate(sourc) - out; + out = mParallelFormant3.resonate(sourc) - out; + out = mParallelFormant2.resonate(sourc) - out; + + out = mAmpBypas * sourc - out; + out = mOutputLowPassFilter.resonate(out); + + *jwave++ = clip(out); /* Convert back to integer */ + } +} + + + +static char * phoneme_to_element_lookup(char *s, void ** data) +{ + int key8 = *s; + int key16 = key8 + (s[1] << 8); + if (s[1] == 0) key16 = -1; // avoid key8==key16 + int i; + for (i = 0; i < PHONEME_COUNT; i++) + { + if (phoneme_to_elements[i].mKey == key16) + { + *data = &phoneme_to_elements[i].mData; + return s+2; + } + if (phoneme_to_elements[i].mKey == key8) + { + *data = &phoneme_to_elements[i].mData; + return s+1; + } + } + // should never happen + *data = NULL; + return s+1; +} + + + +int klatt::phone_to_elm(char *aPhoneme, int aCount, darray *aElement) +{ + int stress = 0; + char *s = aPhoneme; + int t = 0; + char *limit = s + aCount; + + while (s < limit && *s) + { + char *e = NULL; + s = phoneme_to_element_lookup(s, (void**)&e); + + if (e) + { + int n = *e++; + + while (n-- > 0) + { + int x = *e++; + Element * p = &gElement[x]; + /* This works because only vowels have mUD != mDU, + and we set stress just before a vowel + */ + aElement->put(x); + + if (!(p->mFeat & ELM_FEATURE_VWL)) + stress = 0; + + int stressdur = StressDur(p,stress); + + t += stressdur; + + aElement->put(stressdur); + aElement->put(stress); + } + } + + else + { + char ch = *s++; + + switch (ch) + { + + case '\'': /* Primary stress */ + stress = 3; + break; + + case ',': /* Secondary stress */ + stress = 2; + break; + + case '+': /* Tertiary stress */ + stress = 1; + break; + + case '-': /* hyphen in input */ + break; + + default: +// fprintf(stderr, "Ignoring %c in '%.*s'\n", ch, aCount, aPhoneme); + break; + } + } + } + + return t; +} + + + +/* 'a' is dominant element, 'b' is dominated + ext is flag to say to use external times from 'a' rather + than internal i.e. ext != 0 if 'a' is NOT current element. + */ + +static void set_trans(Slope *t, Element * a, Element * b,int ext, int /* e */) +{ + int i; + + for (i = 0; i < ELM_COUNT; i++) + { + t[i].mTime = ((ext) ? a->mInterpolator[i].mExtDelay : a->mInterpolator[i].mIntDelay); + + if (t[i].mTime) + { + t[i].mValue = a->mInterpolator[i].mFixed + (a->mInterpolator[i].mProportion * b->mInterpolator[i].mSteady) * 0.01f; // mProportion is in scale 0..100, so *0.01. + } + else + { + t[i].mValue = b->mInterpolator[i].mSteady; + } + } +} + +static float lerp(float a, float b, int t, int d) +{ + if (t <= 0) + { + return a; + } + + if (t >= d) + { + return b; + } + + float f = (float)t / (float)d; + return a + (b - a) * f; +} + +static float interpolate(Slope *aStartSlope, Slope *aEndSlope, float aMidValue, int aTime, int aDuration) +{ + int steadyTime = aDuration - (aStartSlope->mTime + aEndSlope->mTime); + + if (steadyTime >= 0) + { + // Interpolate to a midpoint, stay there for a while, then interpolate to end + + if (aTime < aStartSlope->mTime) + { + // interpolate to the first value + return lerp(aStartSlope->mValue, aMidValue, aTime, aStartSlope->mTime); + } + // reached midpoint + + aTime -= aStartSlope->mTime; + + if (aTime <= steadyTime) + { + // still at steady state + return aMidValue; + } + + // interpolate to the end + return lerp(aMidValue, aEndSlope->mValue, aTime - steadyTime, aEndSlope->mTime); + } + else + { + // No steady state + float f = 1.0f - ((float) aTime / (float) aDuration); + float sp = lerp(aStartSlope->mValue, aMidValue, aTime, aStartSlope->mTime); + float ep = lerp(aEndSlope->mValue, aMidValue, aDuration - aTime, aEndSlope->mTime); + return f * sp + ((float) 1.0 - f) * ep; + } +} + + + +void klatt::initsynth(int aElementCount,unsigned char *aElement) +{ + mElement = aElement; + mElementCount = aElementCount; + mElementIndex = 0; + mLastElement = &gElement[0]; + mSeed = 5; + mTStress = 0; + mNTStress = 0; + mFrame.mF0FundamentalFreq = mBaseF0; + mTop = 1.1f * mFrame.mF0FundamentalFreq; + mFrame.mNasalPoleFreq = (int)mLastElement->mInterpolator[ELM_FN].mSteady; + mFrame.mFormant1ParallelBandwidth = mFrame.mFormant1Bandwidth = 60; + mFrame.mFormant2ParallelBandwidth = mFrame.mFormant2Bandwidth = 90; + mFrame.mFormant3ParallelBandwidth = mFrame.mFormant3Bandwidth = 150; +// mFrame.mFormant4ParallelBandwidth = (default) + + // Set stress attack/decay slope + mStressS.mTime = 40; + mStressE.mTime = 40; + mStressE.mValue = 0.0; +} + +int klatt::synth(int /* aSampleCount */, short *aSamplePointer) +{ + short *samp = aSamplePointer; + + if (mElementIndex >= mElementCount) + return -1; + + Element * currentElement = &gElement[mElement[mElementIndex++]]; + int dur = mElement[mElementIndex++]; + mElementIndex++; // skip stress + + if (currentElement->mRK == 31) // "END" + { + // Reset the fundamental frequency top + mFrame.mF0FundamentalFreq = mBaseF0; + mTop = 1.1f * mFrame.mF0FundamentalFreq; + } + + // Skip zero length elements which are only there to affect + // boundary values of adjacent elements + + if (dur > 0) + { + Element * ne = (mElementIndex < mElementCount) ? &gElement[mElement[mElementIndex]] : &gElement[0]; + Slope start[ELM_COUNT]; + Slope end[ELM_COUNT]; + int t; + + if (currentElement->mRK > mLastElement->mRK) + { + set_trans(start, currentElement, mLastElement, 0, 's'); + // we dominate last + } + else + { + set_trans(start, mLastElement, currentElement, 1, 's'); + // last dominates us + } + + if (ne->mRK > currentElement->mRK) + { + set_trans(end, ne, currentElement, 1, 'e'); + // next dominates us + } + else + { + set_trans(end, currentElement, ne, 0, 'e'); + // we dominate next + } + + for (t = 0; t < dur; t++, mTStress++) + { + float base = mTop * 0.8f; // 3 * top / 5 + float tp[ELM_COUNT]; + + if (mTStress == mNTStress) + { + int j = mElementIndex; + mStressS = mStressE; + mTStress = 0; + mNTStress = dur; + + while (j <= mElementCount) + { + Element * e = (j < mElementCount) ? &gElement[mElement[j++]] : &gElement[0]; + int du = (j < mElementCount) ? mElement[j++] : 0; + int s = (j < mElementCount) ? mElement[j++] : 3; + + if (s || e->mFeat & ELM_FEATURE_VWL) + { + int d = 0; + + if (s) + mStressE.mValue = (float) s / 3; + else + mStressE.mValue = (float) 0.1; + + do + { + d += du; + e = (j < mElementCount) ? &gElement[mElement[j++]] : &gElement[0]; + du = mElement[j++]; + } + + while ((e->mFeat & ELM_FEATURE_VWL) && mElement[j++] == s); + + mNTStress += d / 2; + + break; + } + + mNTStress += du; + } + } + + int j; + for (j = 0; j < ELM_COUNT; j++) + { + tp[j] = interpolate(&start[j], &end[j], (float) currentElement->mInterpolator[j].mSteady, t, dur); + } + + // Now call the synth for each frame + + mFrame.mF0FundamentalFreq = (int)(base + (mTop - base) * interpolate(&mStressS, &mStressE, (float)0, mTStress, mNTStress)); + mFrame.mVoicingAmpdb = mFrame.mPalallelVoicingAmpdb = (int)tp[ELM_AV]; + mFrame.mFricationAmpdb = (int)tp[ELM_AF]; + mFrame.mNasalZeroFreq = (int)tp[ELM_FN]; + mFrame.mAspirationAmpdb = (int)tp[ELM_ASP]; + mFrame.mVoicingBreathiness = (int)tp[ELM_AVC]; + mFrame.mFormant1ParallelBandwidth = mFrame.mFormant1Bandwidth = (int)tp[ELM_B1]; + mFrame.mFormant2ParallelBandwidth = mFrame.mFormant2Bandwidth = (int)tp[ELM_B2]; + mFrame.mFormant3ParallelBandwidth = mFrame.mFormant3Bandwidth = (int)tp[ELM_B3]; + mFrame.mFormant1Freq = (int)tp[ELM_F1]; + mFrame.mFormant2Freq = (int)tp[ELM_F2]; + mFrame.mFormant3Freq = (int)tp[ELM_F3]; + + // AMP_ADJ + is a kludge to get amplitudes up to klatt-compatible levels + + + //pars.mParallelNasalPoleAmpdb = AMP_ADJ + tp[ELM_AN]; + + mFrame.mBypassFricationAmpdb = AMP_ADJ + (int)tp[ELM_AB]; + mFrame.mFormant5Ampdb = AMP_ADJ + (int)tp[ELM_A5]; + mFrame.mFormant6Ampdb = AMP_ADJ + (int)tp[ELM_A6]; + mFrame.mFormant1Ampdb = AMP_ADJ + (int)tp[ELM_A1]; + mFrame.mFormant2Ampdb = AMP_ADJ + (int)tp[ELM_A2]; + mFrame.mFormant3Ampdb = AMP_ADJ + (int)tp[ELM_A3]; + mFrame.mFormant4Ampdb = AMP_ADJ + (int)tp[ELM_A4]; + + parwave(samp); + + samp += mNspFr; + + // Declination of f0 envelope 0.25Hz / cS + mTop -= mBaseDeclination;// 0.5; + } + } + + mLastElement = currentElement; + + return (int)(samp - aSamplePointer); +} + + +void klatt::init(int aBaseFrequency, float aBaseSpeed, float aBaseDeclination, int aBaseWaveform) +{ + mBaseF0 = aBaseFrequency; + mBaseSpeed = aBaseSpeed; + mBaseDeclination = aBaseDeclination; + mBaseWaveform = aBaseWaveform; + + mSampleRate = 11025; + mF0Flutter = 0; + mF0FundamentalFreq = mBaseF0; + mFrame.mF0FundamentalFreq = mBaseF0; + + int FLPhz = (950 * mSampleRate) / 10000; + int BLPhz = (630 * mSampleRate) / 10000; + mNspFr = (int)(mSampleRate * mBaseSpeed) / 1000; + + mDownSampLowPassFilter.initResonator(FLPhz, BLPhz, mSampleRate); + + mNPer = 0; /* LG */ + mT0 = 0; /* LG */ + + mVLast = 0; /* Previous output of voice */ + mNLast = 0; /* Previous output of random number generator */ + mGlotLast = 0; /* Previous value of glotout */ +} diff --git a/third_party/soloud_speech/klatt.h b/third_party/soloud_speech/klatt.h new file mode 100644 index 0000000..6621090 --- /dev/null +++ b/third_party/soloud_speech/klatt.h @@ -0,0 +1,153 @@ +#ifndef KLATT_H +#define KLATT_H + +#include "resonator.h" + +#define CASCADE_PARALLEL 1 +#define ALL_PARALLEL 2 +#define NPAR 40 + +class klatt_frame +{ +public: + int mF0FundamentalFreq; // Voicing fund freq in Hz + int mVoicingAmpdb; // Amp of voicing in dB, 0 to 70 + int mFormant1Freq; // First formant freq in Hz, 200 to 1300 + int mFormant1Bandwidth; // First formant bw in Hz, 40 to 1000 + int mFormant2Freq; // Second formant freq in Hz, 550 to 3000 + int mFormant2Bandwidth; // Second formant bw in Hz, 40 to 1000 + int mFormant3Freq; // Third formant freq in Hz, 1200 to 4999 + int mFormant3Bandwidth; // Third formant bw in Hz, 40 to 1000 + int mFormant4Freq; // Fourth formant freq in Hz, 1200 to 4999 + int mFormant4Bandwidth; // Fourth formant bw in Hz, 40 to 1000 + int mFormant5Freq; // Fifth formant freq in Hz, 1200 to 4999 + int mFormant5Bandwidth; // Fifth formant bw in Hz, 40 to 1000 + int mFormant6Freq; // Sixth formant freq in Hz, 1200 to 4999 + int mFormant6Bandwidth; // Sixth formant bw in Hz, 40 to 2000 + int mNasalZeroFreq; // Nasal zero freq in Hz, 248 to 528 + int mNasalZeroBandwidth; // Nasal zero bw in Hz, 40 to 1000 + int mNasalPoleFreq; // Nasal pole freq in Hz, 248 to 528 + int mNasalPoleBandwidth; // Nasal pole bw in Hz, 40 to 1000 + int mAspirationAmpdb; // Amp of aspiration in dB, 0 to 70 + int mNoSamplesInOpenPeriod; // # of samples in open period, 10 to 65 + int mVoicingBreathiness; // Breathiness in voicing, 0 to 80 + int mVoicingSpectralTiltdb; // Voicing spectral tilt in dB, 0 to 24 + int mFricationAmpdb; // Amp of frication in dB, 0 to 80 + int mSkewnessOfAlternatePeriods; // Skewness of alternate periods, 0 to 40 in sample#/2 + int mFormant1Ampdb; // Amp of par 1st formant in dB, 0 to 80 + int mFormant1ParallelBandwidth; // Par. 1st formant bw in Hz, 40 to 1000 + int mFormant2Ampdb; // Amp of F2 frication in dB, 0 to 80 + int mFormant2ParallelBandwidth; // Par. 2nd formant bw in Hz, 40 to 1000 + int mFormant3Ampdb; // Amp of F3 frication in dB, 0 to 80 + int mFormant3ParallelBandwidth; // Par. 3rd formant bw in Hz, 40 to 1000 + int mFormant4Ampdb; // Amp of F4 frication in dB, 0 to 80 + int mFormant4ParallelBandwidth; // Par. 4th formant bw in Hz, 40 to 1000 + int mFormant5Ampdb; // Amp of F5 frication in dB, 0 to 80 + int mFormant5ParallelBandwidth; // Par. 5th formant bw in Hz, 40 to 1000 + int mFormant6Ampdb; // Amp of F6 (same as r6pa), 0 to 80 + int mFormant6ParallelBandwidth; // Par. 6th formant bw in Hz, 40 to 2000 + int mParallelNasalPoleAmpdb; // Amp of par nasal pole in dB, 0 to 80 + int mBypassFricationAmpdb; // Amp of bypass fric. in dB, 0 to 80 + int mPalallelVoicingAmpdb; // Amp of voicing, par in dB, 0 to 70 + int mOverallGaindb; // Overall gain, 60 dB is unity, 0 to 60 + klatt_frame(); +}; + +class darray; +class Element; + +class Slope +{ +public: + float mValue; /* boundary value */ + int mTime; /* transition time */ + Slope() + { + mValue = 0; + mTime = 0; + } +}; + + +enum KLATT_WAVEFORM +{ + KW_SAW, + KW_TRIANGLE, + KW_SIN, + KW_SQUARE, + KW_PULSE, + KW_NOISE, + KW_WARBLE +}; + +class klatt +{ + // resonators + resonator mParallelFormant1, mParallelFormant2, mParallelFormant3, + mParallelFormant4, mParallelFormant5, mParallelFormant6, + mParallelResoNasalPole, mNasalPole, mNasalZero, + mCritDampedGlotLowPassFilter, mDownSampLowPassFilter, mOutputLowPassFilter; +public: + int mBaseF0; + float mBaseSpeed; + float mBaseDeclination; + int mBaseWaveform; + + int mF0Flutter; + int mSampleRate; + int mNspFr; + int mF0FundamentalFreq; // Voicing fund freq in Hz + int mVoicingAmpdb; // Amp of voicing in dB, 0 to 70 + int mSkewnessOfAlternatePeriods; // Skewness of alternate periods,0 to 40 + int mTimeCount; // used for f0 flutter + int mNPer; // Current loc in voicing period 40000 samp/s + int mT0; // Fundamental period in output samples times 4 + int mNOpen; // Number of samples in open phase of period + int mNMod; // Position in period to begin noise amp. modul + + // Various amplitude variables used in main loop + + float mAmpVoice; // mVoicingAmpdb converted to linear gain + float mAmpBypas; // mBypassFricationAmpdb converted to linear gain + float mAmpAspir; // AP converted to linear gain + float mAmpFrica; // mFricationAmpdb converted to linear gain + float mAmpBreth; // ATURB converted to linear gain + + // State variables of sound sources + + int mSkew; // Alternating jitter, in half-period units + float mVLast; // Previous output of voice + float mNLast; // Previous output of random number generator + float mGlotLast; // Previous value of glotout + float mDecay; // mVoicingSpectralTiltdb converted to exponential time const + float mOneMd; // in voicing one-pole ELM_FEATURE_LOW-pass filter + + unsigned int mSeed; // random seed + + + + float natural_source(int aNper); + + void frame_init(); + void flutter(); + void pitch_synch_par_reset(int ns); + void parwave(short int *jwave); + void init(int aBaseFrequency = 1330, float aBaseSpeed = 10.0f, float aBaseDeclination = 0.5f, int aBaseWaveform = KW_SAW); + static int phone_to_elm(char *aPhoneme, int aCount, darray *aElement); + + int mElementCount; + unsigned char *mElement; + int mElementIndex; + klatt_frame mFrame; + Element * mLastElement; + int mTStress; + int mNTStress; + Slope mStressS; + Slope mStressE; + float mTop; + void initsynth(int aElementCount,unsigned char *aElement); + int synth(int aSampleCount, short *aSamplePointer); + klatt(); +}; + +#endif \ No newline at end of file diff --git a/third_party/soloud_speech/legal_readme.txt b/third_party/soloud_speech/legal_readme.txt new file mode 100644 index 0000000..878ec6a --- /dev/null +++ b/third_party/soloud_speech/legal_readme.txt @@ -0,0 +1,38 @@ +The speech synth is based on rsynth by the late +Nick Ing-Simmons (et al). + +He described the legal status as: + + This is a text to speech system produced by + integrating various pieces of code and tables + of data, which are all (I believe) in the + public domain. + +Since then, the rsynth source code has passed legal +checks by several open source organizations, so it +"should" be pretty safe. + +The primary copyright claims seem to have to do +with text-to-speech dictionary use, which I've +removed completely. + +I've done some serious refactoring, clean-up and +feature removal on the source, as all I need is +"a" free, simple speech synth, not a "good" +speech synth. Since I've removed a bunch of stuff, +this is probably safer public domain release +than the original. + +(I'm rather surprised there's no good public domain +speech synths out there; after all, it's 2013..) + +I'm placing my changes in public domain as well, +or if that's not acceptable for you, then CC0: +http://creativecommons.org/publicdomain/zero/1.0/ + +The SoLoud interface files (soloud_speech.*) are +under ZLib/LibPNG license. + +-- Jari Komppa + 2013 + \ No newline at end of file diff --git a/third_party/soloud_speech/resonator.cpp b/third_party/soloud_speech/resonator.cpp new file mode 100644 index 0000000..6214444 --- /dev/null +++ b/third_party/soloud_speech/resonator.cpp @@ -0,0 +1,74 @@ +#include +#include "resonator.h" + +#ifndef PI +#define PI 3.1415926535897932384626433832795f +#endif + +/* Convert formant freqencies and bandwidth into resonator difference equation coefficents + */ +void resonator::initResonator( + int aFrequency, /* Frequency of resonator in Hz */ + int aBandwidth, /* Bandwidth of resonator in Hz */ + int aSamplerate) +{ + float arg = (-PI / aSamplerate) * aBandwidth; + float r = (float)exp(arg); + mC = -(r * r); + arg = (-2.0f * PI / aSamplerate) * aFrequency; + mB = r * (float)cos(arg) * 2.0f; + mA = 1.0f - mB - mC; +} + +/* Convert formant freqencies and bandwidth into anti-resonator difference equation constants + */ +void resonator::initAntiresonator( + int aFrequency, /* Frequency of resonator in Hz */ + int aBandwidth, /* Bandwidth of resonator in Hz */ + int aSamplerate) +{ + initResonator(aFrequency, aBandwidth, aSamplerate); /* First compute ordinary resonator coefficients */ + /* Now convert to antiresonator coefficients */ + mA = 1.0f / mA; /* a'= 1/a */ + mB *= -mA; /* b'= -b/a */ + mC *= -mA; /* c'= -c/a */ +} + +/* Generic resonator function */ +float resonator::resonate(float input) +{ + float x = mA * input + mB * mP1 + mC * mP2; + mP2 = mP1; + mP1 = x; + return x; +} + +/* Generic anti-resonator function + Same as resonator except that a,b,c need to be set with initAntiresonator() + and we save inputs in p1/p2 rather than outputs. + There is currently only one of these - "mNasalZero" +*/ +/* Output = (mNasalZero.a * input) + (mNasalZero.b * oldin1) + (mNasalZero.c * oldin2) */ + +float resonator::antiresonate(float input) +{ + float x = mA * input + mB * mP1 + mC * mP2; + mP2 = mP1; + mP1 = input; + return x; +} + +resonator::resonator() +{ + mA = mB = mC = mP1 = mP2 = 0; +} + +resonator::~resonator() +{ +} + +void resonator::setGain(float aG) +{ + mA *= aG; +} + diff --git a/third_party/soloud_speech/resonator.h b/third_party/soloud_speech/resonator.h new file mode 100644 index 0000000..2e75e31 --- /dev/null +++ b/third_party/soloud_speech/resonator.h @@ -0,0 +1,44 @@ +#ifndef RESONATOR_H +#define RESONATOR_H + +class resonator +{ + float mA, mB, mC, mP1, mP2; +public: + + /* Convert formant freqencies and bandwidth into resonator difference equation coefficents + */ + void initResonator( + int aFrequency, /* Frequency of resonator in Hz */ + int aBandwidth, /* Bandwidth of resonator in Hz */ + int aSamplerate); + + /* Convert formant freqencies and bandwidth into anti-resonator difference equation constants + */ + void initAntiresonator( + int aFrequency, /* Frequency of resonator in Hz */ + int aBandwidth, /* Bandwidth of resonator in Hz */ + int aSamplerate); + + /* Set gain */ + void setGain(float aG); + + /* Generic resonator function */ + float resonate(float input); + + /* Generic anti-resonator function + Same as resonator except that a,b,c need to be set with initAntiresonator() + and we save inputs in p1/p2 rather than outputs. + There is currently only one of these - "mNasalZero" + + Output = (mNasalZero.a * input) + (mNasalZero.b * oldin1) + (mNasalZero.c * oldin2) + */ + + float antiresonate(float input); + + resonator(); + + ~resonator(); +}; + +#endif \ No newline at end of file diff --git a/third_party/soloud_speech/tts.cpp b/third_party/soloud_speech/tts.cpp new file mode 100644 index 0000000..8c825b6 --- /dev/null +++ b/third_party/soloud_speech/tts.cpp @@ -0,0 +1,1425 @@ +#include +#include +#include +#include +#include "darray.h" +#include "tts.h" + +static const char *ASCII[] = +{ + "null", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "space", "exclamation mark", "double quote", "hash", + "dollar", "percent", "ampersand", "quote", + "open parenthesis", "close parenthesis", "asterisk", "plus", + "comma", "minus", "full stop", "slash", + "zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine", "colon", "semi colon", + "less than", "equals", "greater than", "question mark", +#ifndef ALPHA_IN_DICT + "at", "ay", "bee", "see", + "dee", "e", "eff", "gee", + "aych", "i", "jay", "kay", + "ell", "em", "en", "ohe", + "pee", "kju", "are", "es", + "tee", "you", "vee", "double you", + "eks", "why", "zed", "open bracket", +#else /* ALPHA_IN_DICT */ + "at", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "open bracket", +#endif /* ALPHA_IN_DICT */ + "back slash", "close bracket", "circumflex", "underscore", +#ifndef ALPHA_IN_DICT + "back quote", "ay", "bee", "see", + "dee", "e", "eff", "gee", + "aych", "i", "jay", "kay", + "ell", "em", "en", "ohe", + "pee", "kju", "are", "es", + "tee", "you", "vee", "double you", + "eks", "why", "zed", "open brace", +#else /* ALPHA_IN_DICT */ + "back quote", "A", "B", "C", + "D", "E", "F", "G", + "H", "I", "J", "K", + "L", "M", "N", "O", + "P", "Q", "R", "S", + "T", "U", "V", "W", + "X", "Y", "Z", "open brace", +#endif /* ALPHA_IN_DICT */ + "vertical bar", "close brace", "tilde", "delete", + NULL +}; + +/* Context definitions */ +static const char Anything[] = ""; +/* No context requirement */ + +static const char Nothing[] = " "; +/* Context is beginning or end of word */ + +static const char Silent[] = ""; +/* No phonemes */ + + +#define LEFT_PART 0 +#define MATCH_PART 1 +#define RIGHT_PART 2 +#define OUT_PART 3 + +typedef const char *Rule[4]; +/* Rule is an array of 4 character pointers */ + + +/*0 = Punctuation */ +/* +** LEFT_PART MATCH_PART RIGHT_PART OUT_PART +*/ + + +static Rule punct_rules[] = +{ + {Anything, " ", Anything, " "}, + {Anything, "-", Anything, ""}, + {".", "'S", Anything, "z"}, + {"#:.E", "'S", Anything, "z"}, + {"#", "'S", Anything, "z"}, + {Anything, "'", Anything, ""}, + {Anything, ",", Anything, " "}, + {Anything, ".", Anything, " "}, + {Anything, "?", Anything, " "}, + {Anything, "!", Anything, " "}, + {Anything, 0, Anything, Silent}, +}; + +static Rule A_rules[] = +{ + {Anything, "A", Nothing, "@"}, + {Nothing, "ARE", Nothing, "0r"}, + {Nothing, "AR", "O", "@r"}, + {Anything, "AR", "#", "er"}, + {"^", "AS", "#", "eIs"}, + {Anything, "A", "WA", "@"}, + {Anything, "AW", Anything, "O"}, + {" :", "ANY", Anything, "eni"}, + {Anything, "A", "^+#", "eI"}, + {"#:", "ALLY", Anything, "@li"}, + {Nothing, "AL", "#", "@l"}, + {Anything, "AGAIN", Anything, "@gen"}, + {"#:", "AG", "E", "IdZ"}, + {Anything, "A", "^+:#", "&"}, + {" :", "A", "^+ ", "eI"}, + {Anything, "A", "^%", "eI"}, + {Nothing, "ARR", Anything, "@r"}, + {Anything, "ARR", Anything, "&r"}, + {" :", "AR", Nothing, "0r"}, + {Anything, "AR", Nothing, "3"}, + {Anything, "AR", Anything, "0r"}, + {Anything, "AIR", Anything, "er"}, + {Anything, "AI", Anything, "eI"}, + {Anything, "AY", Anything, "eI"}, + {Anything, "AU", Anything, "O"}, + {"#:", "AL", Nothing, "@l"}, + {"#:", "ALS", Nothing, "@lz"}, + {Anything, "ALK", Anything, "Ok"}, + {Anything, "AL", "^", "Ol"}, + {" :", "ABLE", Anything, "eIb@l"}, + {Anything, "ABLE", Anything, "@b@l"}, + {Anything, "ANG", "+", "eIndZ"}, + {"^", "A", "^#", "eI"}, + {Anything, "A", Anything, "&"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule B_rules[] = +{ + {Nothing, "BE", "^#", "bI"}, + {Anything, "BEING", Anything, "biIN"}, + {Nothing, "BOTH", Nothing, "b@UT"}, + {Nothing, "BUS", "#", "bIz"}, + {Anything, "BUIL", Anything, "bIl"}, + {Anything, "B", Anything, "b"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule C_rules[] = +{ + {Nothing, "CH", "^", "k"}, + {"^E", "CH", Anything, "k"}, + {Anything, "CH", Anything, "tS"}, + {" S", "CI", "#", "saI"}, + {Anything, "CI", "A", "S"}, + {Anything, "CI", "O", "S"}, + {Anything, "CI", "EN", "S"}, + {Anything, "C", "+", "s"}, + {Anything, "CK", Anything, "k"}, + {Anything, "COM", "%", "kVm"}, + {Anything, "C", Anything, "k"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule D_rules[] = +{ + {"#:", "DED", Nothing, "dId"}, + {".E", "D", Nothing, "d"}, + {"#:^E", "D", Nothing, "t"}, + {Nothing, "DE", "^#", "dI"}, + {Nothing, "DO", Nothing, "mDU"}, + {Nothing, "DOES", Anything, "dVz"}, + {Nothing, "DOING", Anything, "duIN"}, + {Nothing, "DOW", Anything, "daU"}, + {Anything, "DU", "A", "dZu"}, + {Anything, "D", Anything, "d"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule E_rules[] = +{ + {"#:", "E", Nothing, ""}, + {"':^", "E", Nothing, ""}, + {" :", "E", Nothing, "i"}, + {"#", "ED", Nothing, "d"}, + {"#:", "E", "D ", ""}, + {Anything, "EV", "ER", "ev"}, + {Anything, "E", "^%", "i"}, + {Anything, "ERI", "#", "iri"}, + {Anything, "ERI", Anything, "erI"}, + {"#:", "ER", "#", "3"}, + {Anything, "ER", "#", "er"}, + {Anything, "ER", Anything, "3"}, + {Nothing, "EVEN", Anything, "iven"}, + {"#:", "E", "W", ""}, + {"T", "EW", Anything, "u"}, + {"S", "EW", Anything, "u"}, + {"R", "EW", Anything, "u"}, + {"D", "EW", Anything, "u"}, + {"L", "EW", Anything, "u"}, + {"Z", "EW", Anything, "u"}, + {"N", "EW", Anything, "u"}, + {"J", "EW", Anything, "u"}, + {"TH", "EW", Anything, "u"}, + {"CH", "EW", Anything, "u"}, + {"SH", "EW", Anything, "u"}, + {Anything, "EW", Anything, "ju"}, + {Anything, "E", "O", "i"}, + {"#:S", "ES", Nothing, "Iz"}, + {"#:C", "ES", Nothing, "Iz"}, + {"#:G", "ES", Nothing, "Iz"}, + {"#:Z", "ES", Nothing, "Iz"}, + {"#:X", "ES", Nothing, "Iz"}, + {"#:J", "ES", Nothing, "Iz"}, + {"#:CH", "ES", Nothing, "Iz"}, + {"#:SH", "ES", Nothing, "Iz"}, + {"#:", "E", "S ", ""}, + {"#:", "ELY", Nothing, "li"}, + {"#:", "EMENT", Anything, "ment"}, + {Anything, "EFUL", Anything, "fUl"}, + {Anything, "EE", Anything, "i"}, + {Anything, "EARN", Anything, "3n"}, + {Nothing, "EAR", "^", "3"}, + {Anything, "EAD", Anything, "ed"}, + {"#:", "EA", Nothing, "i@"}, + {Anything, "EA", "SU", "e"}, + {Anything, "EA", Anything, "i"}, + {Anything, "EIGH", Anything, "eI"}, + {Anything, "EI", Anything, "i"}, + {Nothing, "EYE", Anything, "aI"}, + {Anything, "EY", Anything, "i"}, + {Anything, "EU", Anything, "ju"}, + {Anything, "E", Anything, "e"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule F_rules[] = +{ + {Anything, "FUL", Anything, "fUl"}, + {Anything, "F", Anything, "f"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule G_rules[] = +{ + {Anything, "GIV", Anything, "gIv"}, + {Nothing, "G", "I^", "g"}, + {Anything, "GE", "T", "ge"}, + {"SU", "GGES", Anything, "gdZes"}, + {Anything, "GG", Anything, "g"}, + {" B#", "G", Anything, "g"}, + {Anything, "G", "+", "dZ"}, + {Anything, "GREAT", Anything, "greIt"}, + {"#", "GH", Anything, ""}, + {Anything, "G", Anything, "g"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule H_rules[] = +{ + {Nothing, "HAV", Anything, "h&v"}, + {Nothing, "HERE", Anything, "hir"}, + {Nothing, "HOUR", Anything, "aU3"}, + {Anything, "HOW", Anything, "haU"}, + {Anything, "H", "#", "h"}, + {Anything, "H", Anything, ""}, + {Anything, 0, Anything, Silent}, +}; + +static Rule I_rules[] = +{ + {Nothing, "IAIN", Nothing, "I@n"}, + {Nothing, "ING", Nothing, "IN"}, + {Nothing, "IN", Anything, "In"}, + {Nothing, "I", Nothing, "aI"}, + {Anything, "IN", "D", "aIn"}, + {Anything, "IER", Anything, "i3"}, + {"#:R", "IED", Anything, "id"}, + {Anything, "IED", Nothing, "aId"}, + {Anything, "IEN", Anything, "ien"}, + {Anything, "IE", "T", "aIe"}, + {" :", "I", "%", "aI"}, + {Anything, "I", "%", "i"}, + {Anything, "IE", Anything, "i"}, + {Anything, "I", "^+:#", "I"}, + {Anything, "IR", "#", "aIr"}, + {Anything, "IZ", "%", "aIz"}, + {Anything, "IS", "%", "aIz"}, + {Anything, "I", "D%", "aI"}, + {"+^", "I", "^+", "I"}, + {Anything, "I", "T%", "aI"}, + {"#:^", "I", "^+", "I"}, + {Anything, "I", "^+", "aI"}, + {Anything, "IR", Anything, "3"}, + {Anything, "IGH", Anything, "aI"}, + {Anything, "ILD", Anything, "aIld"}, + {Anything, "IGN", Nothing, "aIn"}, + {Anything, "IGN", "^", "aIn"}, + {Anything, "IGN", "%", "aIn"}, + {Anything, "IQUE", Anything, "ik"}, + {"^", "I", "^#", "aI"}, + {Anything, "I", Anything, "I"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule J_rules[] = +{ + {Anything, "J", Anything, "dZ"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule K_rules[] = +{ + {Nothing, "K", "N", ""}, + {Anything, "K", Anything, "k"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule L_rules[] = +{ + {Anything, "LO", "C#", "l@U"}, + {"L", "L", Anything, ""}, + {"#:^", "L", "%", "@l"}, + {Anything, "LEAD", Anything, "lid"}, + {Anything, "L", Anything, "l"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule M_rules[] = +{ + {Anything, "MOV", Anything, "muv"}, + {"#", "MM", "#", "m"}, + {Anything, "M", Anything, "m"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule N_rules[] = +{ + {"E", "NG", "+", "ndZ"}, + {Anything, "NG", "R", "Ng"}, + {Anything, "NG", "#", "Ng"}, + {Anything, "NGL", "%", "Ng@l"}, + {Anything, "NG", Anything, "N"}, + {Anything, "NK", Anything, "Nk"}, + {Nothing, "NOW", Nothing, "naU"}, + {"#", "NG", Nothing, "Ng"}, + {Anything, "N", Anything, "n"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule O_rules[] = +{ + {Anything, "OF", Nothing, "@v"}, + {Anything, "OROUGH", Anything, "3@U"}, + {"#:", "OR", Nothing, "3"}, + {"#:", "ORS", Nothing, "3z"}, + {Anything, "OR", Anything, "Or"}, + {Nothing, "ONE", Anything, "wVn"}, + {Anything, "OW", Anything, "@U"}, + {Nothing, "OVER", Anything, "@Uv3"}, + {Anything, "OV", Anything, "Vv"}, + {Anything, "O", "^%", "@U"}, + {Anything, "O", "^EN", "@U"}, + {Anything, "O", "^I#", "@U"}, + {Anything, "OL", "D", "@Ul"}, + {Anything, "OUGHT", Anything, "Ot"}, + {Anything, "OUGH", Anything, "Vf"}, + {Nothing, "OU", Anything, "aU"}, + {"H", "OU", "S#", "aU"}, + {Anything, "OUS", Anything, "@s"}, + {Anything, "OUR", Anything, "Or"}, + {Anything, "OULD", Anything, "Ud"}, + {"^", "OU", "^L", "V"}, + {Anything, "OUP", Anything, "up"}, + {Anything, "OU", Anything, "aU"}, + {Anything, "OY", Anything, "oI"}, + {Anything, "OING", Anything, "@UIN"}, + {Anything, "OI", Anything, "oI"}, + {Anything, "OOR", Anything, "Or"}, + {Anything, "OOK", Anything, "Uk"}, + {Anything, "OOD", Anything, "Ud"}, + {Anything, "OO", Anything, "u"}, + {Anything, "O", "E", "@U"}, + {Anything, "O", Nothing, "@U"}, + {Anything, "OA", Anything, "@U"}, + {Nothing, "ONLY", Anything, "@Unli"}, + {Nothing, "ONCE", Anything, "wVns"}, + {Anything, "ON'T", Anything, "@Unt"}, + {"C", "O", "N", "0"}, + {Anything, "O", "NG", "O"}, + {" :^", "O", "N", "V"}, + {"I", "ON", Anything, "@n"}, + {"#:", "ON", Nothing, "@n"}, + {"#^", "ON", Anything, "@n"}, + {Anything, "O", "ST ", "@U"}, + {Anything, "OF", "^", "Of"}, + {Anything, "OTHER", Anything, "VD3"}, + {Anything, "OSS", Nothing, "Os"}, + {"#:^", "OM", Anything, "Vm"}, + {Anything, "O", Anything, "0"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule P_rules[] = +{ + {Anything, "PH", Anything, "f"}, + {Anything, "PEOP", Anything, "pip"}, + {Anything, "POW", Anything, "paU"}, + {Anything, "PUT", Nothing, "pUt"}, + {Anything, "P", Anything, "p"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule Q_rules[] = +{ + {Anything, "QUAR", Anything, "kwOr"}, + {Anything, "QU", Anything, "kw"}, + {Anything, "Q", Anything, "k"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule R_rules[] = +{ + {Nothing, "RE", "^#", "ri"}, + {Anything, "R", Anything, "r"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule S_rules[] = +{ + {Anything, "SH", Anything, "S"}, + {"#", "SION", Anything, "Z@n"}, + {Anything, "SOME", Anything, "sVm"}, + {"#", "SUR", "#", "Z3"}, + {Anything, "SUR", "#", "S3"}, + {"#", "SU", "#", "Zu"}, + {"#", "SSU", "#", "Su"}, + {"#", "SED", Nothing, "zd"}, + {"#", "S", "#", "z"}, + {Anything, "SAID", Anything, "sed"}, + {"^", "SION", Anything, "S@n"}, + {Anything, "S", "S", ""}, + {".", "S", Nothing, "z"}, + {"#:.E", "S", Nothing, "z"}, + {"#:^##", "S", Nothing, "z"}, + {"#:^#", "S", Nothing, "s"}, + {"U", "S", Nothing, "s"}, + {" :#", "S", Nothing, "z"}, + {Nothing, "SCH", Anything, "sk"}, + {Anything, "S", "C+", ""}, + {"#", "SM", Anything, "zm"}, + {"#", "SN", "'", "z@n"}, + {Anything, "S", Anything, "s"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule T_rules[] = +{ + {Nothing, "THE", Nothing, "D@"}, + {Anything, "TO", Nothing, "tu"}, + {Anything, "THAT", Nothing, "D&t"}, + {Nothing, "THIS", Nothing, "DIs"}, + {Nothing, "THEY", Anything, "DeI"}, + {Nothing, "THERE", Anything, "Der"}, + {Anything, "THER", Anything, "D3"}, + {Anything, "THEIR", Anything, "Der"}, + {Nothing, "THAN", Nothing, "D&n"}, + {Nothing, "THEM", Nothing, "Dem"}, + {Anything, "THESE", Nothing, "Diz"}, + {Nothing, "THEN", Anything, "Den"}, + {Anything, "THROUGH", Anything, "Tru"}, + {Anything, "THOSE", Anything, "D@Uz"}, + {Anything, "THOUGH", Nothing, "D@U"}, + {Nothing, "THUS", Anything, "DVs"}, + {Anything, "TH", Anything, "T"}, + {"#:", "TED", Nothing, "tId"}, + {"S", "TI", "#N", "tS"}, + {Anything, "TI", "O", "S"}, + {Anything, "TI", "A", "S"}, + {Anything, "TIEN", Anything, "S@n"}, + {Anything, "TUR", "#", "tS3"}, + {Anything, "TU", "A", "tSu"}, + {Nothing, "TWO", Anything, "tu"}, + {Anything, "T", Anything, "t"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule U_rules[] = +{ + {Nothing, "UN", "I", "jun"}, + {Nothing, "UN", Anything, "Vn"}, + {Nothing, "UPON", Anything, "@pOn"}, + {"T", "UR", "#", "Ur"}, + {"S", "UR", "#", "Ur"}, + {"R", "UR", "#", "Ur"}, + {"D", "UR", "#", "Ur"}, + {"L", "UR", "#", "Ur"}, + {"Z", "UR", "#", "Ur"}, + {"N", "UR", "#", "Ur"}, + {"J", "UR", "#", "Ur"}, + {"TH", "UR", "#", "Ur"}, + {"CH", "UR", "#", "Ur"}, + {"SH", "UR", "#", "Ur"}, + {Anything, "UR", "#", "jUr"}, + {Anything, "UR", Anything, "3"}, + {Anything, "U", "^ ", "V"}, + {Anything, "U", "^^", "V"}, + {Anything, "UY", Anything, "aI"}, + {" G", "U", "#", ""}, + {"G", "U", "%", ""}, + {"G", "U", "#", "w"}, + {"#N", "U", Anything, "ju"}, + {"T", "U", Anything, "u"}, + {"S", "U", Anything, "u"}, + {"R", "U", Anything, "u"}, + {"D", "U", Anything, "u"}, + {"L", "U", Anything, "u"}, + {"Z", "U", Anything, "u"}, + {"N", "U", Anything, "u"}, + {"J", "U", Anything, "u"}, + {"TH", "U", Anything, "u"}, + {"CH", "U", Anything, "u"}, + {"SH", "U", Anything, "u"}, + {Anything, "U", Anything, "ju"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule V_rules[] = +{ + {Anything, "VIEW", Anything, "vju"}, + {Anything, "V", Anything, "v"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule W_rules[] = +{ + {Nothing, "WERE", Anything, "w3"}, + {Anything, "WA", "S", "w0"}, + {Anything, "WA", "T", "w0"}, + {Anything, "WHERE", Anything, "hwer"}, + {Anything, "WHAT", Anything, "hw0t"}, + {Anything, "WHOL", Anything, "h@Ul"}, + {Anything, "WHO", Anything, "hu"}, + {Anything, "WH", Anything, "hw"}, + {Anything, "WAR", Anything, "wOr"}, + {Anything, "WOR", "^", "w3"}, + {Anything, "WR", Anything, "r"}, + {Anything, "W", Anything, "w"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule X_rules[] = +{ + {Anything, "X", Anything, "ks"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule Y_rules[] = +{ + {Anything, "YOUNG", Anything, "jVN"}, + {Nothing, "YOU", Anything, "ju"}, + {Nothing, "YES", Anything, "jes"}, + {Nothing, "Y", Anything, "j"}, + {"#:^", "Y", Nothing, "i"}, + {"#:^", "Y", "I", "i"}, + {" :", "Y", Nothing, "aI"}, + {" :", "Y", "#", "aI"}, + {" :", "Y", "^+:#", "I"}, + {" :", "Y", "^#", "aI"}, + {Anything, "Y", Anything, "I"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule Z_rules[] = +{ + {Anything, "Z", Anything, "z"}, + {Anything, 0, Anything, Silent}, +}; + +static Rule *Rules[] = +{ + punct_rules, + A_rules, B_rules, C_rules, D_rules, E_rules, F_rules, G_rules, + H_rules, I_rules, J_rules, K_rules, L_rules, M_rules, N_rules, + O_rules, P_rules, Q_rules, R_rules, S_rules, T_rules, U_rules, + V_rules, W_rules, X_rules, Y_rules, Z_rules +}; + + +static const char *Cardinals[] = +{ + "zero", "one", "two", "three", "four", + "five", "six", "seven", "eight", "nine", + "ten", "eleven", "twelve", "thirteen", "fourteen", + "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" +}; + + +static const char *Twenties[] = +{ + "twenty", "thirty", "forty", "fifty", + "sixty", "seventy", "eighty", "ninety" +}; + + +static const char *Ordinals[] = +{ + "zeroth", "first", "second", "third", "fourth", + "fifth", "sixth", "seventh","eighth", "ninth", + "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", + "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth" +}; + + +static const char *Ord_twenties[] = +{ + "twentieth", "thirtieth", "fortieth", "fiftieth", + "sixtieth", "seventieth", "eightieth", "ninetieth" +}; + + +/* +** Translate a number to phonemes. This version is for CARDINAL numbers. +** Note: this is recursive. +*/ +static int xlate_cardinal(int value, darray *phone) +{ + int nph = 0; + + if (value < 0) + { + nph += xlate_string("minus", phone); + value = (-value); + + if (value < 0) /* Overflow! -32768 */ + { + nph += xlate_string("a lot", phone); + return nph; + } + } + + if (value >= 1000000000L) + /* Billions */ + { + nph += xlate_cardinal(value / 1000000000L, phone); + nph += xlate_string("billion", phone); + value = value % 1000000000; + + if (value == 0) + return nph; /* Even billion */ + + if (value < 100) + nph += xlate_string("and", phone); + + /* as in THREE BILLION AND FIVE */ + } + + if (value >= 1000000L) + /* Millions */ + { + nph += xlate_cardinal(value / 1000000L, phone); + nph += xlate_string("million", phone); + value = value % 1000000L; + + if (value == 0) + return nph; /* Even million */ + + if (value < 100) + nph += xlate_string("and", phone); + + /* as in THREE MILLION AND FIVE */ + } + + /* Thousands 1000..1099 2000..99999 */ + /* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */ + + if ((value >= 1000L && value <= 1099L) || value >= 2000L) + { + nph += xlate_cardinal(value / 1000L, phone); + nph += xlate_string("thousand", phone); + value = value % 1000L; + + if (value == 0) + return nph; /* Even thousand */ + + if (value < 100) + nph += xlate_string("and", phone); + + /* as in THREE THOUSAND AND FIVE */ + } + + if (value >= 100L) + { + nph += xlate_string(Cardinals[value / 100], phone); + nph += xlate_string("hundred", phone); + value = value % 100; + + if (value == 0) + return nph; /* Even hundred */ + } + + if (value >= 20) + { + nph += xlate_string(Twenties[(value - 20) / 10], phone); + value = value % 10; + + if (value == 0) + return nph; /* Even ten */ + } + + nph += xlate_string(Cardinals[value], phone); + + return nph; +} + +#if 0 +/* +** Translate a number to phonemes. This version is for ORDINAL numbers. +** Note: this is recursive. +*/ +static int xlate_ordinal(int value, darray *phone) +{ + int nph = 0; + + if (value < 0) + { + nph += xlate_string("minus", phone); + value = (-value); + + if (value < 0) /* Overflow! -32768 */ + { + nph += xlate_string("a lot", phone); + return nph; + } + } + + if (value >= 1000000000L) + /* Billions */ + { + nph += xlate_cardinal(value / 1000000000L, phone); + value = value % 1000000000; + + if (value == 0) + { + nph += xlate_string("billionth", phone); + return nph; /* Even billion */ + } + + nph += xlate_string("billion", phone); + + if (value < 100) + nph += xlate_string("and", phone); + + /* as in THREE BILLION AND FIVE */ + } + + if (value >= 1000000L) + /* Millions */ + { + nph += xlate_cardinal(value / 1000000L, phone); + value = value % 1000000L; + + if (value == 0) + { + nph += xlate_string("millionth", phone); + return nph; /* Even million */ + } + + nph += xlate_string("million", phone); + + if (value < 100) + nph += xlate_string("and", phone); + + /* as in THREE MILLION AND FIVE */ + } + + /* Thousands 1000..1099 2000..99999 */ + /* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */ + + if ((value >= 1000L && value <= 1099L) || value >= 2000L) + { + nph += xlate_cardinal(value / 1000L, phone); + value = value % 1000L; + + if (value == 0) + { + nph += xlate_string("thousandth", phone); + return nph; /* Even thousand */ + } + + nph += xlate_string("thousand", phone); + + if (value < 100) + nph += xlate_string("and", phone); + + /* as in THREE THOUSAND AND FIVE */ + } + + if (value >= 100L) + { + nph += xlate_string(Cardinals[value / 100], phone); + value = value % 100; + + if (value == 0) + { + nph += xlate_string("hundredth", phone); + return nph; /* Even hundred */ + } + + nph += xlate_string("hundred", phone); + } + + if (value >= 20) + { + if ((value % 10) == 0) + { + nph += xlate_string(Ord_twenties[(value - 20) / 10], phone); + return nph; /* Even ten */ + } + + nph += xlate_string(Twenties[(value - 20) / 10], phone); + + value = value % 10; + } + + nph += xlate_string(Ordinals[value], phone); + + return nph; +} +#endif + +static int isvowel(int chr) +{ + return (chr == 'A' || chr == 'E' || chr == 'I' || + chr == 'O' || chr == 'U'); +} + +static int isconsonant(int chr) +{ + return (isupper(chr) && !isvowel(chr)); +} + +static int leftmatch( + const char *pattern, /* first char of pattern to match in text */ + const char *context) /* last char of text to be matched */ + +{ + const char *pat; + const char *text; + int count; + + if (*pattern == '\0') + /* null string matches any context */ + { + return 1; + } + + /* point to last character in pattern string */ + count = (int)strlen(pattern); + + pat = pattern + (count - 1); + + text = context; + + for (; count > 0; pat--, count--) + { + /* First check for simple text or space */ + if (isalpha(*pat) || *pat == '\'' || *pat == ' ') + { + if (*pat != *text) + { + return 0; + } + else + { + text--; + continue; + } + } + + switch (*pat) + { + + case '#': /* One or more vowels */ + + if (!isvowel(*text)) + return 0; + + text--; + + while (isvowel(*text)) + text--; + + break; + + case ':': /* Zero or more consonants */ + while (isconsonant(*text)) + text--; + + break; + + case '^': /* One consonant */ + if (!isconsonant(*text)) + return 0; + + text--; + + break; + + case '.': /* B, D, V, G, J, L, M, N, R, W, Z */ + if (*text != 'B' && *text != 'D' && *text != 'V' + && *text != 'G' && *text != 'J' && *text != 'L' + && *text != 'M' && *text != 'N' && *text != 'R' + && *text != 'W' && *text != 'Z') + return 0; + + text--; + + break; + + case '+': /* E, I or Y (front vowel) */ + if (*text != 'E' && *text != 'I' && *text != 'Y') + return 0; + + text--; + + break; + + case '%': + + default: + fprintf(stderr, "Bad char in left rule: '%c'\n", *pat); + + return 0; + } + } + + return 1; +} + +static int rightmatch( + const char *pattern, /* first char of pattern to match in text */ + const char *context) /* last char of text to be matched */ +{ + const char *pat; + const char *text; + + if (*pattern == '\0') + /* null string matches any context */ + return 1; + + pat = pattern; + + text = context; + + for (pat = pattern; *pat != '\0'; pat++) + { + /* First check for simple text or space */ + if (isalpha(*pat) || *pat == '\'' || *pat == ' ') + { + if (*pat != *text) + { + return 0; + } + else + { + text++; + continue; + } + } + + switch (*pat) + { + + case '#': /* One or more vowels */ + + if (!isvowel(*text)) + return 0; + + text++; + + while (isvowel(*text)) + text++; + + break; + + case ':': /* Zero or more consonants */ + while (isconsonant(*text)) + text++; + + break; + + case '^': /* One consonant */ + if (!isconsonant(*text)) + return 0; + + text++; + + break; + + case '.': /* B, D, V, G, J, L, M, N, R, W, Z */ + if (*text != 'B' && *text != 'D' && *text != 'V' + && *text != 'G' && *text != 'J' && *text != 'L' + && *text != 'M' && *text != 'N' && *text != 'R' + && *text != 'W' && *text != 'Z') + return 0; + + text++; + + break; + + case '+': /* E, I or Y (front vowel) */ + if (*text != 'E' && *text != 'I' && *text != 'Y') + return 0; + + text++; + + break; + + case '%': /* ER, E, ES, ED, ING, ELY (a suffix) */ + if (*text == 'E') + { + text++; + + if (*text == 'L') + { + text++; + + if (*text == 'Y') + { + text++; + break; + } + + else + { + text--; /* Don't gobble L */ + break; + } + } + + else + if (*text == 'R' || *text == 'S' || *text == 'D') + text++; + + break; + } + + else + if (*text == 'I') + { + text++; + + if (*text == 'N') + { + text++; + + if (*text == 'G') + { + text++; + break; + } + } + + return 0; + } + + else + return 0; + + default: + fprintf(stderr, "Bad char in right rule:'%c'\n", *pat); + + return 0; + } + } + + return 1; +} + +static void phone_cat(darray *arg, const char *s) +{ + char ch; + + while ((ch = *s++)) + arg->put(ch); +} + + +static int find_rule(darray *arg, char *word, int index, Rule *rules) +{ + for (;;) /* Search for the rule */ + { + Rule *rule; + const char *left, + *match, + *right, + *output; + int remainder; + rule = rules++; + match = (*rule)[1]; + + if (match == 0) + /* bad symbol! */ + { + fprintf(stderr, "Error: Can't find rule for: '%c' in \"%s\"\n", + word[index], word); + return index + 1; /* Skip it! */ + } + + for (remainder = index; *match != '\0'; match++, remainder++) + { + if (*match != word[remainder]) + break; + } + + if (*match != '\0') + continue; /* found missmatch */ + + left = (*rule)[0]; + + right = (*rule)[2]; + + if (!leftmatch(left, &word[index - 1])) + continue; + + if (!rightmatch(right, &word[remainder])) + continue; + + output = (*rule)[3]; + + phone_cat(arg, output); + + return remainder; + } +} + +static void guess_word(darray *arg, char *word) +{ + int index; /* Current position in word */ + int type; /* First letter of match part */ + index = 1; /* Skip the initial blank */ + + do + { + if (isupper(word[index])) + type = word[index] - 'A' + 1; + else + type = 0; + + index = find_rule(arg, word, index, Rules[type]); + } + + while (word[index] != '\0'); +} + + +static int NRL(const char *s, int n, darray *phone) +{ + int old = phone->getSize(); + char *word = (char *) malloc(n + 3); // TODO: may return null + char *d = word; + *d++ = ' '; + + while (n-- > 0) + { + char ch = *s++; + + if (islower(ch)) + ch = (char)toupper(ch); + + *d++ = ch; + } + + *d++ = ' '; // kinda unnecessary + + *d = '\0'; + guess_word(phone, word); + free(word); + return phone->getSize() - old; +} + + +static int spell_out(const char *word, int n, darray *phone) +{ + int nph = 0; + + while (n-- > 0) + { + nph += xlate_string(ASCII[*word++ & 0x7F], phone); + } + + return nph; +} + +static int suspect_word(const char *s, int n) +{ + int i = 0; + int seen_lower = 0; + int seen_upper = 0; + int seen_vowel = 0; + int last = 0; + + for (i = 0; i < n; i++) + { + char ch = *s++; + + if (i && last != '-' && isupper(ch)) + seen_upper = 1; + + if (islower(ch)) + { + seen_lower = 1; + ch = (char)toupper(ch); + } + + if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y') + seen_vowel = 1; + + last = ch; + } + + return !seen_vowel || (seen_upper && seen_lower) || !seen_lower; +} + +static int xlate_word(const char *word, int n, darray *phone) +{ + int nph = 0; + + if (*word != '[') + { + if (suspect_word(word, n)) + return spell_out(word, n, phone); + else + { + nph += NRL(word, n, phone); + } + } + + else + { + if ((++word)[(--n) - 1] == ']') + n--; + + while (n-- > 0) + { + phone->put(*word++); + nph++; + } + } + + phone->put(' '); + + return nph + 1; +} + + +int xlate_string(const char *string, darray *phone) +{ + int nph = 0; + const char *s = string; + char ch; + + while (isspace(ch = *s)) + s++; + + while (*s) + { + ch = *s; + const char *word = s; + + if (isalpha(ch)) + { + while (isalpha(ch = *s) || ((ch == '\'' || ch == '-' || ch == '.') && isalpha(s[1]))) + { + s++; + } + + if (!ch || isspace(ch) || ispunct(ch) || (isdigit(ch) && !suspect_word(word, (int)(s - word)))) + { + nph += xlate_word(word, (int)(s - word), phone); + } + else + { + while (*s && !isspace(*s) && !ispunct(*s)) + { + ch = *s; + s++; + } + + nph += spell_out(word, (int)(s - word), phone); + } + } + else + { + if (isdigit(ch) || (ch == '-' && isdigit(s[1]))) + { + int sign = (ch == '-') ? -1 : 1; + int value = 0; + + if (sign < 0) + { + ch = *++s; + } + + while (isdigit(ch = *s)) + { + value = value * 10 + ch - '0'; + s++; + } + + if (ch == '.' && isdigit(s[1])) + { + word = ++s; + nph += xlate_cardinal(value * sign, phone); + nph += xlate_string("point", phone); + + while (isdigit(ch = *s)) + { + s++; + } + + nph += spell_out(word, (int)(s - word), phone); + } + else + { + /* check for ordinals, date, time etc. can go in here */ + nph += xlate_cardinal(value * sign, phone); + } + } + else + { + if (ch == '[' && strchr(s, ']')) + { + const char *thisword = s; + + while (*s && *s++ != ']') + /* nothing */ + ; + + nph += xlate_word(thisword, (int)(s - thisword), phone); + } + else + { + if (ispunct(ch)) + { + switch (ch) + { + + case '!': + + case '?': + + case '.': + s++; + phone->put('.');// (' '); + break; + + case '"': /* change pitch ? */ + + case ':': + + case '-': + + case ';': + + case ',': + + case '(': + + case ')': + s++; + phone->put(' '); + break; + + case '[': + { + const char *e = strchr(s, ']'); + + if (e) + { + s++; + + while (s < e) + phone->put(*s++); + + s = e + 1; + + break; + } + } + // fallthrough + default: + nph += spell_out(word, 1, phone); + s++; + break; + } + } + else + { + while (*s && !isspace(*s)) + { + ch = *s; + s++; + } + + nph += spell_out(word, (int)(s - word), phone); + } + } + } + + while (isspace(ch = *s)) + s++; + } + } + + return nph; +} diff --git a/third_party/soloud_speech/tts.h b/third_party/soloud_speech/tts.h new file mode 100644 index 0000000..8966e76 --- /dev/null +++ b/third_party/soloud_speech/tts.h @@ -0,0 +1,5 @@ + +extern int xlate_string (const char *string,darray *phone); + + + diff --git a/third_party/stb b/third_party/stb new file mode 160000 index 0000000..ae721c5 --- /dev/null +++ b/third_party/stb @@ -0,0 +1 @@ +Subproject commit ae721c50eaf761660b4f90cc590453cdb0c2acd0 diff --git a/third_party/sun b/third_party/sun new file mode 160000 index 0000000..a5b6b9e --- /dev/null +++ b/third_party/sun @@ -0,0 +1 @@ +Subproject commit a5b6b9ea53d78b2019201a9fb75b763b7d92873c