pax_global_header00006660000000000000000000000064146730330420014514gustar00rootroot0000000000000052 comment=1e7ab565205667032cf19834a7f9c4e1f375f59e openjazz-20240919/000077500000000000000000000000001467303304200136305ustar00rootroot00000000000000openjazz-20240919/.editorconfig000066400000000000000000000006761467303304200163160ustar00rootroot00000000000000# https://EditorConfig.org root = true [*] trim_trailing_whitespace = true insert_final_newline = true [{CMakeLists.txt,*.cmake,Makefile}] indent_style = tab indent_size = 4 [{*.cpp,*.h}] indent_style = tab indent_size = 4 [ext/**/{*.cpp,*.h}] indent_style = ignore indent_size = ignore [*.json,*.yml,*.rc,*.adoc] indent_style = space indent_size = 2 [*.md] indent_style = space indent_size = 4 [*.xml] indent_style = tab indent_size = 4 openjazz-20240919/.gitattributes000066400000000000000000000002611467303304200165220ustar00rootroot00000000000000* text=auto # source files *.txt text *.cpp text *.h text CMakeLists.txt text *.cmake text *.json text # distribution archives /.github export-ignore /builds/ci export-ignore openjazz-20240919/.gitignore000066400000000000000000000006711467303304200156240ustar00rootroot00000000000000OpenJazz openjazz.cfg openjazz.log *.exe *.a *.o # CMake CMakeCache.txt CMakeFiles/ *.cmake !/builds/cmake/*.cmake ext/Makefile build.ninja CMakeUserPresets.json # homebrew *.elf # wii *.dol # psp *.prx PARAM.SFO EBOOT.PBP # 3ds *.3dsx *.smdh *.cia # RISC OS /res/riscos/\!OpenJazz/data/* *,ff8 *,e1f # macos .DS_Store # distribution archives openjazz-*.tar.* OpenJazz-*.zip # generated documentation /doc/html /res/unix/OpenJazz.6 openjazz-20240919/BUILDING.md000066400000000000000000000031321467303304200153460ustar00rootroot00000000000000 # Building OpenJazz Needed: - SDL 2.x or SDL 1.2.x (deprecated) library (https://libsdl.org/) Optional: - SDL_net library (https://www.libsdl.org/projects/SDL_net/) - CMake (https://cmake.org/) OpenJazz ships a basic Makefile that may be used and adapted to the specific needs of the user or platform where it shall run. It is usually enough to simply run `make` on UNIX-like platforms. For more sophisticated building, a CMake script is also provided: cmake -B OJ -S . cmake --build OJ The optional installation step (`cmake --install OJ`) is not needed for all platforms, but usually for UNIX-like. For Homebrew targets a packaging step is also provided (`cmake --build OJ --target package`), which automatically creates ZIP archives. For network play, you need a platform which natively provides sockets or use `SDL_net`, this is detected by CMake. You can always use `-DNETWORK=OFF` to disable it. On the Windows platform, the socket library (`-lws2_32`) is needed for linking the executable, others might need also additional libraries. Further, currently outdated instructions are available at: http://www.alister.eu/jazz/oj/build.php ## additional build options - `DATAPATH` - add a fixed path for game data files - `SCALE` - enable scaling of the video output (i.e. Scale2X...) - `PORTABLE` - Do not use external directories for configuration saving, etc. (This only affects Unix platforms, Windows version is always portable) Some ports have their own options, see (Platforms)[PLATFORMS.md] for details. ### advanced options - `FULLSCREEN_ONLY` - disable window mode, useful for console ports openjazz-20240919/CMakeLists.txt000066400000000000000000000333311467303304200163730ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16...3.29) project(OpenJazz VERSION 20240919 LANGUAGES CXX HOMEPAGE_URL http://alister.eu/jazz/oj/) # Extra CMake Module files list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/builds/cmake) include(CMakeDependentOption) include(OJ-misc) include(Platform-Helpers) # We are in the process to migrate to C++14 set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # OpenJazz add_executable(OpenJazz # main engine src/game/clientgame.cpp src/game/game.cpp src/game/game.h src/game/gamemode.cpp src/game/gamemode.h src/game/localgame.cpp src/game/servergame.cpp src/io/controls.cpp src/io/controls.h src/io/file.cpp src/io/file.h src/io/gfx/anim.cpp src/io/gfx/anim.h src/io/gfx/font.cpp src/io/gfx/font.h src/io/gfx/paletteeffects.cpp src/io/gfx/paletteeffects.h src/io/gfx/sprite.cpp src/io/gfx/sprite.h src/io/gfx/video.cpp src/io/gfx/video.h src/io/log.cpp src/io/log.h src/io/network.cpp src/io/network.h src/io/sound.cpp src/io/sound.h src/level/level.cpp src/level/level.h src/level/levelplayer.h src/level/movable.cpp src/level/movable.h src/logo.h src/loop.h src/main.cpp src/menu/filemenu.cpp src/menu/gamemenu.cpp src/menu/mainmenu.cpp src/menu/menu.cpp src/menu/menu.h src/menu/plasma.cpp src/menu/plasma.h src/menu/setupmenu.cpp src/OpenJazz.h src/platforms/platforms.h src/player/player.cpp src/player/player.h src/setup.cpp src/setup.h src/types.h src/util.cpp src/util.h src/version.cpp src/version.h # episode 1 src/jj1/bonuslevel/jj1bonuslevel.cpp src/jj1/bonuslevel/jj1bonuslevel.h src/jj1/bonuslevel/jj1bonuslevelplayer.cpp src/jj1/bonuslevel/jj1bonuslevelplayer.h src/jj1/level//jj1bird.cpp src/jj1/level//jj1bird.h src/jj1/level//jj1levelplayer.cpp src/jj1/level//jj1levelplayer.h src/jj1/level//jj1levelplayerframe.cpp src/jj1/level/event/jj1bridge.cpp src/jj1/level/event/jj1event.cpp src/jj1/level/event/jj1event.h src/jj1/level/event/jj1guardians.cpp src/jj1/level/event/jj1guardians.h src/jj1/level/event/jj1standardevent.cpp src/jj1/level/jj1bullet.cpp src/jj1/level/jj1bullet.h src/jj1/level/jj1demolevel.cpp src/jj1/level/jj1level.cpp src/jj1/level/jj1level.h src/jj1/level/jj1levelframe.cpp src/jj1/level/jj1levelload.cpp src/jj1/planet/jj1planet.cpp src/jj1/planet/jj1planet.h src/jj1/save/jj1save.cpp src/jj1/save/jj1save.h src/jj1/scene/jj1scene.cpp src/jj1/scene/jj1scene.h src/jj1/scene/jj1sceneload.cpp) target_include_directories(OpenJazz PUBLIC src) # portable mode cmake_dependent_option(PORTABLE "Do not write to external directories, etc." OFF "NOT OJ_DEFAULT_PORTABLE" ON) if(PORTABLE) set(PORTABLE_STATUS "Enabled") target_compile_definitions(OpenJazz PRIVATE PORTABLE) else() set(PORTABLE_STATUS "Disabled") endif() if((PORTABLE AND OJ_DEFAULT_PORTABLE) OR (NOT PORTABLE AND NOT OJ_DEFAULT_PORTABLE)) set(PORTABLE_STATUS "${PORTABLE_STATUS} (Default)") endif() # path to game data set(DATAPATH "" CACHE PATH "Fixed path where game data is available") if(DATAPATH) target_compile_definitions(OpenJazz PRIVATE DATAPATH="${DATAPATH}") endif() # libraries if(EMSCRIPTEN) # emscripten ships a "port" of SDL2 set(OJ_LIBS_SDL "SDL2") set(SDL_STATUS "SDL2 (emscripten port)") target_compile_options(OpenJazz PRIVATE "--use-port=sdl2;-flto;-fexceptions") elseif(LEGACY_SDL) find_package(SDL REQUIRED) target_include_directories(OpenJazz PRIVATE ${SDL_INCLUDE_DIR}) set(OJ_LIBS_SDL ${SDL_LIBRARY}) set(SDL_STATUS "SDL1.2 (legacy)") else() find_package(SDL2 REQUIRED) target_include_directories(OpenJazz PRIVATE ${SDL2_INCLUDE_DIRS}) set(OJ_LIBS_SDL ${SDL2_LIBRARIES}) set(SDL_STATUS "SDL2") endif() # common bundled libraries add_subdirectory(ext) # network set(NETWORK_STATUS "Disabled") option(NETWORK "Enable Network support" ON) if(NETWORK) set(NETWORK_STATUS "Enabled, platform specific") if(NOT OJ_LIBS_NET) # sockets include(CheckIncludeFileCXX) check_include_file_cxx(sys/socket.h HAVE_SOCKETS) if(HAVE_SOCKETS) set(NETWORK_STATUS "Enabled, sockets") target_compile_definitions(OpenJazz PRIVATE USE_SOCKETS) else() # SDL_net find_package(SDL_net) if(SDL_NET_FOUND) set(NETWORK_STATUS "Enabled, SDL_net") target_compile_definitions(OpenJazz PRIVATE USE_SDL_NET) else() set(NETWORK_STATUS "Disabled - no sockets or SDL_net found") endif() endif() endif() endif() # scaling set(SCALE_STATUS "Disabled") cmake_dependent_option(SCALE "Allow scaling" ON "OJ_ALLOW_SCALE;LEGACY_SDL" OFF) if(SCALE) set(SCALE_STATUS "Enabled, scale2x") target_compile_definitions(OpenJazz PRIVATE SCALE) set(OJ_LIBS_SCALE scale2x) add_subdirectory(ext/scale2x) elseif(NOT LEGACY_SDL) # FIXME: add codepath for gpu scaling set(SCALE_STATUS "Disabled (currently broken in SDL2 port)") endif() option(ENABLE_JJ2 "Enable experimental Episode 2 support (not recommended)" OFF) if(ENABLE_JJ2) target_sources(OpenJazz PRIVATE src/jj2/level/event/jj2event.cpp src/jj2/level/event/jj2event.h src/jj2/level/event/jj2eventframe.cpp src/jj2/level/jj2layer.cpp src/jj2/level/jj2level.cpp src/jj2/level/jj2level.h src/jj2/level/jj2levelframe.cpp src/jj2/level/jj2levelload.cpp src/jj2/level/jj2levelplayer.cpp src/jj2/level/jj2levelplayer.h src/jj2/level/jj2levelplayerframe.cpp) target_compile_definitions(OpenJazz PRIVATE ENABLE_JJ2) endif() # version string(TIMESTAMP OJ_DATE "%Y-%m-%d") include(GetGitRevisionDescription) git_get_exact_tag(GIT_TAG) # Do not include a hash, if we are building a release tag if(NOT GIT_TAG) # otherwise concatenate a version with hash git_describe(GIT_DESCRIPTION --exclude continuous) if(GIT_DESCRIPTION) string(REPLACE "-" ";" GIT_DESCRIPTION ${GIT_DESCRIPTION}) list(LENGTH GIT_DESCRIPTION GIT_DESCRIPTION_PARTS) if(GIT_DESCRIPTION_PARTS EQUAL 3) list(GET GIT_DESCRIPTION 0 GIT_TAG) list(GET GIT_DESCRIPTION 1 GIT_COMMITS) list(GET GIT_DESCRIPTION 2 GIT_HASH) set(GIT_STATUS "${GIT_COMMITS} commits since tag \"${GIT_TAG}\", ") string(PREPEND GIT_COMMITS "+") else() # no tags found, only hash list(GET GIT_DESCRIPTION 0 GIT_HASH) endif() # strip the g prefix string(SUBSTRING ${GIT_HASH} 1 -1 GIT_HASH) set(OJ_VERSION_GIT "git${GIT_COMMITS}@${GIT_HASH}") string(APPEND GIT_STATUS "object hash is ${GIT_HASH}") git_local_changes(GIT_DIRTY) if(GIT_DIRTY STREQUAL "DIRTY") string(APPEND OJ_VERSION_GIT "-dirty") string(APPEND GIT_STATUS ", with uncommitted changes") endif() endif() endif() set_property(SOURCE src/version.cpp PROPERTY COMPILE_DEFINITIONS OJ_VERSION="${PROJECT_VERSION}"; OJ_DATE="${OJ_DATE}"; $<$:OJ_VERSION_GIT="${OJ_VERSION_GIT}">) # project links set(OJ_BUGREPORT "https://github.com/AlisterT/openjazz/issues") set_property(SOURCE src/main.cpp PROPERTY COMPILE_DEFINITIONS OJ_URL="${PROJECT_HOMEPAGE_URL}"; OJ_BUGREPORT="${OJ_BUGREPORT}") target_link_libraries(OpenJazz argparse miniz psmplug ${OJ_LIBS_SCALE} ${OJ_LIBS_SDL} ${OJ_LIBS_NET} ${OJ_LIBS_HOST}) # platform stuff if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(OpenJazz PRIVATE "-fno-math-errno") endif() if(WIN32) # open console for debug builds set_target_properties(OpenJazz PROPERTIES WIN32_EXECUTABLE $<$>:TRUE>) # add icon target_sources(OpenJazz PRIVATE res/windows/OpenJazz.rc) elseif(EMSCRIPTEN) set_target_properties(OpenJazz PROPERTIES SUFFIX ".html") set(JS_SHELL "${CMAKE_CURRENT_SOURCE_DIR}/res/emscripten/shell.html") set_property(TARGET OpenJazz PROPERTY LINK_FLAGS "--shell-file ${JS_SHELL} -sMINIFY_HTML=0 -flto --use-port=sdl2 -sFORCE_FILESYSTEM -sEXIT_RUNTIME=1 -sASYNCIFY -sENVIRONMENT=web --closure 1 -sEXPORTED_RUNTIME_METHODS=['allocate'] -sASSERTIONS -sNO_DISABLE_EXCEPTION_CATCHING") set_source_files_properties("src/main.cpp" PROPERTIES OBJECT_DEPENDS "${JS_SHELL}") elseif(RISCOS) target_sources(OpenJazz PRIVATE src/platforms/riscos.cpp src/platforms/riscos.h) target_link_options(OpenJazz PRIVATE "-static") elf2aif(OpenJazz) elseif(3DS) target_sources(OpenJazz PRIVATE src/platforms/3ds.cpp src/platforms/3ds.h) target_link_libraries(OpenJazz -lcitro3d) ctr_generate_smdh(OpenJazz.smdh NAME "OpenJazz" DESCRIPTION "Jack Jazzrabbit 1 game engine reimplementation" AUTHOR "AlisterT & carstene1ns" ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/3ds/OpenJazz.png) ctr_create_3dsx(OpenJazz SMDH OpenJazz.smdh ${ROMFS_ARG} ${ROMFS_PATH}) elseif(WII) target_sources(OpenJazz PRIVATE src/platforms/wii.cpp src/platforms/wii.h) target_link_libraries(OpenJazz -laesnd -lfat -lwiikeyboard) ogc_create_dol(OpenJazz) elseif(PSP) target_sources(OpenJazz PRIVATE src/platforms/psp.cpp src/platforms/psp.h) target_link_libraries(OpenJazz -lGL -lpspdebug -lpspgu -lpspctrl -lpspge -lpspdisplay -lpsphprm -lpspvfpu -lpspaudio -lpspirkeyb -lpsppower) create_pbp_file(TARGET OpenJazz TITLE "\"Jazz Jackrabbit (OpenJazz)\"" ICON_PATH ${CMAKE_CURRENT_SOURCE_DIR}/res/psp/icon.png BUILD_PRX) elseif(PSVITA) # unfinished target_sources(OpenJazz PRIVATE src/platforms/psvita.cpp src/platforms/psvita.h) elseif(GP2X OR WIZ) target_sources(OpenJazz PRIVATE src/platforms/wiz.cpp src/platforms/wiz.h) elseif(HAIKU) target_sources(OpenJazz PRIVATE src/platforms/haiku.cpp src/platforms/haiku.h) elseif(UNIX) target_sources(OpenJazz PRIVATE src/platforms/xdg.cpp src/platforms/xdg.h) target_link_libraries(OpenJazz -lm) endif() # installation if(WIN32) # put everything in a folder install(TARGETS OpenJazz RUNTIME DESTINATION dist) install(CODE [[ file(GET_RUNTIME_DEPENDENCIES EXECUTABLES $ RESOLVED_DEPENDENCIES_VAR _r_deps UNRESOLVED_DEPENDENCIES_VAR _u_deps DIRECTORIES $ ) foreach(_file ${_r_deps}) string(TOLOWER ${_file} _file_lower) if(NOT ${_file_lower} MATCHES "c:[\\/]windows[\\/]system32.*") file(INSTALL DESTINATION dist TYPE SHARED_LIBRARY FOLLOW_SYMLINK_CHAIN FILES "${_file}" ) endif() endforeach() #message("UNRESOLVED_DEPENDENCIES_VAR: ${_u_deps}") ]]) elseif(3DS) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpenJazz.3dsx DESTINATION OpenJazz COMPONENT 3ds) elseif(WII) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpenJazz.dol RENAME boot.dol DESTINATION OpenJazz COMPONENT wii) install(FILES res/wii/icon.png res/wii/meta.xml res/wii/READMII.txt DESTINATION OpenJazz COMPONENT wii) elseif(PSP) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/EBOOT.PBP DESTINATION OpenJazz COMPONENT psp) install(TARGETS OpenJazz RUNTIME DESTINATION . COMPONENT debug) elseif(RISCOS) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpenJazz,ff8 res/riscos/!Run,feb res/riscos/!Boot,feb res/riscos/!Sprites,ff9 DESTINATION !OpenJazz COMPONENT riscos) elseif(PANDORA OR CANOO OR WIZ OR GP2X OR DINGOO OR GAMESHELL) # left blank for the moment (TODO: opk?) elseif(UNIX) include(GNUInstallDirs) install(TARGETS OpenJazz RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES res/unix/OpenJazz.png DESTINATION ${CMAKE_INSTALL_DATADIR}/pixmaps) install(FILES res/unix/OpenJazz.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/48x48/apps) install(FILES res/unix/OpenJazz.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) install(FILES res/unix/OpenJazz.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) else() message(VERBOSE "No idea how to install for your platform") endif() # debug information if(3DS OR WII) install(TARGETS OpenJazz RUNTIME DESTINATION . COMPONENT debug) dkp_target_generate_symbol_list(OpenJazz) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpenJazz.map ${CMAKE_CURRENT_BINARY_DIR}/OpenJazz.lst DESTINATION . COMPONENT debug) endif() # packaging for distribution set(CPACK_GENERATOR "ZIP") set(CPACK_SOURCE_GENERATOR ";") # disable set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) # split archives set(CPACK_PACKAGE_FILE_NAME "OpenJazz-${PROJECT_VERSION}") set(CPACK_ARCHIVE_DEBUG_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-debug") set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY OFF) # we do this manually include(CPack) # manual page find_program(ASCIIDOCTOR_EXECUTABLE asciidoctor) set(MANUAL_STATUS "Unavailable") set(MAN_PATH "res/unix/OpenJazz.6") if(ASCIIDOCTOR_EXECUTABLE) add_custom_command(OUTPUT ${MAN_PATH} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/res/unix COMMAND ${ASCIIDOCTOR_EXECUTABLE} -a oj_version="${PROJECT_VERSION}" -b manpage -o ${CMAKE_CURRENT_BINARY_DIR}/${MAN_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/${MAN_PATH}.adoc DEPENDS ${MAN_PATH}.adoc COMMENT "(Re-)building manual page" VERBATIM) if(UNIX) add_custom_target(man ALL DEPENDS ${MAN_PATH}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${MAN_PATH} DESTINATION ${CMAKE_INSTALL_MANDIR}/man6) set(MANUAL_STATUS "Generated") else() add_custom_target(man DEPENDS ${MAN_PATH}) set(MANUAL_STATUS "Optional") endif() else() # distribution archive? if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${MAN_PATH}) if(UNIX) install(FILES ${MAN_PATH} DESTINATION ${CMAKE_INSTALL_MANDIR}/man6) endif() set(MANUAL_STATUS "Bundled") endif() endif() unset(MAN_PATH) # Print short summary message(STATUS "") message(STATUS "OpenJazz") message(STATUS "========") message(STATUS "Version: ${PROJECT_VERSION}") if(GIT_STATUS) message(STATUS "Git status: ${GIT_STATUS}") endif() message(STATUS "Target system: ${OJ_HOST}") message(STATUS "Platform abstraction: ${SDL_STATUS}") if(ROMFS) message(STATUS "RomFS: Embedding directory \"${ROMFS_PATH}\"") endif() message(STATUS "Network: ${NETWORK_STATUS}") message(STATUS "Scaling: ${SCALE_STATUS}") if(DATAPATH) message(STATUS "Additional/System Game Data Path: \"${DATAPATH}\"") endif() message(STATUS "Manual page: ${MANUAL_STATUS}") message(STATUS "Portable Engine: ${PORTABLE_STATUS}") message(STATUS "") message(STATUS "In case something goes wrong, report bugs to ${OJ_BUGREPORT}") message(STATUS "") openjazz-20240919/CMakePresets.json000066400000000000000000000162371467303304200170620ustar00rootroot00000000000000{ "version": 6, "configurePresets": [ { "name": "base", "displayName": "Comment: base preset, all inherit from it", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/build-${presetName}", "cacheVariables": { "WANT_CCACHE": "ON" } }, { "name": "type-debug", "displayName": "Comment: build type debug preset", "hidden": true, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "type-release", "displayName": "Comment: build type release preset", "hidden": true, "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { "name": "parent", "inherits": "base", "displayName": "Comment: platform/special preset", "description": "Default build using Ninja generator", "hidden": true }, { "name": "debug", "displayName": "Native (Debug)", "inherits": [ "parent", "type-debug" ] }, { "name": "release", "displayName": "Native (Release)", "inherits": [ "parent", "type-release" ] }, { "name": "asan-parent", "inherits": "base", "displayName": "Comment: platform/special preset", "description": "ASAN build using Ninja generator", "cacheVariables": { "WANT_ASAN": "ON" }, "hidden": true }, { "name": "asan-debug", "displayName": "ASAN (Debug)", "inherits": [ "asan-parent", "type-debug" ] }, { "name": "3ds-parent", "inherits": "base", "displayName": "Comment: platform/special preset", "description": "Homebrew build using Ninja generator", "toolchainFile": "$env{DEVKITPRO}/cmake/3DS.cmake", "hidden": true }, { "name": "3ds-debug", "displayName": "Nintendo 3DS (Debug)", "inherits": [ "3ds-parent", "type-debug" ] }, { "name": "3ds-release", "displayName": "Nintendo 3DS (Release)", "inherits": [ "3ds-parent", "type-release" ] }, { "name": "wii-parent", "inherits": "base", "displayName": "Comment: platform/special preset", "description": "Homebrew build using Ninja generator", "toolchainFile": "$env{DEVKITPRO}/cmake/Wii.cmake", "hidden": true }, { "name": "wii-debug", "displayName": "Nintendo Wii (Debug)", "inherits": [ "wii-parent", "type-debug" ] }, { "name": "wii-release", "displayName": "Nintendo Wii (Release)", "inherits": [ "wii-parent", "type-release" ] }, { "name": "psp-parent", "inherits": "base", "displayName": "Comment: platform/special preset", "description": "Homebrew build using Ninja generator", "toolchainFile": "$env{PSPDEV}/psp/share/pspdev.cmake", "hidden": true }, { "name": "psp-debug", "displayName": "Sony PSP (Debug)", "inherits": [ "psp-parent", "type-debug" ] }, { "name": "psp-release", "displayName": "Sony PSP (Release)", "inherits": [ "psp-parent", "type-release" ] } ], "buildPresets": [ { "name": "debug", "configurePreset": "debug" }, { "name": "release", "configurePreset": "release" }, { "name": "asan-debug", "configurePreset": "asan-debug" }, { "name": "3ds-debug", "configurePreset": "3ds-debug" }, { "name": "3ds-release", "configurePreset": "3ds-release" }, { "name": "wii-debug", "configurePreset": "wii-debug" }, { "name": "wii-release", "configurePreset": "wii-release" }, { "name": "psp-debug", "configurePreset": "psp-debug" }, { "name": "psp-release", "configurePreset": "psp-release" } ], "packagePresets": [ { "name": "debug", "configurePreset": "debug" }, { "name": "release", "configurePreset": "release" }, { "name": "asan-debug", "configurePreset": "asan-debug" }, { "name": "3ds-debug", "configurePreset": "3ds-debug" }, { "name": "3ds-release", "configurePreset": "3ds-release" }, { "name": "wii-debug", "configurePreset": "wii-debug" }, { "name": "wii-release", "configurePreset": "wii-release" }, { "name": "psp-debug", "configurePreset": "psp-debug" }, { "name": "psp-release", "configurePreset": "psp-release" } ], "workflowPresets": [ { "name": "debug", "steps": [ { "type": "configure", "name": "debug" }, { "type": "build", "name": "debug" } ] }, { "name": "release", "steps": [ { "type": "configure", "name": "release" }, { "type": "build", "name": "release" } ] }, { "name": "asan-debug", "steps": [ { "type": "configure", "name": "asan-debug" }, { "type": "build", "name": "asan-debug" } ] }, { "name": "3ds-debug", "steps": [ { "type": "configure", "name": "3ds-debug" }, { "type": "build", "name": "3ds-debug" }, { "type": "package", "name": "3ds-debug" } ] }, { "name": "3ds-release", "steps": [ { "type": "configure", "name": "3ds-release" }, { "type": "build", "name": "3ds-release" }, { "type": "package", "name": "3ds-release" } ] }, { "name": "wii-debug", "steps": [ { "type": "configure", "name": "wii-debug" }, { "type": "build", "name": "wii-debug" }, { "type": "package", "name": "wii-debug" } ] }, { "name": "wii-release", "steps": [ { "type": "configure", "name": "wii-release" }, { "type": "build", "name": "wii-release" }, { "type": "package", "name": "wii-release" } ] }, { "name": "psp-debug", "steps": [ { "type": "configure", "name": "psp-debug" }, { "type": "build", "name": "psp-debug" }, { "type": "package", "name": "psp-debug" } ] }, { "name": "psp-release", "steps": [ { "type": "configure", "name": "psp-release" }, { "type": "build", "name": "psp-release" }, { "type": "package", "name": "psp-release" } ] } ] } openjazz-20240919/COPYING000066400000000000000000000432541467303304200146730ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. openjazz-20240919/Makefile000066400000000000000000000047711467303304200153010ustar00rootroot00000000000000# OpenJazz makefile # Sane defaults CXX ?= g++ -std=c++14 CXXFLAGS ?= -g -Wall -O2 DEFINES = -DSCALE -DPORTABLE CPPFLAGS = $(DEFINES) -Isrc -Iext/scale2x -Iext/psmplug -Iext/miniz -Iext/argparse # Network support CXXFLAGS += -DUSE_SOCKETS ifeq ($(OS),Windows_NT) # Only needed under Windows. LIBS += -lws2_32 endif # SDL1.2 or SDL2 SDLCONFIG ?= sdl-config CXXFLAGS += $(shell $(SDLCONFIG) --cflags) LIBS += $(shell $(SDLCONFIG) --libs) LIBS += -lm # Libraries OJEXTLIBOBJ = \ ext/argparse/argparse.o \ ext/miniz/miniz.o \ ext/psmplug/fastmix.o \ ext/psmplug/load_psm.o \ ext/psmplug/psmplug.o \ ext/psmplug/snd_dsp.o \ ext/psmplug/snd_flt.o \ ext/psmplug/snd_fx.o \ ext/psmplug/sndfile.o \ ext/psmplug/sndmix.o \ ext/scale2x/scale2x.o \ ext/scale2x/scale3x.o \ ext/scale2x/scalebit.o # Main engine OJOBJS = \ src/game/clientgame.o \ src/game/game.o \ src/game/gamemode.o \ src/game/localgame.o \ src/game/servergame.o \ src/io/controls.o \ src/io/file.o \ src/io/log.o \ src/io/gfx/anim.o \ src/io/gfx/font.o \ src/io/gfx/paletteeffects.o \ src/io/gfx/sprite.o \ src/io/gfx/video.o \ src/io/network.o \ src/io/sound.o \ src/level/level.o \ src/level/movable.o \ src/main.o \ src/menu/filemenu.o \ src/menu/gamemenu.o \ src/menu/mainmenu.o \ src/menu/menu.o \ src/menu/plasma.o \ src/menu/setupmenu.o \ src/player/player.o \ src/setup.o \ src/util.o \ src/version.o # Episode 1 OJ1OBJS = \ src/jj1/bonuslevel/jj1bonuslevel.o \ src/jj1/bonuslevel/jj1bonuslevelplayer.o \ src/jj1/level/event/jj1bridge.o \ src/jj1/level/event/jj1event.o \ src/jj1/level/event/jj1guardians.o \ src/jj1/level/event/jj1standardevent.o \ src/jj1/level/jj1bird.o \ src/jj1/level/jj1bullet.o \ src/jj1/level/jj1demolevel.o \ src/jj1/level/jj1level.o \ src/jj1/level/jj1levelframe.o \ src/jj1/level/jj1levelload.o \ src/jj1/level/jj1levelplayer.o \ src/jj1/level/jj1levelplayerframe.o \ src/jj1/planet/jj1planet.o \ src/jj1/save/jj1save.o \ src/jj1/scene/jj1scene.o \ src/jj1/scene/jj1sceneload.o OBJS = $(OJEXTLIBOBJ) $(OJOBJS) $(OJ1OBJS) # Stamp DEPR = .mk-stamp-depr # Build rules .PHONY: clean OpenJazz: $(DEPR) $(OBJS) @-echo [LD] $@ @$(CXX) -o OpenJazz $(LDFLAGS) $(OBJS) $(LIBS) $(DEPR): @-echo [WARNING] This Makefile is deprecated! Please use CMake, if possible. @-echo Report problems at https://github.com/AlisterT/openjazz/issues @touch $(DEPR) %.o: %.cpp @-echo [CXX] $< @$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ clean: @-echo Cleaning... @rm -f OpenJazz $(OBJS) $(DEPR) openjazz-20240919/PLATFORMS.md000066400000000000000000000013141467303304200155200ustar00rootroot00000000000000 # Platforms _This is currently unfinished._ Use a cross-compiler, enable specific platform code with option: ## Embedded Linux systems * Pandora: `-DPANDORA=ON` * GP2X Canoo: `-DCAANOO=ON` * GP2X Wiz: `-DWIZ=ON` * GP2X: `-DGP2X=ON` * Dingoo: `-DDINGOO=ON` * GameShell: `-DGAMESHELL=ON` ## RISC OS `-DRISCOS=ON` ## Homebrew toolchains Use the provided CMake Toolchain file or wrapper script: ### Wii `cmake -DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/Wii.cmake` ### 3DS `cmake -DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake` #### Additional options: * Embed a directory in the executable: `-DROMFS=ON` ### PSP Use `psp-cmake`. ## Haiku Works natively. ## Windows Works natively. openjazz-20240919/README.md000066400000000000000000000062401467303304200151110ustar00rootroot00000000000000 # ![OJ Logo][logo] OpenJazz ## About *OpenJazz* is a free, open-source version of the classic Jazz Jackrabbit™ games. OpenJazz can be compiled on a wide range of operating systems, including Windows, macOS, GNU/Linux and BSD flavors. Also ports are available for some homebrew platforms, consoles and handhelds. See [Platforms](PLATFORMS.md) for details. You can even play it [inside your web browser][web port]. To play, you will need the files from one of the original games. With the demise of DOS-based operating systems, it has become necessary to use emulators to play old DOS games. Jazz Jackrabbit™ deserves more - and would benefit greatly from new features. *Jazz Jackrabbit™* is a PC platform game. Produced by Epic Games (then Epic MegaGames), it was first released in 1994. The fast-paced, colourful gameplay proved popular, and the game won PC Format's Arcade Game of the Year award. Many people still fondly recall the shareware versions. ## History OpenJazz was started on the 23rd of August, 2005, by Alister Thomson. Academic pressures put the project on hold until late December 2005. The source code was released on the 25th, and the first version with a degree of playability was released on the 15th of January, 2006. Since then, a variety of ports have been released by other people. A lot of them have since been merged back into the main code base. More academic pressures meant there were few updates over the following few years, but in 2009 a multiplayer version was released. After that, more eye candy features have made it into the engine. These are features not directly involving the levels. However, without bonus levels, movie playback and the menu plasma effect, OpenJazz would be incomplete. In 2014, the project has been moved to GitHub to allow others to easily propose changes, report bugs and also fix them. This also lead to more ports being made. Nowadays, development has slowed down. The original author is involved with other projects and there is no clear roadmap for new features, besides striving for a "true" and faithful Jazz Jackrabbit™ adaption on all possible platforms. GitHub user [carstene1ns](https://github.com/carstene1ns) has cleaned and restructured parts of the code base, integrated some ports and tries to resolve bug reports. ## License OpenJazz is available under the GNU General Public License version 2 or later, see [licenses.txt](licenses.txt) for additional information and other included software. ## [Controls](res/unix/OpenJazz.6.adoc#ingame-controls) (See manual) ## [Building](BUILDING.md) ## Running [![Play in Browser][web badge]][web port] Execute `OpenJazz`. Depending on the platform and compile time options, the data files are expected to be under different paths (alongside the executable works always as a general fallback). You can also specifiy a game folder as command line argument. ## Authors Original author: Alister Thomson (alister_j_t at yahoo dot com) [Additional Authors](res/unix/OpenJazz.6.adoc#authors) (See manual) ## Homepage http://alister.eu/jazz/oj/ [logo]: res/unix/OpenJazz.png [web badge]: https://img.shields.io/badge/Play_in-Browser-blue?style=plastic [web port]: https://openjazz.github.io openjazz-20240919/builds/000077500000000000000000000000001467303304200151125ustar00rootroot00000000000000openjazz-20240919/builds/cmake/000077500000000000000000000000001467303304200161725ustar00rootroot00000000000000openjazz-20240919/builds/cmake/Find-Restrict-Keyword.cmake000066400000000000000000000027001467303304200232720ustar00rootroot00000000000000 include(CheckCXXSourceCompiles) # This module checks if the C++ compiler supports the restrict keyword or # some variant of it: # - restrict (The standard C99 keyword, not in C++ standard, Windows VS has it) # - __restrict and __restrict__ (G++ has them) # - _Restrict (seems to be used by Sun's compiler) # Some C++ compilers do not support any variant, in which case the RESTRICT_KEYWORD variable is empty if(FOUND_RESTRICT_KEYWORD) return() # already tested endif() set(_MSG "Looking for restrict keyword") message(STATUS ${_MSG}) set(Find_restrict_KEYWORD_SRC " char f(const char * PLACEHOLDER_RESTRICT_KEYWORD x){ return *x; } int main(int argc, char *argv[]) { return 0; } ") set(CMAKE_REQUIRED_QUIET TRUE) # silence tests set(RESTRICT_KEYWORD) # empty set(Find_restrict_KEYWORDS restrict __restrict __restrict__ _Restrict) # candidates foreach(restrict_KEYWORD IN LISTS Find_restrict_KEYWORDS) string(REPLACE "PLACEHOLDER_RESTRICT_KEYWORD" "${restrict_KEYWORD}" _SRC "${Find_restrict_KEYWORD_SRC}") check_cxx_source_compiles("${_SRC}" HAVE_KEYWORD_${restrict_KEYWORD}) if(HAVE_KEYWORD_${restrict_KEYWORD}) set(RESTRICT_KEYWORD ${restrict_KEYWORD}) break() # end loop endif() endforeach() if(RESTRICT_KEYWORD) message(STATUS "${_MSG} - found \"${RESTRICT_KEYWORD}\"") set(FOUND_RESTRICT_KEYWORD 1 CACHE INTERNAL "Restrict keyword has been found") else() message(STATUS "${_MSG} - not found") endif() openjazz-20240919/builds/cmake/GetGitRevisionDescription.cmake000066400000000000000000000224721467303304200243110ustar00rootroot00000000000000# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_describe_working_tree( [ ...]) # # Returns the results of git describe on the working tree (--dirty option), # and adjusting the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # git_local_changes() # # Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. # Uses the return code of "git diff-index --quiet HEAD --". # Does not regard untracked files. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2020 Ryan Pavlik # http://academic.cleardefinition.com # # Copyright 2009-2013, Iowa State University. # Copyright 2013-2020, Ryan Pavlik # Copyright 2013-2020, Contributors # SPDX-License-Identifier: BSL-1.0 # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) # Function _git_find_closest_git_dir finds the next closest .git directory # that is part of any directory in the path defined by _start_dir. # The result is returned in the parent scope variable whose name is passed # as variable _git_dir_var. If no .git directory can be found, the # function returns an empty string via _git_dir_var. # # Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and # neither foo nor bar contain a file/directory .git. This wil return # C:/bla/.git # function(_git_find_closest_git_dir _start_dir _git_dir_var) set(cur_dir "${_start_dir}") set(git_dir "${_start_dir}/.git") while(NOT EXISTS "${git_dir}") # .git dir not found, search parent directories set(git_previous_parent "${cur_dir}") get_filename_component(cur_dir "${cur_dir}" DIRECTORY) if(cur_dir STREQUAL git_previous_parent) # We have reached the root directory, we are not in git set(${_git_dir_var} "" PARENT_SCOPE) return() endif() set(git_dir "${cur_dir}/.git") endwhile() set(${_git_dir_var} "${git_dir}" PARENT_SCOPE) endfunction() function(get_git_head_revision _refspecvar _hashvar) _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) else() set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) endif() if(NOT "${GIT_DIR}" STREQUAL "") file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" "${GIT_DIR}") if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) # We've gone above the CMake root dir. set(GIT_DIR "") endif() endif() if("${GIT_DIR}" STREQUAL "") set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() # Check if the current source dir is a git submodule or a worktree. # In both cases .git is a file instead of a directory. # if(NOT IS_DIRECTORY ${GIT_DIR}) # The following git command will return a non empty string that # points to the super project working tree if the current # source dir is inside a git submodule. # Otherwise the command will return an empty string. # execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT "${out}" STREQUAL "") # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule}) string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree file(READ ${GIT_DIR} worktree_ref) # The .git directory contains a path to the worktree information directory # inside the parent git repo of the worktree. # string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref}) string(STRIP ${git_worktree_dir} git_worktree_dir) _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") endif() else() set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${HEAD_SOURCE_FILE}") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process( COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_describe_working_tree _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() execute_process( COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_local_changes _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() execute_process( COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(res EQUAL 0) set(${_var} "CLEAN" PARENT_SCOPE) else() set(${_var} "DIRTY" PARENT_SCOPE) endif() endfunction() openjazz-20240919/builds/cmake/GetGitRevisionDescription.cmake.in000066400000000000000000000027661467303304200247220ustar00rootroot00000000000000# # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2023 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright 2009-2012, Iowa State University # Copyright 2011-2023, Contributors # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # SPDX-License-Identifier: BSL-1.0 set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) else() if(EXISTS "@GIT_DIR@/packed-refs") configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() endif() endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() openjazz-20240919/builds/cmake/OJ-misc.cmake000066400000000000000000000061211467303304200204350ustar00rootroot00000000000000 # ninja/parallel builds disable output colors, forcing them here. # Needs to be at global scope to catch all targets. # cmake will have this build into 3.24 if(CMAKE_VERSION VERSION_LESS "3.24") option(CMAKE_COLOR_DIAGNOSTICS "Always produce ANSI-colored output" OFF) if(CMAKE_COLOR_DIAGNOSTICS) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") add_compile_options("-fdiagnostics-color=always") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_compile_options("-fcolor-diagnostics") endif() endif() endif() # compiler cache option(WANT_CCACHE "Use ccache to speed up rebuilds" OFF) if(WANT_CCACHE) if(NOT FOUND_CCACHE) find_program(CCACHE_EXECUTABLE ccache) if(CCACHE_EXECUTABLE) message(STATUS "Using ccache as CXX compiler launcher") set(FOUND_CCACHE 1 CACHE INTERNAL "Ccache has been found") endif() endif() if(CCACHE_EXECUTABLE) set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}") endif() endif() # warnings if(MSVC) add_compile_options(/W4) else() add_compile_options(-Wall -Wextra) endif() # global scope, since ASAN needs to catch all targets option(WANT_ASAN "build with address sanitizer" OFF) if(WANT_ASAN) message(STATUS "Building with address sanitizer") add_compile_options(-fno-omit-frame-pointer -fsanitize=address) add_link_options(-fno-omit-frame-pointer -fsanitize=address) endif() # formatting and static analysis file(GLOB_RECURSE ALL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h) if(NOT FOUND_ASTYLE) find_program(ASTYLE_EXECUTABLE astyle) if(ASTYLE_EXECUTABLE) message(STATUS "Found source code formatter: astyle") set(FOUND_ASTYLE 1 CACHE INTERNAL "Astyle has been found") endif() endif() if(ASTYLE_EXECUTABLE) list(APPEND ASTYLE_ARGS --suffix=none --style=attach --indent=tab=4 --pad-oper --pad-header --unpad-paren --max-code-length=100 --break-after-logical --attach-closing-while --align-pointer=type --align-reference=name --indent-classes --indent-preproc-block --indent-switches --min-conditional-indent=0) add_custom_target(format COMMAND ${ASTYLE_EXECUTABLE} ${ASTYLE_ARGS} ${ALL_SRC} COMMENT "Running astyle to format source code" VERBATIM) endif() if(NOT FOUND_CPPCHECK) find_program(CPPCHECK_EXECUTABLE cppcheck) if(CPPCHECK_EXECUTABLE) message(STATUS "Found static analysis tool: cppcheck") set(FOUND_CPPCHECK 1 CACHE INTERNAL "Cppcheck has been found") endif() endif() if(CPPCHECK_EXECUTABLE) list(APPEND CPPCHECK_ARGS --enable=warning,style,performance,portability,unusedFunction --std=c++11 --language=c++ -I${CMAKE_CURRENT_SOURCE_DIR}/src -U__SYMBIAN32__ -UUIQ3 -UENABLE_JJ2 # unmaintained -UGP2X -UWIZ -UDINGOO -UCAANOO -UGAMESHELL # contributed -UPSP -U__vita__ -U_3DS -U__wii__ # homebrew -U__riscos__ #--enable=information ) add_custom_target(cppcheck COMMAND ${CPPCHECK_EXECUTABLE} ${CPPCHECK_ARGS} ${ALL_SRC} COMMENT "Running cppcheck for static analysis" USES_TERMINAL VERBATIM) endif() if(NOT FOUND_ASTYLE AND NOT FOUND_CPPCHECK) message(STATUS "No source code formatter or static analysis tool enabled.") endif() openjazz-20240919/builds/cmake/Platform-Helpers.cmake000066400000000000000000000073661467303304200223740ustar00rootroot00000000000000 set(OJ_HOST "Unknown") set(PLATFORM_LIST "") set(OJ_ALLOW_SCALE ON) set(OJ_LIBS_NET) set(OJ_LIBS_HOST) set(OJ_DEFAULT_PORTABLE ON) option(PANDORA "Build for Pandora" OFF) # arm-none-linux-gnueabi option(CAANOO "Build for GP2X Canoo" OFF) # arm-gph-linux-gnueabi option(WIZ "Build for GP2X Wiz" OFF) # arm-openwiz-linux-gnu option(GP2X "Build for GP2X" OFF) # arm-open2x-linux option(DINGOO "Build for Dingoo" OFF) # mipsel-linux* option(GAMESHELL "Build for GameShell" OFF) # armv7l-unknown-linux-gnueabihf option(RISCOS "Build for RISC OS" OFF) # arm-unknown-riscos # Official/Homebrew Toolchain files define these if(NINTENDO_3DS) set(3DS ON) set(OJ_HOST "3DS") list(APPEND PLATFORM_LIST ${OJ_HOST}) set(OJ_ALLOW_SCALE OFF) option(ROMFS "Embed a directory in the executable" OFF) set(ROMFS_PATH "romfs" CACHE PATH "Directory to include in executable as romfs:/ path") set(ROMFS_ARG "NO_ROMFS_IGNORE_ME") if(ROMFS) set(ROMFS_ARG "ROMFS") endif() elseif(NINTENDO_WII) set(WII ON) set(OJ_HOST "Wii") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(HAIKU) add_compile_definitions(_BSD_SOURCE) set(OJ_LIBS_HOST "-lbe") set(OJ_LIBS_NET "-lnetwork") set(OJ_HOST "Haiku") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(WIN32) set(OJ_LIBS_NET "-lws2_32") elseif(EMSCRIPTEN) add_compile_definitions(EMSCRIPTEN) set(OJ_HOST "Web") set(OJ_ALLOW_SCALE OFF) elseif(PSP) set(OJ_HOST "PSP") set(OJ_ALLOW_SCALE OFF) endif() if(NINTENDO_3DS OR NINTENDO_WII OR PSP) add_compile_options(-fno-rtti) endif() if(PANDORA OR CANOO OR WIZ OR GP2X) add_compile_options(-fsigned-char) endif() if(PANDORA) add_compile_definitions(PANDORA) set(OJ_LIBS_HOST "-lts") set(OJ_HOST "Pandora") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(CAANOO) add_compile_definitions(CAANOO) set(OJ_HOST "GP2X Caanoo") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(WIZ) add_compile_definitions(WIZ) set(OJ_HOST "GP2X Wiz") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(GP2X) add_compile_definitions(GP2X) set(OJ_HOST "GP2X") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(DINGOO) add_compile_definitions(DINGOO) set(OJ_HOST "Dingoo") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(GAMESHELL) add_compile_definitions(GAMESHELL) set(OJ_HOST "ClockworkPi GameShell") list(APPEND PLATFORM_LIST ${OJ_HOST}) elseif(RISCOS) set(OJ_HOST "RISC OS") list(APPEND PLATFORM_LIST ${OJ_HOST}) find_program(ELF2AIF_EXECUTABLE elf2aif) function(elf2aif target) get_target_property(TARGET_BINARY_DIR ${target} BINARY_DIR) get_target_property(TARGET_OUTPUT_NAME ${target} OUTPUT_NAME) if(NOT TARGET_OUTPUT_NAME) set(TARGET_OUTPUT_NAME "${target}") endif() set(AIF_OUTPUT "${TARGET_BINARY_DIR}/${TARGET_OUTPUT_NAME},ff8") add_custom_command(TARGET ${target} POST_BUILD COMMAND "${ELF2AIF_EXECUTABLE}" "$" "${AIF_OUTPUT}" BYPRODUCTS "${AIF_OUTPUT}" COMMENT "Converting ${target} to AIF format" VERBATIM) endfunction() endif() # sanity check list(LENGTH PLATFORM_LIST NUM_PLATFORMS) if(${NUM_PLATFORMS} GREATER 1) if(${CMAKE_VERSION} VERSION_LESS 3.12) set(PLATFORMS ${PLATFORM_LIST}) else() list(JOIN PLATFORM_LIST ", " PLATFORMS) endif() message(FATAL_ERROR "Can only target one platform at a time! Got ${PLATFORMS}.") endif() # autodetect if(${OJ_HOST} STREQUAL "Unknown") set(OJ_HOST ${CMAKE_SYSTEM_NAME}) set(OJ_ALLOW_NEW_SDL TRUE) if(UNIX) # usually we do a system-wide installation set(OJ_DEFAULT_PORTABLE OFF) endif() endif() # choose SDL library for Linux/Windows/Mac/etc., but not homebrew platforms cmake_dependent_option(LEGACY_SDL "Build for SDL 1.2" OFF "OJ_ALLOW_NEW_SDL" ON) # Endianess check include(TestBigEndian) test_big_endian(PLATFORM_BIGENDIAN) if(PLATFORM_BIGENDIAN) add_compile_definitions(WORDS_BIGENDIAN=1) endif() openjazz-20240919/builds/symbian/000077500000000000000000000000001467303304200165545ustar00rootroot00000000000000openjazz-20240919/builds/symbian/s60v3/000077500000000000000000000000001467303304200174355ustar00rootroot00000000000000openjazz-20240919/builds/symbian/s60v3/BLD.INF000066400000000000000000000001221467303304200203670ustar00rootroot00000000000000PRJ_PLATFORMS GCCE WINSCW THUMB PRJ_MMPFILES gnumakefile icons.mk .\openjazz.mmp openjazz-20240919/builds/symbian/s60v3/icons.mk000066400000000000000000000010101467303304200210710ustar00rootroot00000000000000ifeq (WINS,$(findstring WINS, $(PLATFORM))) ZDIR=$(EPOCROOT)epoc32\release\$(PLATFORM)\$(CFG)\Z else ZDIR=$(EPOCROOT)epoc32\data\z endif TARGETDIR=$(ZDIR)\RESOURCE\APPS ICONTARGETFILENAME=$(TARGETDIR)\openjazz.mif do_nothing : @rem do_nothing MAKMAKE : do_nothing BLD : do_nothing CLEAN : do_nothing LIB : do_nothing CLEANLIB : do_nothing RESOURCE : mifconv $(ICONTARGETFILENAME) \ /c32 openjazz.svg FREEZE : do_nothing SAVESPACE : do_nothing RELEASABLES : @echo $(ICONTARGETFILENAME) FINAL : do_nothing openjazz-20240919/builds/symbian/s60v3/openjazz.mmp000066400000000000000000000051041467303304200220100ustar00rootroot00000000000000target openjazz.exe targetpath \sys\bin targettype exe SYSTEMINCLUDE \epoc32\include\ESDL SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\..\..\src sourcepath ..\..\..\src\platforms source symbian.cpp SOURCEPATH . START RESOURCE openjazz.rss HEADER TARGETPATH \Resource\Apps LANG SC END START RESOURCE openjazz_reg.rss TARGETPATH \private\10003a3f\apps END EPOCSTACKSIZE 80000 EPOCHEAPSIZE 8192000 32768000 UID 0x100039ce 0xA000A005 LIBRARY cone.lib eikcore.lib LIBRARY euser.lib apparc.lib fbscli.lib LIBRARY apgrfx.lib staticlibrary modpluglibpsm.lib staticlibrary esdl.lib sdl_mixer.lib sdl_net.lib zlib.lib LIBRARY gdi.lib hal.lib bitgdi.lib LIBRARY mediaclientaudiostream.lib efsrv.lib ws32.lib library bafl.lib estlib.lib esock.lib insock.lib library avkon.lib MACRO S60V3 CAPABILITY NetworkServices LocalServices sourcepath ..\..\..\src userinclude ..\..\..\src source main.cpp util.cpp baselevel.cpp sourcepath ..\..\..\src\bonus userinclude ..\..\..\src\bonus source bonus.cpp sourcepath ..\..\..\src\scene userinclude ..\..\..\src\scene source scene.cpp sceneload.cpp sourcepath ..\..\..\src\game userinclude ..\..\..\src\game source clientgame.cpp game.cpp gamemode.cpp servergame.cpp sourcepath ..\..\..\src\planet userinclude ..\..\..\src\planet source planet.cpp sourcepath ..\..\..\src\io userinclude ..\..\..\src\io source controls.cpp file.cpp network.cpp sound.cpp sourcepath ..\..\..\src\io\gfx userinclude ..\..\..\src\io\gfx source anim.cpp font.cpp paletteeffects.cpp sprite.cpp video.cpp sourcepath ..\..\..\src\level userinclude ..\..\..\src\level source bullet.cpp demolevel.cpp level.cpp levelframe.cpp levelload.cpp movable.cpp sourcepath ..\..\..\src\level\event userinclude ..\..\..\src\level\event source event.cpp eventframe.cpp bridge.cpp guardians.cpp sourcepath ..\..\..\src\menu userinclude ..\..\..\src\menu source gamemenu.cpp mainmenu.cpp menu.cpp setupmenu.cpp plasma.cpp sourcepath ..\..\..\src\player userinclude ..\..\..\src\player source bird.cpp bonusplayer.cpp jj2levelplayer.cpp jj2levelplayerframe.cpp levelplayer.cpp levelplayerframe.cpp player.cpp userinclude ..\..\..\src\jj2level sourcepath ..\..\..\src\jj2level source jj2layer.cpp jj2level.cpp jj2levelframe.cpp jj2levelload.cpp userinclude ..\..\..\src\jj2level\jj2event sourcepath ..\..\..\src\jj2level\jj2event source jj2event.cpp jj2eventframe.cpp MACRO USE_SDL_NET MACRO USE_MODPLUG MACRO FULLSCREEN_ONLY OPTION GCCE -Wno-multichar -Wno-reorder -Wno-unused -Wno-format -fsigned-char OPTION CW -char signed openjazz-20240919/builds/symbian/s60v3/openjazz.pkg000066400000000000000000000020101467303304200217710ustar00rootroot00000000000000; ; Basic install file for openjazz ; ; Languages ; none - English only by default ; Installation header ; Only one component name as we only support English ; UID is the app's UID - &EN ; List of localised vendor names - one per language. At least one must be provided (English [EN]). ; List must correspond to list of languages specified elsewhere in the .pkg %{"SomeOne"} ; The non-localised, globally unique vendor name (mandatory) :"SomeOne" #{"OpenJazz"},(0xA000A005),0,2,1 ;Supports Series 60 v 3.0 [0x101F7961], 0, 0, 0, {"Series60ProductID"} ; Application file "\epoc32\release\gcce\urel\openjazz.exe"-"!:\sys\bin\openjazz.exe" "\epoc32\data\z\resource\apps\openjazz.rsc"-"!:\resource\apps\openjazz.rsc" "\epoc32\data\z\private\10003a3f\apps\openjazz_reg.rsc"-"!:\private\10003a3f\import\apps\openjazz_reg.rsc" "\epoc32\data\z\resource\apps\openjazz.mif"-"!:\resource\apps\openjazz.mif" ""-"c:\data\openjazz\sdl.ini",FN ""-"c:\data\openjazz\openjazz.cfg",FN ; Required files ; None ; Component .sis files ; None openjazz-20240919/builds/symbian/s60v3/openjazz.rss000066400000000000000000000015271467303304200220330ustar00rootroot00000000000000NAME OJZZ // Include definitions of resource STRUCTS used by this // resource script #include #include // Include the standard Eikon resource ids #include RESOURCE RSS_SIGNATURE { } RESOURCE TBUF16 { buf=""; } RESOURCE EIK_APP_INFO { } // This file localise the applications icons and caption RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info { caption_and_icon = { CAPTION_AND_ICON_INFO { // The caption text is defined in the rls file caption = "OpenJazz"; // Icons are used to represent applications in the // application launcher and application title bar. // The number_of_icons value identifies how many icons // that exist in the icon_file. number_of_icons = 1; // Using the application icons. icon_file = "\\Resource\\Apps\\OpenJazz.mif"; } }; } openjazz-20240919/builds/symbian/s60v3/openjazz.svg000066400000000000000000003437431467303304200220340ustar00rootroot00000000000000 image/svg+xml openjazz-20240919/builds/symbian/s60v3/openjazz_reg.rss000066400000000000000000000014261467303304200226660ustar00rootroot00000000000000// All registration files need to #include appinfo.rh. #include #include // All registration files must define UID2, which is always // KUidAppRegistrationResourceFile, and UID3, which is the application's UID. UID2 KUidAppRegistrationResourceFile UID3 0xA000A005 // application UID // Registration file need to containo an APP_REGISTRATION_INFO resource that // minimally needs to provide the name of the application binary (using the // app_file statement). RESOURCE APP_REGISTRATION_INFO { app_file = "openjazz"; // filename of application binary (minus extension) // Specify the location of the localisable icon/caption definition file localisable_resource_file = "\\Resource\\Apps\\openjazz"; localisable_resource_id = R_LOCALISABLE_APP_INFO; } openjazz-20240919/builds/symbian/uiq3/000077500000000000000000000000001467303304200174355ustar00rootroot00000000000000openjazz-20240919/builds/symbian/uiq3/BLD.INF000066400000000000000000000001071467303304200203720ustar00rootroot00000000000000PRJ_PLATFORMS WINS ARMI GCCE WINSCW THUMB PRJ_MMPFILES .\openjazz.mmp openjazz-20240919/builds/symbian/uiq3/openjazz.mmp000066400000000000000000000061461467303304200220170ustar00rootroot00000000000000target openjazz.exe targetpath \sys\bin targettype exe SYSTEMINCLUDE \epoc32\include\ESDL SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\..\..\src sourcepath ..\..\..\src\platforms source symbian.cpp SOURCEPATH . START RESOURCE openjazz.rss HEADER TARGETPATH \Resource\Apps LANG SC END START RESOURCE openjazz_reg.rss TARGETPATH \private\10003a3f\apps END EPOCSTACKSIZE 80000 EPOCHEAPSIZE 8192000 32768000 sourcepath . START BITMAP OpenJazz.mbm HEADER TARGETPATH \Resource\Apps SOURCEPATH . // Source Color-depth Source-bitmap-list // c denotes whether the bitmap is a colour bitmap and the digits represent the // colour-depth of the bitmap and the bitmap mask respectively SOURCE c24 openjazz_s.bmp SOURCE 8 openjazz_sm.bmp SOURCE c24 openjazz_l.bmp SOURCE 8 openjazz_lm.bmp SOURCE c24 openjazz_xl.bmp SOURCE 8 openjazz_xlm.bmp END UID 0x100039ce 0xA000A005 LIBRARY cone.lib eikcore.lib LIBRARY euser.lib apparc.lib fbscli.lib LIBRARY apgrfx.lib staticlibrary modpluglibpsm.lib staticlibrary esdl.lib sdl_mixer.lib sdl_net.lib zlib.lib LIBRARY gdi.lib hal.lib bitgdi.lib LIBRARY mediaclientaudiostream.lib efsrv.lib ws32.lib library bafl.lib estlib.lib esock.lib insock.lib library qikcore.lib MACRO UIQ3 CAPABILITY NetworkServices LocalServices sourcepath ..\..\..\src userinclude ..\..\..\src source main.cpp util.cpp baselevel.cpp sourcepath ..\..\..\src\bonus userinclude ..\..\..\src\bonus source bonus.cpp sourcepath ..\..\..\src\scene userinclude ..\..\..\src\scene source scene.cpp sceneload.cpp sourcepath ..\..\..\src\game userinclude ..\..\..\src\game source clientgame.cpp game.cpp gamemode.cpp servergame.cpp sourcepath ..\..\..\src\planet userinclude ..\..\..\src\planet source planet.cpp sourcepath ..\..\..\src\io userinclude ..\..\..\src\io source controls.cpp file.cpp network.cpp sound.cpp sourcepath ..\..\..\src\io\gfx userinclude ..\..\..\src\io\gfx source anim.cpp font.cpp paletteeffects.cpp sprite.cpp video.cpp sourcepath ..\..\..\src\level userinclude ..\..\..\src\level source bullet.cpp demolevel.cpp level.cpp levelframe.cpp levelload.cpp movable.cpp sourcepath ..\..\..\src\level\event userinclude ..\..\..\src\level\event source event.cpp eventframe.cpp bridge.cpp guardians.cpp sourcepath ..\..\..\src\menu userinclude ..\..\..\src\menu source gamemenu.cpp mainmenu.cpp menu.cpp setupmenu.cpp plasma.cpp sourcepath ..\..\..\src\player userinclude ..\..\..\src\player source bird.cpp bonusplayer.cpp jj2levelplayer.cpp jj2levelplayerframe.cpp levelplayer.cpp levelplayerframe.cpp player.cpp userinclude ..\..\..\src\jj2level sourcepath ..\..\..\src\jj2level source jj2layer.cpp jj2level.cpp jj2levelframe.cpp jj2levelload.cpp userinclude ..\..\..\src\jj2level\jj2event sourcepath ..\..\..\src\jj2level\jj2event source jj2event.cpp jj2eventframe.cpp MACRO USE_SDL_NET MACRO USE_MODPLUG MACRO FULLSCREEN_ONLY OPTION GCCE -Wno-multichar -Wno-reorder -Wno-unused -Wno-format -fsigned-char OPTION CW -char signed openjazz-20240919/builds/symbian/uiq3/openjazz.pkg000066400000000000000000000021071467303304200220000ustar00rootroot00000000000000; ; Basic install file for openjazz ; ; Languages ; none - English only by default ; Installation header ; Only one component name as we only support English ; UID is the app's UID - &EN ; List of localised vendor names - one per language. At least one must be provided (English [EN]). ; List must correspond to list of languages specified elsewhere in the .pkg %{"SomeOne"} ; The non-localised, globally unique vendor name (mandatory) :"SomeOne" #{"OpenJazz"},(0xA000A005),0,2,1 ; ProductID for UIQ 3.0 ; Product/platform version UID, Major, Minor, Build, Product ID (0x101F6300), 3, 0, 0, {"UIQ30ProductID"} ; Application file "\epoc32\release\gcce\urel\openjazz.exe"-"!:\sys\bin\openjazz.exe" "\epoc32\data\z\resource\apps\openjazz.rsc"-"!:\resource\apps\openjazz.rsc" "\epoc32\data\z\private\10003a3f\apps\openjazz_reg.rsc"-"!:\private\10003a3f\import\apps\openjazz_reg.rsc" ;"\epoc32\data\z\resource\apps\openjazz.mbm"-"!:\resource\apps\openjazz.mbm" ""-"c:\shared\openjazz\sdl.ini",FN ""-"c:\shared\openjazz\openjazz.cfg",FN ; Required files ; None ; Component .sis files ; None openjazz-20240919/builds/symbian/uiq3/openjazz.rss000066400000000000000000000016231467303304200220300ustar00rootroot00000000000000NAME OJZZ // Include definitions of resource STRUCTS used by this // resource script #include #include #include #include // Include the standard Eikon resource ids #include RESOURCE RSS_SIGNATURE { } RESOURCE TBUF16 { buf=""; } RESOURCE EIK_APP_INFO { } // This file localise the applications icons and caption RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info { caption_and_icon = { CAPTION_AND_ICON_INFO { // The caption text is defined in the rls file caption = "OpenJazz"; // Icons are used to represent applications in the // application launcher and application title bar. // The number_of_icons value identifies how many icons // that exist in the icon_file. number_of_icons = 3; // Using the application icons. icon_file = "\\Resource\\Apps\\OpenJazz.mbm"; } }; } #include openjazz-20240919/builds/symbian/uiq3/openjazz_l.bmp000066400000000000000000000113661467303304200223170ustar00rootroot00000000000000BM6((( @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5.!u   )];@@@@@BslA@@@@@@@@@@@@@@?3= 7@@nh@@@@@@@@@@@@=,I@@@@@@@@@@=am@@@@@@@@= >F@@@@@@@'N8C+ * z@@@@@@?  h(I mA@@e@@@@@@+OD_ E@@@@@@@@@@n3D-@@@@@@@@@@@  l.[@@@@@@@@@@@,9{@@@@@@@@@@@,3{@@@@@@@@@@@j* ^@@@@@@@@@@@Y<11@@@@@@@@@@@,R6lB@@@@@@@@@@@=T _x@@@@@@@@@@@@@*Y"L]@=@@@@@@@@@@@@@=*R@@@@@@@@@@@@@@@8% u?@@@@@@@@@@@@@@@@;)c?@@@@@@@@@@@@@@@@@@@*K 4 @@@@@@@@@@@@@@@@@@@@@@?.D  w6@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@openjazz-20240919/builds/symbian/uiq3/openjazz_lm.bmp000066400000000000000000000113661467303304200224740ustar00rootroot00000000000000BM6((( openjazz-20240919/builds/symbian/uiq3/openjazz_reg.rss000066400000000000000000000014261467303304200226660ustar00rootroot00000000000000// All registration files need to #include appinfo.rh. #include #include // All registration files must define UID2, which is always // KUidAppRegistrationResourceFile, and UID3, which is the application's UID. UID2 KUidAppRegistrationResourceFile UID3 0xA000A005 // application UID // Registration file need to containo an APP_REGISTRATION_INFO resource that // minimally needs to provide the name of the application binary (using the // app_file statement). RESOURCE APP_REGISTRATION_INFO { app_file = "openjazz"; // filename of application binary (minus extension) // Specify the location of the localisable icon/caption definition file localisable_resource_file = "\\Resource\\Apps\\openjazz"; localisable_resource_id = R_LOCALISABLE_APP_INFO; } openjazz-20240919/builds/symbian/uiq3/openjazz_s.bmp000066400000000000000000000020461467303304200223210ustar00rootroot00000000000000BM&6( @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?*V  ,R?CC@@;C>E'Zv*,nPA|xn.H@@)@@@'@@@~wv@@@&d}yx*V@@@? ?@@@@; ;@@@@@@?&d'Z>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@openjazz-20240919/builds/symbian/uiq3/openjazz_sm.bmp000066400000000000000000000020461467303304200224760ustar00rootroot00000000000000BM&6(  openjazz-20240919/builds/symbian/uiq3/openjazz_xl.bmp000066400000000000000000000300661467303304200225050ustar00rootroot00000000000000BM606(@@0 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AB!W'}&| VB@@@@@@@@@@BgdB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@B"X0GA@@@@@BB@@@@@@@@@@@@@@@@@@@@@@@@@@@@0G+ 2@@@]L@@@@@@@@@@@@@@@@@@@@@@@@@?Ct_@@@@@@@@@@@@@@@@@@@@@@@? L@@@@@@@@@@@@@@@@@@@@@?DB@@@@@@@@@@@@@@@@@@@Au wp@@@@@@@@@@@@@@@@@@@"P.B@@@@@@@@@@@@@@@@@DOeVVpO DfB@Ng@@@@@@@@@@@@@@@@@)`Q3GL@@@@@@@@@@@@@@@@@@@@@BdVA @@@@@@N@@@@@@@@@@@@@@@@/>\3g@@@@@@@@@@@@@@@@@@@@@@@%s !O@@@@@@@@@@@@@@@@@@@@@@@@lO'}@@@@@@@@@@@@@@@@@@@@@@@@Yh@@@@@@@@@@@@@@@@@@@@@@@@6I@@@@@@@@@@@@@@@@@@@@@@@@6@@@@@@@@@@@@@@@@@@@@@@@@@Vb@@@@@@@@@@@@@@@@@@@@@@@@ttO @@@@@@@@@@@@@@@@@@@@@@@@&|!W@@@@@@@@@@@@@@@@@@@@@@@@/>fQC@@@@@@@@@@@@@@@@@@@@@@@@CodA@@@@@@@@@@@@@@@@@@@@@@@@@}f\"X@@@@@@@@@@@@@@@@@@@@@@@@@@?lY66YlB@@@@@@@@@@@@@@@@@@@@@@@@@@@$Z0G@@@@@@@@@@@@@@@@@@@@@@@@@@@@B@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@0?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@??@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@0 ?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@B$Z"PA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?})`D@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@C/>%s%s/>B@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@openjazz-20240919/builds/symbian/uiq3/openjazz_xlm.bmp000066400000000000000000000041661467303304200226640ustar00rootroot00000000000000BMvv(@@  wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwpwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwppwwwwwwwwpwwpwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwopenjazz-20240919/doc/000077500000000000000000000000001467303304200143755ustar00rootroot00000000000000openjazz-20240919/doc/Doxyfile000066400000000000000000000264271467303304200161160ustar00rootroot00000000000000# Doxyfile 1.8.14 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = OpenJazz PROJECT_NUMBER = PROJECT_BRIEF = "Open Source Jazz Jackrabbit Engine" PROJECT_LOGO = OpenJazz.png OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 0 AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO GROUP_NESTED_COMPOUNDS = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_AS_ERROR = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = ../src INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.cpp *.h RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 110 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 200 HTML_TIMESTAMP = YES HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = YES HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_STYLESHEET = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = RTF_SOURCE_CODE = NO #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_SUBDIR = MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = svg INTERACTIVE_SVG = YES DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = PLANTUML_JAR_PATH = PLANTUML_CFG_FILE = PLANTUML_INCLUDE_PATH = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES openjazz-20240919/doc/OpenJazz.png000066400000000000000000000023311467303304200166420ustar00rootroot00000000000000PNG  IHDRP7OIDATxxXYm۶m۶m۶m۶mն8s鬗Mv^},‡ѕ䲾%{R%m.u<)Ȏ2P1aM&kb $s7~ ėGeQ[ L&$dQU Ga^trr [&drޓ3U +LdLH ' S#8 !LG$ ,/0Oe&)g|i,Ta0lNۑpiT˩*t2$P8|{% cx s?81a H>5Eocfr+*^ Ghk8{v;yri(pW}n2,ml%ޚp_'\0qx+oH/N)V@EYx<6l0K 05*YB=Gw 7+YBOLf >Zo50p>IxX[IU% |R63N0' ؋‰i7N Rπ &;7.З)+U7&Np2Qw$_`ՙ)%,Nv;K/a|I/~" Mܐ܂ªH2"ɽ>s+rwy{gGMH;>V>!.3!@!΄wuw Î߆ŜyyCXkvUBoSM!K+^M/cfC[)F(n|X \ǣ_A ]l~P@ymKcԟ ^eHhhk#:޵ 4!At=;`=.3`Di.·֑%Qhȓio!p0.,pv@0wF8q^8x IYMƇ eb #x={a_LdXX]k强:ʨ8K\q*AR}e#ryoHI)8aW-s =<Ąox y]2!٥F0WA24ц3„0HzqBFaޗy`N6*DmraOweN66Y.ɗrL ̈́?L4 לR+EVZ]mC%k2 uIENDB`openjazz-20240919/ext/000077500000000000000000000000001467303304200144305ustar00rootroot00000000000000openjazz-20240919/ext/CMakeLists.txt000066400000000000000000000013051467303304200171670ustar00rootroot00000000000000 # argparse add_library(argparse STATIC argparse/argparse.h argparse/argparse.cpp) target_include_directories(argparse PUBLIC argparse) # miniz add_library(miniz STATIC miniz/miniz.h miniz/miniz.cpp) target_compile_definitions(miniz PUBLIC MINIZ_NO_ARCHIVE_APIS) #target_compile_definitions(miniz PRIVATE _LARGEFILE64_SOURCE) target_include_directories(miniz PUBLIC miniz) # psmplug add_library(psmplug STATIC psmplug/fastmix.cpp psmplug/load_psm.cpp psmplug/psmplug.cpp psmplug/psmplug.h psmplug/snd_dsp.cpp psmplug/snd_flt.cpp psmplug/snd_fx.cpp psmplug/sndfile.cpp psmplug/sndfile.h psmplug/sndmix.cpp psmplug/stdafx.h psmplug/tables.h ) target_include_directories(psmplug PUBLIC psmplug) openjazz-20240919/ext/argparse/000077500000000000000000000000001467303304200162345ustar00rootroot00000000000000openjazz-20240919/ext/argparse/LICENSE000066400000000000000000000021241467303304200172400ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2012-2013 Yecheng Fu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. openjazz-20240919/ext/argparse/argparse.cpp000066400000000000000000000260611467303304200205510ustar00rootroot00000000000000/** * Copyright (C) 2012-2015 Yecheng Fu * All rights reserved. * * Use of this source code is governed by a MIT-style license that can be found * in the LICENSE file. */ #include #include #include #include #include #include "argparse.h" #define OPT_UNSET 1 #define OPT_LONG (1 << 1) static const char * prefix_skip(const char *str, const char *prefix) { size_t len = strlen(prefix); return strncmp(str, prefix, len) ? NULL : str + len; } static int prefix_cmp(const char *str, const char *prefix) { for (;; str++, prefix++) if (!*prefix) { return 0; } else if (*str != *prefix) { return (unsigned char)*prefix - (unsigned char)*str; } } static void argparse_error(struct argparse *self, const struct argparse_option *opt, const char *reason, int flags) { (void)self; if (flags & OPT_LONG) { fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); } else { fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); } exit(EXIT_FAILURE); } static int argparse_getvalue(struct argparse *self, const struct argparse_option *opt, int flags) { const char *s = NULL; if (!opt->value) goto skipped; switch (opt->type) { case ARGPARSE_OPT_BOOLEAN: if (flags & OPT_UNSET) { *(int *)opt->value = *(int *)opt->value - 1; } else { *(int *)opt->value = *(int *)opt->value + 1; } if (*(int *)opt->value < 0) { *(int *)opt->value = 0; } break; case ARGPARSE_OPT_BIT: if (flags & OPT_UNSET) { *(int *)opt->value &= ~opt->data; } else { *(int *)opt->value |= opt->data; } break; case ARGPARSE_OPT_STRING: if (self->optvalue) { *(const char **)opt->value = self->optvalue; self->optvalue = NULL; } else if (self->argc > 1) { self->argc--; *(const char **)opt->value = *++self->argv; } else { argparse_error(self, opt, "requires a value", flags); } break; case ARGPARSE_OPT_INTEGER: errno = 0; if (self->optvalue) { *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); self->optvalue = NULL; } else if (self->argc > 1) { self->argc--; *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); } else { argparse_error(self, opt, "requires a value", flags); } if (errno == ERANGE) argparse_error(self, opt, "numerical result out of range", flags); if (s[0] != '\0') // no digits or contains invalid characters argparse_error(self, opt, "expects an integer value", flags); break; #ifdef ARGPARSE_HAVE_FLOAT case ARGPARSE_OPT_FLOAT: errno = 0; if (self->optvalue) { *(float *)opt->value = strtof(self->optvalue, (char **)&s); self->optvalue = NULL; } else if (self->argc > 1) { self->argc--; *(float *)opt->value = strtof(*++self->argv, (char **)&s); } else { argparse_error(self, opt, "requires a value", flags); } if (errno == ERANGE) argparse_error(self, opt, "numerical result out of range", flags); if (s[0] != '\0') // no digits or contains invalid characters argparse_error(self, opt, "expects a numerical value", flags); break; #endif default: assert(0); } skipped: if (opt->callback) { return opt->callback(self, opt); } return 0; } static void argparse_options_check(const struct argparse_option *options) { for (; options->type != ARGPARSE_OPT_END; options++) { switch (options->type) { case ARGPARSE_OPT_END: case ARGPARSE_OPT_BOOLEAN: case ARGPARSE_OPT_BIT: case ARGPARSE_OPT_INTEGER: #ifdef ARGPARSE_HAVE_FLOAT case ARGPARSE_OPT_FLOAT: #endif case ARGPARSE_OPT_STRING: case ARGPARSE_OPT_GROUP: continue; default: fprintf(stderr, "wrong option type: %d", options->type); break; } } } static int argparse_short_opt(struct argparse *self, const struct argparse_option *options) { for (; options->type != ARGPARSE_OPT_END; options++) { if (options->short_name == *self->optvalue) { self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; return argparse_getvalue(self, options, 0); } } return -2; } static int argparse_long_opt(struct argparse *self, const struct argparse_option *options) { for (; options->type != ARGPARSE_OPT_END; options++) { const char *rest; int opt_flags = 0; if (!options->long_name) continue; rest = prefix_skip(self->argv[0] + 2, options->long_name); if (!rest) { // negation disabled? if (options->flags & OPT_NONEG) { continue; } // only OPT_BOOLEAN/OPT_BIT supports negation if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) { continue; } if (prefix_cmp(self->argv[0] + 2, "no-")) { continue; } rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); if (!rest) continue; opt_flags |= OPT_UNSET; } if (*rest) { if (*rest != '=') continue; self->optvalue = rest + 1; } return argparse_getvalue(self, options, opt_flags | OPT_LONG); } return -2; } int argparse_init(struct argparse *self, struct argparse_option *options, const char *const *usages, int flags) { memset(self, 0, sizeof(*self)); self->options = options; self->usages = usages; self->flags = flags; self->description = NULL; self->epilog = NULL; return 0; } void argparse_describe(struct argparse *self, const char *description, const char *epilog) { self->description = description; self->epilog = epilog; } int argparse_parse(struct argparse *self, int argc, char **argv) { self->argc = argc - 1; self->argv = argv + 1; self->out = argv; argparse_options_check(self->options); for (; self->argc; self->argc--, self->argv++) { const char *arg = self->argv[0]; if (arg[0] != '-' || !arg[1]) { if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { goto end; } // if it's not option or is a single char '-', copy verbatim self->out[self->cpidx++] = self->argv[0]; continue; } // short option if (arg[1] != '-') { self->optvalue = arg + 1; switch (argparse_short_opt(self, self->options)) { case -1: break; case -2: goto unknown; } while (self->optvalue) { switch (argparse_short_opt(self, self->options)) { case -1: break; case -2: goto unknown; } } continue; } // if '--' presents if (!arg[2]) { self->argc--; self->argv++; break; } // long option switch (argparse_long_opt(self, self->options)) { case -1: break; case -2: goto unknown; } continue; unknown: fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); argparse_usage(self); if (!(self->flags & ARGPARSE_IGNORE_UNKNOWN_ARGS)) { exit(EXIT_FAILURE); } } end: memmove(self->out + self->cpidx, self->argv, self->argc * sizeof(*self->out)); self->out[self->cpidx + self->argc] = NULL; return self->cpidx + self->argc; } void argparse_usage(struct argparse *self) { if (self->usages) { fprintf(stdout, "Usage: %s\n", *self->usages++); while (*self->usages && **self->usages) fprintf(stdout, " or: %s\n", *self->usages++); } else { fprintf(stdout, "Usage:\n"); } // print description if (self->description) fprintf(stdout, "%s\n", self->description); fputc('\n', stdout); const struct argparse_option *options; // figure out best width size_t usage_opts_width = 0; size_t len; options = self->options; for (; options->type != ARGPARSE_OPT_END; options++) { len = 0; if ((options)->short_name) { len += 2; } if ((options)->short_name && (options)->long_name) { len += 2; // separator ", " } if ((options)->long_name) { len += strlen((options)->long_name) + 2; } if (options->type == ARGPARSE_OPT_INTEGER) { len += strlen("="); #ifdef ARGPARSE_HAVE_FLOAT } else if (options->type == ARGPARSE_OPT_FLOAT) { len += strlen("="); #endif } else if (options->type == ARGPARSE_OPT_STRING) { len += strlen("="); } len = (len + 3) - ((len + 3) & 3); if (usage_opts_width < len) { usage_opts_width = len; } } usage_opts_width += 4; // 4 spaces prefix options = self->options; for (; options->type != ARGPARSE_OPT_END; options++) { size_t pos = 0; size_t pad = 0; if (options->type == ARGPARSE_OPT_GROUP) { fputc('\n', stdout); fprintf(stdout, "%s", options->help); fputc('\n', stdout); continue; } pos = fprintf(stdout, " "); if (options->short_name) { pos += fprintf(stdout, "-%c", options->short_name); } if (options->long_name && options->short_name) { pos += fprintf(stdout, ", "); } if (options->long_name) { pos += fprintf(stdout, "--%s", options->long_name); } if (options->type == ARGPARSE_OPT_INTEGER) { pos += fprintf(stdout, "="); #ifdef ARGPARSE_HAVE_FLOAT } else if (options->type == ARGPARSE_OPT_FLOAT) { pos += fprintf(stdout, "="); #endif } else if (options->type == ARGPARSE_OPT_STRING) { pos += fprintf(stdout, "="); } if (pos <= usage_opts_width) { pad = usage_opts_width - pos; } else { fputc('\n', stdout); pad = usage_opts_width; } fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help); } // print epilog if (self->epilog) fprintf(stdout, "%s\n", self->epilog); } int argparse_help_cb(struct argparse *self, const struct argparse_option *option) { (void)option; argparse_usage(self); exit(EXIT_SUCCESS); } openjazz-20240919/ext/argparse/argparse.h000066400000000000000000000071111467303304200202110ustar00rootroot00000000000000/** * Copyright (C) 2012-2015 Yecheng Fu * All rights reserved. * * Use of this source code is governed by a MIT-style license that can be found * in the LICENSE file. */ #ifndef ARGPARSE_H #define ARGPARSE_H /* For c++ compatibility */ #ifdef __cplusplus extern "C" { #endif #include struct argparse; struct argparse_option; typedef int argparse_callback (struct argparse *self, const struct argparse_option *option); enum argparse_flag { ARGPARSE_STOP_AT_NON_OPTION = 1 << 0, ARGPARSE_IGNORE_UNKNOWN_ARGS = 1 << 1, }; enum argparse_option_type { /* special */ ARGPARSE_OPT_END, ARGPARSE_OPT_GROUP, /* options with no arguments */ ARGPARSE_OPT_BOOLEAN, ARGPARSE_OPT_BIT, /* options with arguments (optional or required) */ ARGPARSE_OPT_INTEGER, #ifdef ARGPARSE_HAVE_FLOAT ARGPARSE_OPT_FLOAT, #endif ARGPARSE_OPT_STRING, }; enum argparse_option_flags { OPT_NONEG = 1, /* disable negation */ }; /** * argparse option * * `type`: * holds the type of the option, you must have an ARGPARSE_OPT_END last in your * array. * * `short_name`: * the character to use as a short option name, '\0' if none. * * `long_name`: * the long option name, without the leading dash, NULL if none. * * `value`: * stores pointer to the value to be filled. * * `help`: * the short help message associated to what the option does. * Must never be NULL (except for ARGPARSE_OPT_END). * * `callback`: * function is called when corresponding argument is parsed. * * `data`: * associated data. Callbacks can use it like they want. * * `flags`: * option flags. */ struct argparse_option { enum argparse_option_type type; const char short_name; const char *long_name; void *value; const char *help; argparse_callback *callback; intptr_t data; int flags; }; /** * argpparse */ struct argparse { // user supplied const struct argparse_option *options; const char *const *usages; int flags; const char *description; // a description after usage const char *epilog; // a description at the end // internal context int argc; char **argv; char **out; int cpidx; const char *optvalue; // current option value }; // built-in callbacks int argparse_help_cb(struct argparse *self, const struct argparse_option *option); // built-in option macros #define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } #ifdef ARGPARSE_HAVE_FLOAT #define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } #endif #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } #define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } #define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ "show this help message and exit", \ argparse_help_cb, 0, OPT_NONEG) int argparse_init(struct argparse *self, struct argparse_option *options, const char *const *usages, int flags); void argparse_describe(struct argparse *self, const char *description, const char *epilog); int argparse_parse(struct argparse *self, int argc, char **argv); void argparse_usage(struct argparse *self); #ifdef __cplusplus } #endif #endif openjazz-20240919/ext/miniz/000077500000000000000000000000001467303304200155565ustar00rootroot00000000000000openjazz-20240919/ext/miniz/LICENSE000066400000000000000000000022121467303304200165600ustar00rootroot00000000000000Copyright 2013-2014 RAD Game Tools and Valve Software Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. openjazz-20240919/ext/miniz/miniz.cpp000066400000000000000000011437701467303304200174250ustar00rootroot00000000000000/************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #include "miniz.h" typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API's */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ #if 0 mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } #else /* Faster, but larger CPU cache footprint. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_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 }; mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; while (buf_len >= 4) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; pByte_buf += 4; buf_len -= 4; } while (buf_len) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++pByte_buf; --buf_len; } return ~crc32; } #endif void mz_free(void *p) { MZ_FREE(p); } void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_ZLIB_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for (;;) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; /* Can't make forward progress without some input. */ } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflate(mz_streamp pStream, int flush) { inflate_state *pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state *)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } /* flush != MZ_FINISH then we must assume there's more input. */ if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for (;;) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ else if (flush == MZ_FINISH) { /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif /*MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression (independent from all decompression API's) */ /* Purposely making these tables static for faster init and thread safety. */ static const mz_uint16 s_tdefl_len_sym[256] = { 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 }; /* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32 *pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq *t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } /* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next = 1; next < n - 1; next++) { if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; while (avbl > 0) { while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2 * used; dpth++; used = 0; } } /* Limits canonical Huffman code table's max code size. */ enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) \ do \ { \ mz_uint bits = b; \ mz_uint len = l; \ MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); \ d->m_bits_in += len; \ while (d->m_bits_in >= 8) \ { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } \ MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() \ { \ if (rle_repeat_count) \ { \ if (rle_repeat_count < 3) \ { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) \ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } \ else \ { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 16; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } \ rle_repeat_count = 0; \ } \ } #define TDEFL_RLE_ZERO_CODE_SIZE() \ { \ if (rle_z_count) \ { \ if (rle_z_count < 3) \ { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ while (rle_z_count--) \ packed_code_sizes[num_packed_code_sizes++] = 0; \ } \ else if (rle_z_count <= 10) \ { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 17; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } \ else \ { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 18; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } \ rle_z_count = 0; \ } \ } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) \ { \ bit_buffer |= (((mz_uint64)(b)) << bits_in); \ bits_in += (l); \ } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64 *)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #ifdef MINIZ_UNALIGNED_USE_MEMCPY static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) #define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16 *)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; p = s; probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; /* Simple lazy/greedy parsing state machine. */ len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } /* Move the lookahead forward by len_to_move bytes. */ d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); /* Check if it's time to flush the current LZ codes to the internal output buffer. */ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8 *)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; /* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ #endif /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } /* write dummy header */ for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); /* compress image data */ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } /* write real header */ *pLen_out = out_buf.m_size - 41; { static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54 }; pnghdr[18] = (mz_uint8)(w >> 8); pnghdr[19] = (mz_uint8)w; pnghdr[22] = (mz_uint8)(h >> 8); pnghdr[23] = (mz_uint8)h; pnghdr[25] = chans[num_chans]; pnghdr[33] = (mz_uint8)(*pLen_out >> 24); pnghdr[34] = (mz_uint8)(*pLen_out >> 16); pnghdr[35] = (mz_uint8)(*pLen_out >> 8); pnghdr[36] = (mz_uint8)*pLen_out; c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); memcpy(out_buf.m_pBuf, pnghdr, 41); } /* write footer (IDAT CRC-32, followed by IEND chunk) */ if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); /* compute final size of file, grab compressed data buffer and return */ *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc() { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } void tdefl_compressor_free(tdefl_compressor *pComp) { MZ_FREE(pComp); } #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } #endif /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Decompression (completely independent from all compression API's) */ #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN \ switch (r->m_state) \ { \ case 0: #define TINFL_CR_RETURN(state_index, result) \ do \ { \ status = result; \ r->m_state = state_index; \ goto common_exit; \ case state_index:; \ } \ MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) \ do \ { \ for (;;) \ { \ TINFL_CR_RETURN(state_index, result); \ } \ } \ MZ_MACRO_END #define TINFL_CR_FINISH } #define TINFL_GET_BYTE(state_index, c) \ do \ { \ while (pIn_buf_cur >= pIn_buf_end) \ { \ TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ } \ c = *pIn_buf_cur++; \ } \ MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) \ do \ { \ mz_uint c; \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ b = bit_buf & ((1 << (n)) - 1); \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END /* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do \ { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } \ else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ /* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ /* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ #define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ do \ { \ int temp; \ mz_uint code_len, c; \ if (num_bits < 15) \ { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } \ else \ { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ pIn_buf_cur += 2; \ num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ bit_buf >>= code_len; \ num_bits -= code_len; \ } \ MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) { mz_uint8 *pSrc; for (;;) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif while(counter>2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; counter -= 3; } if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ TINFL_SKIP_BITS(32, num_bits & 7); while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } } r->m_num_bits = num_bits; r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } /* Higher level helper functions. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for (;;) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for (;;) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } tinfl_decompressor *tinfl_decompressor_alloc() { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) tinfl_init(pDecomp); return pDecomp; } void tinfl_decompressor_free(tinfl_decompressor *pDecomp) { MZ_FREE(pDecomp); } #ifdef __cplusplus } #endif /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * Copyright 2016 Martin Raiber * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- .ZIP archive reading */ #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #if defined(_MSC_VER) || defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE *pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE *pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__GNUC__) && _LARGEFILE64_SOURCE #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #elif defined(__APPLE__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen(p, m, s) #define MZ_DELETE_FILE remove #else #pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #ifdef __STRICT_ANSI__ #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #else #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #endif #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif /* #ifdef _MSC_VER */ #endif /* #ifdef MINIZ_NO_STDIO */ #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) /* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ enum { /* ZIP archive identifiers and record sizes */ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, /* ZIP64 archive identifier and record sizes */ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, /* Central directory header record offsets */ MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, /* Local directory header offsets */ MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, /* End of central directory offsets */ MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, /* ZIP64 End of central directory locator offsets */ MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ /* ZIP64 End of central directory header offsets */ MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ uint32_t m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ mz_bool m_zip64_has_extended_info_fields; /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ MZ_FILE *m_pFile; mz_uint64 m_file_archive_start_ofs; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) { MZ_ASSERT(index < pArray->m_size); return index; } #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] #else #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] #endif static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) { memset(pArray, 0, sizeof(mz_zip_array)); pArray->m_element_size = element_size; } static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif /* #ifdef _MSC_VER */ *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifndef MINIZ_NO_STDIO #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) { struct MZ_FILE_STAT_STRUCT file_stat; /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; *pTime = file_stat.st_mtime; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) { struct utimbuf t; memset(&t, 0, sizeof(t)); t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_TIME */ static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) { if (pZip) pZip->m_last_error = err_num; return MZ_FALSE; } static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; pZip->m_last_error = MZ_ZIP_NO_ERROR; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_init_flags = flags; pZip->m_pState->m_zip64 = MZ_FALSE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; pZip->m_zip_mode = MZ_ZIP_MODE_READING; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) \ do \ { \ mz_uint32 t = a; \ a = b; \ b = t; \ } \ MZ_MACRO_END /* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices; mz_uint32 start, end; const mz_uint32 size = pZip->m_total_files; if (size <= 1U) return; pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); start = (size - 2U) >> 1U; for (;;) { mz_uint64 child, root = start; for (;;) { if ((child = (root << 1U) + 1U) >= size) break; child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } if (!start) break; start--; } end = size - 1; while (end > 0) { mz_uint64 child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for (;;) { if ((child = (root << 1U) + 1U) >= end) break; child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) { mz_int64 cur_file_ofs; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; /* Basic sanity checks - reject files which are too small */ if (pZip->m_archive_size < record_size) return MZ_FALSE; /* Find the record by scanning the file from the end towards the beginning. */ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for (;;) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) { mz_uint s = MZ_READ_LE32(pBuf + i); if (s == record_sig) { if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) break; } } if (i >= 0) { cur_file_ofs += i; break; } /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } *pOfs = cur_file_ofs; return MZ_TRUE; } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) { mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; mz_uint64 cdir_ofs = 0; mz_int64 cur_file_ofs = 0; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; mz_uint64 zip64_end_of_central_dir_ofs = 0; /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); /* Read and verify the end of central directory record. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { pZip->m_pState->m_zip64 = MZ_TRUE; } } } } } pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if (pZip->m_pState->m_zip64) { mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (zip64_total_num_of_disks != 1U) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); /* Check for miniz's practical limits */ if (zip64_cdir_total_entries > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ if (zip64_size_of_central_directory > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); cdir_size = (mz_uint32)zip64_size_of_central_directory; num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); } if (pZip->m_total_files != cdir_entries_on_this_disk) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); /* Now create an index into the central directory file records, do some basic sanity checking on each record */ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; mz_uint64 comp_size, decomp_size, local_header_ofs; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && (ext_data_size) && (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { /* Attempt to find zip64 extended information field in the entry's extra data */ mz_uint32 extra_size_remaining = ext_data_size; if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ pZip->m_pState->m_zip64 = MZ_TRUE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (comp_size != MZ_UINT32_MAX) { if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) MZ_CLEAR_OBJ(*pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_bool status = MZ_TRUE; if (!pZip) return MZ_FALSE; if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) { if (set_last_error) pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { return mz_zip_reader_end_internal(pZip, MZ_TRUE); } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) { if ((!pZip) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) { if (!pMem) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pNeeds_keepalive = NULL; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); } mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { mz_uint64 cur_file_ofs; if ((!pZip) || (!pFile)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); cur_file_ofs = MZ_FTELL64(pFile); if (!archive_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); archive_size = MZ_FTELL64(pFile) - cur_file_ofs; if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = archive_size; pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; } mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) { mz_uint bit_flag; mz_uint method; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if ((method != 0) && (method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); return MZ_FALSE; } if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); return MZ_FALSE; } if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, attribute_mapping_id, external_attr; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ /* FIXME: Remove this check? Is it necessary - we already check the filename. */ attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; (void)attribute_mapping_id; external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) { return MZ_TRUE; } return MZ_FALSE; } static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) { mz_uint n; const mz_uint8 *p = pCentral_dir_header; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_FALSE; if ((!p) || (!pStat)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Extract fields from the central directory record. */ pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); /* Copy as much of the filename and comment as possible. */ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; /* Set some flags for convienance */ pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); /* See if we need to read any zip64 extended information fields. */ /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) { /* Attempt to find zip64 extended information field in the entry's extra data */ mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; mz_uint32 field_data_remaining = field_data_size; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_TRUE; if (pStat->m_uncomp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_uncomp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_comp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_comp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_local_header_ofs == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const uint32_t size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) *pIndex = 0; if (size) { /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ mz_int64 l = 0, h = (mz_int64)size - 1; while (l <= h) { mz_int64 m = l + ((h - l) >> 1); uint32_t file_index = pIndices[(uint32_t)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } else if (comp < 0) l = m + 1; else h = m - 1; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint32 index; if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) return -1; else return (int)index; } mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) { mz_uint file_index; size_t name_len, comment_len; if (pIndex) *pIndex = 0; if ((!pZip) || (!pZip->m_pState) || (!pName)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* See if we can use a binary search */ if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { return mz_zip_locate_file_binary_search(pZip, pName, pIndex); } /* Locate the entry by scanning the entire central directory */ name_len = strlen(pName); if (name_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); comment_len = pComment ? strlen(pComment) : 0; if (comment_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Ensure supplied output buffer is large enough. */ needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); /* Read and parse the local directory entry. */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) { if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); } #endif return MZ_TRUE; } /* Decompress the file either directly from memory or from a file input buffer. */ tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { /* Read directly from the archive in memory. */ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { /* Use a user provided read buffer. */ if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { /* Temporarily allocate a read buffer. */ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); status = TINFL_STATUS_FAILED; } #endif } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return NULL; } comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); return NULL; } if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); /* Decompress the file either directly from memory or from a file input buffer. */ if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pState->m_pMem) { if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; } else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); #endif } cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); } #endif if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); status = TINFL_STATUS_FAILED; } else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); #endif if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (file_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; } #endif } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_reader_extract_iter_state *pState; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; /* Argument sanity check */ if ((!pZip) || (!pZip->m_pState)) return NULL; /* Allocate an iterator status structure */ pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); if (!pState) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } /* Fetch file details */ if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Encryption and patch files are not supported. */ if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Init state - save args */ pState->pZip = pZip; pState->flags = flags; /* Init state - reset variables to defaults */ pState->status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS pState->file_crc32 = MZ_CRC32_INIT; #endif pState->read_buf_ofs = 0; pState->out_buf_ofs = 0; pState->pRead_buf = NULL; pState->pWrite_buf = NULL; pState->out_blk_remain = 0; /* Read and parse the local directory entry. */ pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Decompress the file either directly from memory or from a file input buffer. */ if (pZip->m_pState->m_pMem) { pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; pState->comp_remaining = pState->file_stat.m_comp_size; } else { if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, therefore intermediate read buffer required */ pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } else { /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ pState->read_buf_size = 0; } pState->read_buf_avail = 0; pState->comp_remaining = pState->file_stat.m_comp_size; } if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, init decompressor */ tinfl_init( &pState->inflator ); /* Allocate write buffer */ if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pState->pRead_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } return pState; } mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_uint32 file_index; /* Locate file index by name */ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return NULL; /* Construct iterator */ return mz_zip_reader_extract_iter_new(pZip, file_index, flags); } size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) { size_t copied_to_caller = 0; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) return 0; if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data, calc amount to return. */ copied_to_caller = MZ_MIN( buf_size, pState->comp_remaining ); /* Zip is in memory....or requires reading from a file? */ if (pState->pZip->m_pState->m_pMem) { /* Copy data to caller's buffer */ memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; } else { /* Read directly into caller's buffer */ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) { /* Failed to read all that was asked for, flag failure and alert user */ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; copied_to_caller = 0; } } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Compute CRC if not returning compressed data only */ if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); #endif /* Advance offsets, dec counters */ pState->cur_file_ofs += copied_to_caller; pState->out_buf_ofs += copied_to_caller; pState->comp_remaining -= copied_to_caller; } else { do { /* Calc ptr to write buffer - given current output pos and block size */ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); /* Calc max output size - given current output pos and block size */ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if (!pState->out_blk_remain) { /* Read more data from file if none available (and reading from file) */ if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { /* Calc read size */ pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) { mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Advance offsets, dec counters */ pState->cur_file_ofs += pState->read_buf_avail; pState->comp_remaining -= pState->read_buf_avail; pState->read_buf_ofs = 0; } /* Perform decompression */ in_buf_size = (size_t)pState->read_buf_avail; pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); pState->read_buf_avail -= in_buf_size; pState->read_buf_ofs += in_buf_size; /* Update current output block size remaining */ pState->out_blk_remain = out_buf_size; } if (pState->out_blk_remain) { /* Calc amount to return. */ size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); /* Copy data to caller's buffer */ memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); #endif /* Decrement data consumed from block */ pState->out_blk_remain -= to_copy; /* Inc output offset, while performing sanity check */ if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Increment counter of data copied to caller */ copied_to_caller += to_copy; } } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); } /* Return how many bytes were copied into user buffer */ return copied_to_caller; } mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) { int status; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) return MZ_FALSE; /* Was decompression completed and requested? */ if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); pState->status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (pState->file_crc32 != pState->file_stat.m_crc32) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; } #endif } /* Free buffers */ if (!pState->pZip->m_pState->m_pMem) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); if (pState->pWrite_buf) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); /* Save status */ status = pState->status; /* Free context */ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); return status == TINFL_STATUS_DONE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) { if (status) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) { mz_zip_archive_file_stat file_stat; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); } mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); } #endif /* #ifndef MINIZ_NO_STDIO */ static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_uint32 *p = (mz_uint32 *)pOpaque; (void)file_ofs; *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); return n; } mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_zip_internal_state *pState; const mz_uint8 *pCentral_dir_header; mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint64 local_header_ofs = 0; mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; mz_bool has_data_descriptor; mz_uint32 local_header_bit_flags; mz_zip_array file_data_array; mz_zip_array_init(&file_data_array, 1); if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (file_index > pZip->m_total_files) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_is_encrypted) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports stored and deflate. */ if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); if (!file_stat.m_is_supported) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); /* Read and parse the local directory entry. */ local_header_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); has_data_descriptor = (local_header_bit_flags & 8) != 0; if (local_header_filename_len != strlen(file_stat.m_filename)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (local_header_filename_len) { if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_uint32 extra_size_remaining = local_header_extra_len; const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) { mz_uint8 descriptor_buf[32]; mz_bool has_id; const mz_uint8 *pSrc; mz_uint32 file_crc32; mz_uint64 comp_size = 0, uncomp_size = 0; mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; file_crc32 = MZ_READ_LE32(pSrc); if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); } else { comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); } if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } else { if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } mz_zip_array_clear(pZip, &file_data_array); if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) return MZ_FALSE; /* 1 more check to be sure, although the extract checks too. */ if (uncomp_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); return MZ_FALSE; } } return MZ_TRUE; handle_failure: mz_zip_array_clear(pZip, &file_data_array); return MZ_FALSE; } mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; uint32_t i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Basic sanity checks */ if (!pState->m_zip64) { if (pZip->m_total_files > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pZip->m_archive_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } else { if (pZip->m_total_files >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } for (i = 0; i < pZip->m_total_files; i++) { if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { mz_uint32 found_index; mz_zip_archive_file_stat stat; if (!mz_zip_reader_file_stat(pZip, i, &stat)) return MZ_FALSE; if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) return MZ_FALSE; /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ if (found_index != i) return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); } if (!mz_zip_validate_file(pZip, i, flags)) return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if ((!pMem) || (!size)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if (!pFilename) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #endif /* #ifndef MINIZ_NO_STDIO */ /* ------------------- .ZIP archive writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) { mz_write_le32(p, (mz_uint32)v); mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); if (!n) return 0; /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); return 0; } if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return 0; } pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) { mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) { if (!pZip->m_pRead) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (pZip->m_file_offset_alignment) { /* Ensure user specified file offset alignment is a power of 2. */ if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_zip64 = zip64; pZip->m_pState->m_zip64_has_extended_info_fields = zip64; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { return mz_zip_writer_init_v2(pZip, existing_size, 0); } mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) { mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); return 0; } return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); } mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } pZip->m_pState->m_pFile = pFile; pZip->m_zip_type = MZ_ZIP_TYPE_FILE; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) { pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, 0, flags)) return MZ_FALSE; pZip->m_pState->m_pFile = pFile; pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ mz_zip_reader_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory location. */ /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); } /* TODO: pArchive_name is a terrible name here! */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) { mz_uint8 *pDst = pBuf; mz_uint32 field_size = 0; MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); MZ_WRITE_LE16(pDst + 2, 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { MZ_WRITE_LE64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pComp_size) { MZ_WRITE_LE64(pDst, *pComp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pLocal_header_ofs) { MZ_WRITE_LE64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } MZ_WRITE_LE16(pBuf + 2, field_size); return (mz_uint32)(pDst - pBuf); } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; if (!pZip->m_pState->m_zip64) { if (local_header_ofs > 0xFFFFFFFF) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { /* Try to resize the central directory array back into its original state. */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ if (*pArchive_name == '/') return MZ_FALSE; while (*pArchive_name) { if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; pArchive_name++; } return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); } mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint16 bit_flags = 0; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); #ifndef MINIZ_NO_TIME if (last_modified != NULL) { mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); } else { MZ_TIME_T cur_time; time(&cur_time); mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } #endif /* #ifndef MINIZ_NO_TIME */ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { /* Set DOS Subdirectory attribute bit. */ ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; /* Subdirectories cannot contain data. */ if ((buf_size) || (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes; MZ_CLEAR_OBJ(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { method = MZ_DEFLATED; } if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pExtra_data != NULL) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += buf_size; comp_size = buf_size; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; if (uncomp_size) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_zip_internal_state *pState; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) { /* Source file is too large for non-zip64 */ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ pState->m_zip64 = MZ_TRUE; } /* We could support this, but why? */ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } #ifndef MINIZ_NO_TIME if (pFile_time) { mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); } #endif if (uncomp_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_archive_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (uncomp_size && level) { method = MZ_DEFLATED; } MZ_CLEAR_OBJ(local_dir_header); if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, extra_size + user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (uncomp_size) { mz_uint64 uncomp_remaining = uncomp_size; void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!level) { while (uncomp_remaining) { mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); uncomp_remaining -= n; cur_archive_file_ofs += n; } comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); } for (;;) { size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); tdefl_status status; tdefl_flush flush = TDEFL_NO_FLUSH; if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); break; } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); uncomp_remaining -= in_buf_size; if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) flush = TDEFL_FULL_FLUSH; status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) { mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); break; } } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { MZ_FILE *pSrc_file = NULL; mz_uint64 uncomp_size = 0; MZ_TIME_T file_modified_time; MZ_TIME_T *pFile_time = NULL; mz_bool status; memset(&file_modified_time, 0, sizeof(file_modified_time)); #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) pFile_time = &file_modified_time; if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); #endif pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); MZ_FCLOSE(pSrc_file); return status; } #endif /* #ifndef MINIZ_NO_STDIO */ static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { mz_uint8 new_ext_block[64]; mz_uint8 *pDst = new_ext_block; mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); mz_write_le16(pDst + sizeof(mz_uint16), 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { mz_write_le64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); } if (pComp_size) { mz_write_le64(pDst, *pComp_size); pDst += sizeof(mz_uint64); } if (pLocal_header_ofs) { mz_write_le64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); } if (pDisk_start) { mz_write_le32(pDst, *pDisk_start); pDst += sizeof(mz_uint32); } mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if ((pExt) && (ext_len)) { mz_uint32 extra_size_remaining = ext_len; const mz_uint8 *pExtra_data = pExt; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } return MZ_TRUE; } /* TODO: This func is now pretty freakin complex due to zip64, split it up? */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; mz_zip_archive_file_stat src_file_stat; mz_uint32 src_filename_len, src_comment_len, src_ext_len; mz_uint32 local_header_filename_size, local_header_extra_len; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Get pointer to the source central dir header and crack it */ if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); if (!pState->m_zip64) { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) return MZ_FALSE; cur_src_file_ofs = src_file_stat.m_local_header_ofs; cur_dst_file_ofs = pZip->m_archive_size; /* Read the source archive's local dir header */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Compute the total size we need to copy (filename+extra data+compressed data) */ local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; /* Try to find a zip64 extended information field */ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_zip_array file_data_array; const mz_uint8 *pExtra_data; mz_uint32 extra_size_remaining = local_header_extra_len; mz_zip_array_init(&file_data_array, 1); if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (const mz_uint8 *)file_data_array.m_p; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); mz_zip_array_clear(pZip, &file_data_array); } if (!pState->m_zip64) { /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ /* We also check when the archive is finalized so this doesn't need to be perfect. */ mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; if (approx_new_archive_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } /* Write dest archive padding */ if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); while (src_archive_bytes_remaining) { n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_dst_file_ofs += n; src_archive_bytes_remaining -= n; } /* Now deal with the optional data descriptor */ bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { /* Copy data descriptor */ if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { /* src is zip64, dest must be zip64 */ /* name uint32_t's */ /* id 1 (optional in zip64?) */ /* crc 1 */ /* comp_size 2 */ /* uncomp_size 2 */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); } else { /* src is NOT zip64 */ mz_bool has_id; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); const mz_uint32 src_crc32 = pSrc_descriptor[0]; const mz_uint64 src_comp_size = pSrc_descriptor[1]; const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); n = sizeof(mz_uint32) * 6; } else { /* dest is NOT zip64, just copy it as-is */ n = sizeof(mz_uint32) * (has_id ? 4 : 3); } } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); /* Finally, add the new central dir header */ orig_central_dir_size = pState->m_central_dir.m_size; memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); if (pState->m_zip64) { /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; mz_zip_array new_ext_block; mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { mz_zip_array_clear(pZip, &new_ext_block); return MZ_FALSE; } MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) { mz_zip_array_clear(pZip, &new_ext_block); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } mz_zip_array_clear(pZip, &new_ext_block); } else { /* sanity checks */ if (cur_dst_file_ofs > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (local_dir_header_ofs >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } } /* This shouldn't trigger unless we screwed up during the initial sanity checks */ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { /* TODO: Support central dirs >= 32-bits in size */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[256]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { /* Write central directory */ central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += central_dir_size; } if (pState->m_zip64) { /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } /* Write end of central directory record */ MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { if ((!ppBuf) || (!pSize)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); *ppBuf = NULL; *pSize = 0; if ((!pZip) || (!pZip->m_pState)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_pWrite != mz_zip_heap_write_func) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *ppBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { return mz_zip_writer_end_internal(pZip, MZ_TRUE); } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); } mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; mz_zip_zero_struct(&zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (!mz_zip_writer_validate_archive_name(pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_FILENAME; return MZ_FALSE; } /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { /* Create a new archive. */ if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } created_new_archive = MZ_TRUE; } else { /* Append to an existing archive. */ if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); actual_err = zip_archive.m_last_error; /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ if (!mz_zip_writer_finalize_archive(&zip_archive)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if (!mz_zip_writer_end_internal(&zip_archive, status)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if ((!status) && (created_new_archive)) { /* It's a new archive and something went wrong, so just delete it. */ int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } if (pErr) *pErr = actual_err; return status; } void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) { mz_uint32 file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return NULL; } mz_zip_zero_struct(&zip_archive); if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return NULL; } if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); } mz_zip_reader_end_internal(&zip_archive, p != NULL); if (pErr) *pErr = zip_archive.m_last_error; return p; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ /* ------------------- Misc utils */ mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; } mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; } mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = err_num; return prev_err; } mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { if (!pZip) return MZ_ZIP_INVALID_PARAMETER; return pZip->m_last_error; } mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); } mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = MZ_ZIP_NO_ERROR; return prev_err; } const char *mz_zip_get_error_string(mz_zip_error mz_err) { switch (mz_err) { case MZ_ZIP_NO_ERROR: return "no error"; case MZ_ZIP_UNDEFINED_ERROR: return "undefined error"; case MZ_ZIP_TOO_MANY_FILES: return "too many files"; case MZ_ZIP_FILE_TOO_LARGE: return "file too large"; case MZ_ZIP_UNSUPPORTED_METHOD: return "unsupported method"; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: return "unsupported encryption"; case MZ_ZIP_UNSUPPORTED_FEATURE: return "unsupported feature"; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: return "failed finding central directory"; case MZ_ZIP_NOT_AN_ARCHIVE: return "not a ZIP archive"; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: return "invalid header or archive is corrupted"; case MZ_ZIP_UNSUPPORTED_MULTIDISK: return "unsupported multidisk archive"; case MZ_ZIP_DECOMPRESSION_FAILED: return "decompression failed or archive is corrupted"; case MZ_ZIP_COMPRESSION_FAILED: return "compression failed"; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: return "unexpected decompressed size"; case MZ_ZIP_CRC_CHECK_FAILED: return "CRC-32 check failed"; case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: return "unsupported central directory size"; case MZ_ZIP_ALLOC_FAILED: return "allocation failed"; case MZ_ZIP_FILE_OPEN_FAILED: return "file open failed"; case MZ_ZIP_FILE_CREATE_FAILED: return "file create failed"; case MZ_ZIP_FILE_WRITE_FAILED: return "file write failed"; case MZ_ZIP_FILE_READ_FAILED: return "file read failed"; case MZ_ZIP_FILE_CLOSE_FAILED: return "file close failed"; case MZ_ZIP_FILE_SEEK_FAILED: return "file seek failed"; case MZ_ZIP_FILE_STAT_FAILED: return "file stat failed"; case MZ_ZIP_INVALID_PARAMETER: return "invalid parameter"; case MZ_ZIP_INVALID_FILENAME: return "invalid filename"; case MZ_ZIP_BUF_TOO_SMALL: return "buffer too small"; case MZ_ZIP_INTERNAL_ERROR: return "internal error"; case MZ_ZIP_FILE_NOT_FOUND: return "file not found"; case MZ_ZIP_ARCHIVE_TOO_LARGE: return "archive is too large"; case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: return "write calledback failed"; default: break; } return "unknown error"; } /* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return MZ_FALSE; return pZip->m_pState->m_zip64; } size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_central_dir.m_size; } mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) { if (!pZip) return 0; return pZip->m_archive_size; } mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_file_archive_start_ofs; } MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_pFile; } size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); } mz_bool mz_zip_end(mz_zip_archive *pZip) { if (!pZip) return MZ_FALSE; if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) return mz_zip_reader_end(pZip); #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) return mz_zip_writer_end(pZip); #endif return MZ_FALSE; } #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ openjazz-20240919/ext/miniz/miniz.h000066400000000000000000002017721467303304200170660ustar00rootroot00000000000000/* miniz.c 2.0.8 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #pragma once // OpenJazz modification #ifndef MINIZ_NO_ARCHIVE_APIS #define MINIZ_NO_ARCHIVE_APIS #endif /* Defines to completely disable specific portions of miniz.c: If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ /* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ /* get/set file times, and the C run-time funcs that get/set times won't be called. */ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ /* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ /* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ /*#define MINIZ_NO_ZLIB_APIS */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ /* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME #endif #include #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) /* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 #else #define MINIZ_X86_OR_X64_CPU 0 #endif #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API Definitions. */ /* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; /* Method */ #define MZ_DEFLATED 8 /* Heap allocation callbacks. Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); /* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; #define MZ_VERSION "10.0.3" #define MZ_VERNUM 0xA030 #define MZ_VER_MAJOR 10 #define MZ_VER_MINOR 0 #define MZ_VER_REVISION 3 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS /* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; /* Return status codes. MZ_PARAM_ERROR is non-standard. */ enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; /* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; /* Compression/decompression stream struct. */ typedef struct mz_stream_s { const unsigned char *next_in; /* pointer to next byte to read */ unsigned int avail_in; /* number of bytes available at next_in */ mz_ulong total_in; /* total number of bytes consumed so far */ unsigned char *next_out; /* pointer to next byte to write */ unsigned int avail_out; /* number of bytes that can be written to next_out */ mz_ulong total_out; /* total number of bytes produced so far */ char *msg; /* error msg (unused) */ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ mz_free_func zfree; /* optional heap free function (defaults to free) */ void *opaque; /* heap alloc function user pointer */ int data_type; /* data_type (unused) */ mz_ulong adler; /* adler32 of the source or uncompressed data */ mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ const char *mz_version(void); /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ /* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ /* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ /* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ /* Return values: */ /* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ /* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ mz_ulong mz_compressBound(mz_ulong source_len); /* Initializes a decompressor. */ int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ /* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ /* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ /* Return values: */ /* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ /* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_DATA_ERROR if the deflate stream is invalid. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ #endif /* MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif #pragma once #include #include #include #include /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef int64_t mz_int64; typedef uint64_t mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) /* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #define MZ_FILE FILE #endif /* #ifdef MINIZ_NO_STDIO */ #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { int m_dummy; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else #define MZ_TIME_T time_t #endif #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); extern void miniz_def_free_func(void *opaque, void *address); extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) #ifdef __cplusplus } #endif #pragma once #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression API Definitions */ /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ #define TDEFL_LESS_MEMORY 0 /* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ /* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; /* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ /* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ /* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ /* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ /* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ /* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ /* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ /* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ /* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; /* High level compression functions: */ /* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ /* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ /* Returns 0 on failure. */ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ /* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ /* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ /* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ /* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; /* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif /* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1 } tdefl_status; /* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; /* tdefl's compression state structure. */ typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; /* Initializes the compressor. */ /* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ /* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc(); void tdefl_compressor_free(tdefl_compressor *pComp); #ifdef __cplusplus } #endif #pragma once /* ------------------- Low-level Decompression API Definitions */ #ifdef __cplusplus extern "C" { #endif /* Decompression flags used by tinfl_decompress(). */ /* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ /* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ /* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ /* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; /* High level decompression functions: */ /* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ /* On return: */ /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer needed. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tinfl_decompressor *tinfl_decompressor_alloc(); void tinfl_decompressor_free(tinfl_decompressor *pDecomp); /* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 /* Return status. */ typedef enum { /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ TINFL_STATUS_BAD_PARAM = -3, /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ TINFL_STATUS_ADLER32_MISMATCH = -2, /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ TINFL_STATUS_FAILED = -1, /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ TINFL_STATUS_DONE = 0, /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ TINFL_STATUS_NEEDS_MORE_INPUT = 1, /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ /* so I may need to add some code to address this. */ TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; /* Initializes the decompressor to its initial state. */ #define tinfl_init(r) \ do \ { \ (r)->m_state = 0; \ } \ MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 /* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else #define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif #pragma once /* ------------------- ZIP archive reading/writing */ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif enum { /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; typedef struct { /* Central directory file index. */ mz_uint32 m_file_index; /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ mz_uint64 m_central_dir_ofs; /* These fields are copied directly from the zip's central dir. */ mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME MZ_TIME_T m_time; #endif /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; /* File's compressed size. */ mz_uint64 m_comp_size; /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ mz_uint64 m_uncomp_size; /* Zip internal and external file attributes. */ mz_uint16 m_internal_attr; mz_uint32 m_external_attr; /* Entry's local header file offset in bytes. */ mz_uint64 m_local_header_ofs; /* Size of comment in bytes. */ mz_uint32 m_comment_size; /* MZ_TRUE if the entry appears to be a directory. */ mz_bool m_is_directory; /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ mz_bool m_is_encrypted; /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ mz_bool m_is_supported; /* Filename. If string ends in '/' it's a subdirectory entry. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; /* Comment field. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 } mz_zip_flags; typedef enum { MZ_ZIP_TYPE_INVALID = 0, MZ_ZIP_TYPE_USER, MZ_ZIP_TYPE_MEMORY, MZ_ZIP_TYPE_HEAP, MZ_ZIP_TYPE_FILE, MZ_ZIP_TYPE_CFILE, MZ_ZIP_TOTAL_TYPES } mz_zip_type; /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ typedef enum { MZ_ZIP_NO_ERROR = 0, MZ_ZIP_UNDEFINED_ERROR, MZ_ZIP_TOO_MANY_FILES, MZ_ZIP_FILE_TOO_LARGE, MZ_ZIP_UNSUPPORTED_METHOD, MZ_ZIP_UNSUPPORTED_ENCRYPTION, MZ_ZIP_UNSUPPORTED_FEATURE, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, MZ_ZIP_NOT_AN_ARCHIVE, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, MZ_ZIP_UNSUPPORTED_MULTIDISK, MZ_ZIP_DECOMPRESSION_FAILED, MZ_ZIP_COMPRESSION_FAILED, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, MZ_ZIP_CRC_CHECK_FAILED, MZ_ZIP_UNSUPPORTED_CDIR_SIZE, MZ_ZIP_ALLOC_FAILED, MZ_ZIP_FILE_OPEN_FAILED, MZ_ZIP_FILE_CREATE_FAILED, MZ_ZIP_FILE_WRITE_FAILED, MZ_ZIP_FILE_READ_FAILED, MZ_ZIP_FILE_CLOSE_FAILED, MZ_ZIP_FILE_SEEK_FAILED, MZ_ZIP_FILE_STAT_FAILED, MZ_ZIP_INVALID_PARAMETER, MZ_ZIP_INVALID_FILENAME, MZ_ZIP_BUF_TOO_SMALL, MZ_ZIP_INTERNAL_ERROR, MZ_ZIP_FILE_NOT_FOUND, MZ_ZIP_ARCHIVE_TOO_LARGE, MZ_ZIP_VALIDATION_FAILED, MZ_ZIP_WRITE_CALLBACK_FAILED, MZ_ZIP_TOTAL_ERRORS } mz_zip_error; typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; mz_zip_mode m_zip_mode; mz_zip_type m_zip_type; mz_zip_error m_last_error; mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef struct { mz_zip_archive *pZip; mz_uint flags; int status; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint file_crc32; #endif mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; void *pWrite_buf; size_t out_blk_remain; tinfl_decompressor inflator; } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ /* Inits a ZIP archive reader. */ /* These functions read and validate the archive's central directory. */ mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); #ifndef MINIZ_NO_STDIO /* Read a archive from a disk file. */ /* file_start_ofs is the file offset where the archive actually begins, or 0. */ /* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file position. */ /* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif /* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ mz_bool mz_zip_reader_end(mz_zip_archive *pZip); /* -------- ZIP reading or writing */ /* Clears a mz_zip_archive struct to all zeros. */ /* Important: This must be done before passing the struct to any mz_zip functions. */ void mz_zip_zero_struct(mz_zip_archive *pZip); mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); /* Returns the total number of files in the archive. */ mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); const char *mz_zip_get_error_string(mz_zip_error mz_err); /* MZ_TRUE if the archive file entry is a directory entry. */ mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the file is encrypted/strong encrypted. */ mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); /* Retrieves the filename of an archive file entry. */ /* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); /* Attempts to locates a file in the archive's central directory. */ /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ /* Returns -1 if the file cannot be found. */ int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); /* Returns detailed information about an archive file entry. */ mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); /* MZ_TRUE if the file is in zip64 format. */ /* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); /* Returns the total central directory size in bytes. */ /* The current max supported size is <= MZ_UINT32_MAX. */ size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); /* Extracts a archive file to a memory buffer using no memory allocation. */ /* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); /* Extracts a archive file to a memory buffer. */ mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); /* Extracts a archive file to a dynamically allocated heap buffer. */ /* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ /* Returns NULL and sets the last error on failure. */ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); /* Extracts a archive file using a callback function to output the file's data. */ mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); /* Extract a file iteratively */ mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); #ifndef MINIZ_NO_STDIO /* Extracts a archive file to a disk file and sets its last accessed and modified times. */ /* This function only extracts files, not archive directory records. */ mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); /* Extracts a archive file starting at the current position in the destination FILE stream. */ mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif #if 0 /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif /* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ /* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); /* Validates an entire archive by calling mz_zip_validate_file() on each file. */ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ mz_bool mz_zip_end(mz_zip_archive *pZip); /* -------- ZIP writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS /* Inits a ZIP archive writer. */ /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif /* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ /* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ /* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ /* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ /* the archive is finalized the file's central directory will be hosed. */ mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ /* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif /* Adds a file to an archive by fully cloning the data from another archive. */ /* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); /* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ /* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ /* An archive must be manually finalized by calling this function for it to be valid. */ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); /* Finalizes a heap archive, returning a poiner to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); /* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ /* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ /* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be extracted. */ /* Returns NULL on failure. */ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifdef __cplusplus } #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ openjazz-20240919/ext/psmplug/000077500000000000000000000000001467303304200161175ustar00rootroot00000000000000openjazz-20240919/ext/psmplug/LICENSE000066400000000000000000000003061467303304200171230ustar00rootroot00000000000000Olivier Lapicque, author of Modplug, which is arguably the best quality MOD-playing software available, has placed his sound rendering code in the public domain. This library is based on that code. openjazz-20240919/ext/psmplug/fastmix.cpp000066400000000000000000001532551467303304200203110ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque * Markus Fick spline + fir-resampler */ #include "stdafx.h" #include "sndfile.h" #include #ifdef MSC_VER #pragma bss_seg(".modplug") #endif // Front Mix Buffer (Also room for interleaved rear mix) int MixSoundBuffer[MIXBUFFERSIZE*4]; // Reverb Mix Buffer #ifndef MODPLUG_NO_REVERB int MixReverbBuffer[MIXBUFFERSIZE*2]; extern UINT gnReverbSend; #endif int MixRearBuffer[MIXBUFFERSIZE*2]; float MixFloatBuffer[MIXBUFFERSIZE*2]; #ifdef MSC_VER #pragma bss_seg() #endif extern LONG gnDryROfsVol; extern LONG gnDryLOfsVol; extern LONG gnRvbROfsVol; extern LONG gnRvbLOfsVol; /* *----------------------------------------------------------------------------- cubic spline interpolation doc, (derived from "digital image warping", g. wolberg) interpolation polynomial: f(x) = A3*(x-floor(x))**3 + A2*(x-floor(x))**2 + A1*(x-floor(x)) + A0 with Y = equispaced data points (dist=1), YD = first derivates of data points and IP = floor(x) the A[0..3] can be found by solving A0 = Y[IP] A1 = YD[IP] A2 = 3*(Y[IP+1]-Y[IP])-2.0*YD[IP]-YD[IP+1] A3 = -2.0 * (Y[IP+1]-Y[IP]) + YD[IP] - YD[IP+1] with the first derivates as YD[IP] = 0.5 * (Y[IP+1] - Y[IP-1]); YD[IP+1] = 0.5 * (Y[IP+2] - Y[IP]) the coefs becomes A0 = Y[IP] A1 = YD[IP] = 0.5*(Y[IP+1] - Y[IP-1]); A2 = 3.0*(Y[IP+1]-Y[IP])-2.0*YD[IP]-YD[IP+1] = 3.0*(Y[IP+1]-Y[IP]) - 0.5*2.0*(Y[IP+1]-Y[IP-1]) - 0.5*(Y[IP+2]-Y[IP]) = 3.0*Y[IP+1] - 3.0*Y[IP] - Y[IP+1] + Y[IP-1] - 0.5*Y[IP+2] + 0.5*Y[IP] = -0.5*Y[IP+2] + 2.0 * Y[IP+1] - 2.5*Y[IP] + Y[IP-1] = Y[IP-1] + 2 * Y[IP+1] - 0.5 * (5.0 * Y[IP] + Y[IP+2]) A3 = -2.0*(Y[IP+1]-Y[IP]) + YD[IP] + YD[IP+1] = -2.0*Y[IP+1] + 2.0*Y[IP] + 0.5*(Y[IP+1]-Y[IP-1]) + 0.5*(Y[IP+2]-Y[IP]) = -2.0*Y[IP+1] + 2.0*Y[IP] + 0.5*Y[IP+1] - 0.5*Y[IP-1] + 0.5*Y[IP+2] - 0.5*Y[IP] = 0.5 * Y[IP+2] - 1.5 * Y[IP+1] + 1.5 * Y[IP] - 0.5 * Y[IP-1] = 0.5 * (3.0 * (Y[IP] - Y[IP+1]) - Y[IP-1] + YP[IP+2]) then interpolated data value is (horner rule) out = (((A3*x)+A2)*x+A1)*x+A0 this gives parts of data points Y[IP-1] to Y[IP+2] of part x**3 x**2 x**1 x**0 Y[IP-1] -0.5 1 -0.5 0 Y[IP] 1.5 -2.5 0 1 Y[IP+1] -1.5 2 0.5 0 Y[IP+2] 0.5 -0.5 0 0 *--------------------------------------------------------------------------- */ // number of bits used to scale spline coefs #define SPLINE_QUANTBITS 14 #define SPLINE_QUANTSCALE (1L< _LScale) ? _LScale : _LCm1) ); lut[_LIdx+1] = (signed short)( (_LC0 < -_LScale) ? -_LScale : ((_LC0 > _LScale) ? _LScale : _LC0 ) ); lut[_LIdx+2] = (signed short)( (_LC1 < -_LScale) ? -_LScale : ((_LC1 > _LScale) ? _LScale : _LC1 ) ); lut[_LIdx+3] = (signed short)( (_LC2 < -_LScale) ? -_LScale : ((_LC2 > _LScale) ? _LScale : _LC2 ) ); #ifdef SPLINE_CLAMPFORUNITY _LSum = lut[_LIdx+0]+lut[_LIdx+1]+lut[_LIdx+2]+lut[_LIdx+3]; if( _LSum != SPLINE_QUANTSCALE ) { int _LMax = _LIdx; if( lut[_LIdx+1]>lut[_LMax] ) _LMax = _LIdx+1; if( lut[_LIdx+2]>lut[_LMax] ) _LMax = _LIdx+2; if( lut[_LIdx+3]>lut[_LMax] ) _LMax = _LIdx+3; lut[_LMax] += ((signed short)SPLINE_QUANTSCALE-_LSum); } #endif } } CzCUBICSPLINE::~CzCUBICSPLINE( ) { // nothing todo } CzCUBICSPLINE sspline; /* ------------------------------------------------------------------------------ fir interpolation doc, (derived from "an engineer's guide to fir digital filters", n.j. loy) calculate coefficients for ideal lowpass filter (with cutoff = fc in 0..1 (mapped to 0..nyquist)) c[-N..N] = (i==0) ? fc : sin(fc*pi*i)/(pi*i) then apply selected window to coefficients c[-N..N] *= w(0..N) with n in 2*N and w(n) being a window function (see loy) then calculate gain and scale filter coefs to have unity gain. ------------------------------------------------------------------------------ */ // quantizer scale of window coefs #define WFIR_QUANTBITS 15 #define WFIR_QUANTSCALE (1L<>1) // cutoff (1.0 == pi/2) #define WFIR_CUTOFF 0.90f // wfir type #define WFIR_HANN 0 #define WFIR_HAMMING 1 #define WFIR_BLACKMANEXACT 2 #define WFIR_BLACKMAN3T61 3 #define WFIR_BLACKMAN3T67 4 #define WFIR_BLACKMAN4T92 5 #define WFIR_BLACKMAN4T74 6 #define WFIR_KAISER4T 7 #define WFIR_TYPE WFIR_BLACKMANEXACT // wfir help #ifndef M_zPI #define M_zPI 3.1415926535897932384626433832795 #endif #define M_zEPS 1e-8 #define M_zBESSELEPS 1e-21 class CzWINDOWEDFIR { public: CzWINDOWEDFIR( ); ~CzWINDOWEDFIR( ); float coef( int _PCnr, float _POfs, float _PCut, int _PWidth, int _PType ) //OLD args to coef: float _PPos, float _PFc, int _PLen ) { double _LWidthM1 = _PWidth-1; double _LWidthM1Half = 0.5*_LWidthM1; double _LPosU = ((double)_PCnr - _POfs); double _LPos = _LPosU-_LWidthM1Half; double _LPIdl = 2.0*M_zPI/_LWidthM1; double _LWc,_LSi; if( fabs(_LPos)_LScale)?_LScale:_LCoef) ); } } } CzWINDOWEDFIR::~CzWINDOWEDFIR() { // nothing todo } CzWINDOWEDFIR sfir; // ---------------------------------------------------------------------------- // MIXING MACROS // ---------------------------------------------------------------------------- #define SNDMIX_BEGINSAMPLELOOP8\ MODCHANNEL * const pChn = pChannel;\ nPos = pChn->nPosLo;\ const signed char *p = (signed char *)(pChn->pCurrentSample+pChn->nPos);\ if (pChn->dwFlags & CHN_STEREO) p += pChn->nPos;\ int *pvol = pbuffer;\ do { #define SNDMIX_BEGINSAMPLELOOP16\ MODCHANNEL * const pChn = pChannel;\ nPos = pChn->nPosLo;\ const signed short *p = (signed short *)(pChn->pCurrentSample+(pChn->nPos*2));\ if (pChn->dwFlags & CHN_STEREO) p += pChn->nPos;\ int *pvol = pbuffer;\ do { #define SNDMIX_ENDSAMPLELOOP\ nPos += pChn->nInc;\ } while (pvol < pbufmax);\ pChn->nPos += nPos >> 16;\ pChn->nPosLo = nPos & 0xFFFF; #define SNDMIX_ENDSAMPLELOOP8 SNDMIX_ENDSAMPLELOOP #define SNDMIX_ENDSAMPLELOOP16 SNDMIX_ENDSAMPLELOOP ////////////////////////////////////////////////////////////////////////////// // Mono // No interpolation #define SNDMIX_GETMONOVOL8NOIDO\ int vol = p[nPos >> 16] << 8; #define SNDMIX_GETMONOVOL16NOIDO\ int vol = p[nPos >> 16]; // Linear Interpolation #define SNDMIX_GETMONOVOL8LINEAR\ int poshi = nPos >> 16;\ int poslo = (nPos >> 8) & 0xFF;\ int srcvol = p[poshi];\ int destvol = p[poshi+1];\ int vol = (srcvol<<8) + ((int)(poslo * (destvol - srcvol))); #define SNDMIX_GETMONOVOL16LINEAR\ int poshi = nPos >> 16;\ int poslo = (nPos >> 8) & 0xFF;\ int srcvol = p[poshi];\ int destvol = p[poshi+1];\ int vol = srcvol + ((int)(poslo * (destvol - srcvol)) >> 8); // spline interpolation (2 guard bits should be enough???) #define SPLINE_FRACSHIFT ((16-SPLINE_FRACBITS)-2) #define SPLINE_FRACMASK (((1L<<(16-SPLINE_FRACSHIFT))-1)&~3) #define SNDMIX_GETMONOVOL8SPLINE \ int poshi = nPos >> 16; \ int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ int vol = (CzCUBICSPLINE::lut[poslo ]*(int)p[poshi-1] + \ CzCUBICSPLINE::lut[poslo+1]*(int)p[poshi ] + \ CzCUBICSPLINE::lut[poslo+3]*(int)p[poshi+2] + \ CzCUBICSPLINE::lut[poslo+2]*(int)p[poshi+1]) >> SPLINE_8SHIFT; #define SNDMIX_GETMONOVOL16SPLINE \ int poshi = nPos >> 16; \ int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ int vol = (CzCUBICSPLINE::lut[poslo ]*(int)p[poshi-1] + \ CzCUBICSPLINE::lut[poslo+1]*(int)p[poshi ] + \ CzCUBICSPLINE::lut[poslo+3]*(int)p[poshi+2] + \ CzCUBICSPLINE::lut[poslo+2]*(int)p[poshi+1]) >> SPLINE_16SHIFT; // fir interpolation #define WFIR_FRACSHIFT (16-(WFIR_FRACBITS+1+WFIR_LOG2WIDTH)) #define WFIR_FRACMASK ((((1L<<(17-WFIR_FRACSHIFT))-1)&~((1L<> 16;\ int poslo = (nPos & 0xFFFF);\ int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ int vol = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[poshi+1-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[poshi+2-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[poshi+3-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[poshi+4-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+4]*(int)p[poshi+5-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[poshi+6-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[poshi+7-4]); \ vol += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[poshi+8-4]); \ vol >>= WFIR_8SHIFT; #define SNDMIX_GETMONOVOL16FIRFILTER \ int poshi = nPos >> 16;\ int poslo = (nPos & 0xFFFF);\ int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ int vol1 = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[poshi+1-4]); \ vol1 += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[poshi+2-4]); \ vol1 += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[poshi+3-4]); \ vol1 += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[poshi+4-4]); \ int vol2 = (CzWINDOWEDFIR::lut[firidx+4]*(int)p[poshi+5-4]); \ vol2 += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[poshi+6-4]); \ vol2 += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[poshi+7-4]); \ vol2 += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[poshi+8-4]); \ int vol = ((vol1>>1)+(vol2>>1)) >> (WFIR_16BITSHIFT-1); ///////////////////////////////////////////////////////////////////////////// // Stereo // No interpolation #define SNDMIX_GETSTEREOVOL8NOIDO\ int vol_l = p[(nPos>>16)*2] << 8;\ int vol_r = p[(nPos>>16)*2+1] << 8; #define SNDMIX_GETSTEREOVOL16NOIDO\ int vol_l = p[(nPos>>16)*2];\ int vol_r = p[(nPos>>16)*2+1]; // Linear Interpolation #define SNDMIX_GETSTEREOVOL8LINEAR\ int poshi = nPos >> 16;\ int poslo = (nPos >> 8) & 0xFF;\ int srcvol_l = p[poshi*2];\ int vol_l = (srcvol_l<<8) + ((int)(poslo * (p[poshi*2+2] - srcvol_l)));\ int srcvol_r = p[poshi*2+1];\ int vol_r = (srcvol_r<<8) + ((int)(poslo * (p[poshi*2+3] - srcvol_r))); #define SNDMIX_GETSTEREOVOL16LINEAR\ int poshi = nPos >> 16;\ int poslo = (nPos >> 8) & 0xFF;\ int srcvol_l = p[poshi*2];\ int vol_l = srcvol_l + ((int)(poslo * (p[poshi*2+2] - srcvol_l)) >> 8);\ int srcvol_r = p[poshi*2+1];\ int vol_r = srcvol_r + ((int)(poslo * (p[poshi*2+3] - srcvol_r)) >> 8);\ // Spline Interpolation #define SNDMIX_GETSTEREOVOL8SPLINE \ int poshi = nPos >> 16; \ int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ int vol_l = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2 ] + \ CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2 ] + \ CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2 ] + \ CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2 ]) >> SPLINE_8SHIFT; \ int vol_r = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2+1] + \ CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2+1] + \ CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2+1] + \ CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2+1]) >> SPLINE_8SHIFT; #define SNDMIX_GETSTEREOVOL16SPLINE \ int poshi = nPos >> 16; \ int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ int vol_l = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2 ] + \ CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2 ] + \ CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2 ] + \ CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2 ]) >> SPLINE_16SHIFT; \ int vol_r = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2+1] + \ CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2+1] + \ CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2+1] + \ CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2+1]) >> SPLINE_16SHIFT; // fir interpolation #define SNDMIX_GETSTEREOVOL8FIRFILTER \ int poshi = nPos >> 16;\ int poslo = (nPos & 0xFFFF);\ int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ int vol_l = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2 ]); \ vol_l += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2 ]); \ vol_l >>= WFIR_8SHIFT; \ int vol_r = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2+1]); \ vol_r += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2+1]); \ vol_r >>= WFIR_8SHIFT; #define SNDMIX_GETSTEREOVOL16FIRFILTER \ int poshi = nPos >> 16;\ int poslo = (nPos & 0xFFFF);\ int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ int vol1_l = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2 ]); \ vol1_l += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2 ]); \ vol1_l += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2 ]); \ vol1_l += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2 ]); \ int vol2_l = (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2 ]); \ vol2_l += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2 ]); \ vol2_l += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2 ]); \ vol2_l += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2 ]); \ int vol_l = ((vol1_l>>1)+(vol2_l>>1)) >> (WFIR_16BITSHIFT-1); \ int vol1_r = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2+1]); \ vol1_r += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2+1]); \ vol1_r += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2+1]); \ vol1_r += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2+1]); \ int vol2_r = (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2+1]); \ vol2_r += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2+1]); \ vol2_r += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2+1]); \ vol2_r += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2+1]); \ int vol_r = ((vol1_r>>1)+(vol2_r>>1)) >> (WFIR_16BITSHIFT-1); ///////////////////////////////////////////////////////////////////////////// #define SNDMIX_STOREMONOVOL\ pvol[0] += vol * pChn->nRightVol;\ pvol[1] += vol * pChn->nLeftVol;\ pvol += 2; #define SNDMIX_STORESTEREOVOL\ pvol[0] += vol_l * pChn->nRightVol;\ pvol[1] += vol_r * pChn->nLeftVol;\ pvol += 2; #define SNDMIX_STOREFASTMONOVOL\ int v = vol * pChn->nRightVol;\ pvol[0] += v;\ pvol[1] += v;\ pvol += 2; #define SNDMIX_RAMPMONOVOL\ nRampLeftVol += pChn->nLeftRamp;\ nRampRightVol += pChn->nRightRamp;\ pvol[0] += vol * (nRampRightVol >> VOLUMERAMPPRECISION);\ pvol[1] += vol * (nRampLeftVol >> VOLUMERAMPPRECISION);\ pvol += 2; #define SNDMIX_RAMPFASTMONOVOL\ nRampRightVol += pChn->nRightRamp;\ int fastvol = vol * (nRampRightVol >> VOLUMERAMPPRECISION);\ pvol[0] += fastvol;\ pvol[1] += fastvol;\ pvol += 2; #define SNDMIX_RAMPSTEREOVOL\ nRampLeftVol += pChn->nLeftRamp;\ nRampRightVol += pChn->nRightRamp;\ pvol[0] += vol_l * (nRampRightVol >> VOLUMERAMPPRECISION);\ pvol[1] += vol_r * (nRampLeftVol >> VOLUMERAMPPRECISION);\ pvol += 2; /////////////////////////////////////////////////// // Resonant Filters // Mono #define MIX_BEGIN_FILTER\ int fy1 = pChannel->nFilter_Y1;\ int fy2 = pChannel->nFilter_Y2;\ #define MIX_END_FILTER\ pChannel->nFilter_Y1 = fy1;\ pChannel->nFilter_Y2 = fy2; #define SNDMIX_PROCESSFILTER\ vol = (vol * pChn->nFilter_A0 + fy1 * pChn->nFilter_B0 + fy2 * pChn->nFilter_B1 + 4096) >> 13;\ fy2 = fy1;\ fy1 = vol;\ // Stereo #define MIX_BEGIN_STEREO_FILTER\ int fy1 = pChannel->nFilter_Y1;\ int fy2 = pChannel->nFilter_Y2;\ int fy3 = pChannel->nFilter_Y3;\ int fy4 = pChannel->nFilter_Y4;\ #define MIX_END_STEREO_FILTER\ pChannel->nFilter_Y1 = fy1;\ pChannel->nFilter_Y2 = fy2;\ pChannel->nFilter_Y3 = fy3;\ pChannel->nFilter_Y4 = fy4;\ #define SNDMIX_PROCESSSTEREOFILTER\ vol_l = (vol_l * pChn->nFilter_A0 + fy1 * pChn->nFilter_B0 + fy2 * pChn->nFilter_B1 + 4096) >> 13;\ vol_r = (vol_r * pChn->nFilter_A0 + fy3 * pChn->nFilter_B0 + fy4 * pChn->nFilter_B1 + 4096) >> 13;\ fy2 = fy1; fy1 = vol_l;\ fy4 = fy3; fy3 = vol_r;\ ////////////////////////////////////////////////////////// // Interfaces typedef VOID (MPPASMCALL * LPMIXINTERFACE)(MODCHANNEL *, int *, int *); #define BEGIN_MIX_INTERFACE(func)\ VOID MPPASMCALL func(MODCHANNEL *pChannel, int *pbuffer, int *pbufmax)\ {\ LONG nPos; #define END_MIX_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ } // Volume Ramps #define BEGIN_RAMPMIX_INTERFACE(func)\ BEGIN_MIX_INTERFACE(func)\ LONG nRampRightVol = pChannel->nRampRightVol;\ LONG nRampLeftVol = pChannel->nRampLeftVol; #define END_RAMPMIX_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ pChannel->nRampRightVol = nRampRightVol;\ pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ pChannel->nRampLeftVol = nRampLeftVol;\ pChannel->nLeftVol = nRampLeftVol >> VOLUMERAMPPRECISION;\ } #define BEGIN_FASTRAMPMIX_INTERFACE(func)\ BEGIN_MIX_INTERFACE(func)\ LONG nRampRightVol = pChannel->nRampRightVol; #define END_FASTRAMPMIX_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ pChannel->nRampRightVol = nRampRightVol;\ pChannel->nRampLeftVol = nRampRightVol;\ pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ pChannel->nLeftVol = pChannel->nRightVol;\ } // Mono Resonant Filters #define BEGIN_MIX_FLT_INTERFACE(func)\ BEGIN_MIX_INTERFACE(func)\ MIX_BEGIN_FILTER #define END_MIX_FLT_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ MIX_END_FILTER\ } #define BEGIN_RAMPMIX_FLT_INTERFACE(func)\ BEGIN_MIX_INTERFACE(func)\ LONG nRampRightVol = pChannel->nRampRightVol;\ LONG nRampLeftVol = pChannel->nRampLeftVol;\ MIX_BEGIN_FILTER #define END_RAMPMIX_FLT_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ MIX_END_FILTER\ pChannel->nRampRightVol = nRampRightVol;\ pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ pChannel->nRampLeftVol = nRampLeftVol;\ pChannel->nLeftVol = nRampLeftVol >> VOLUMERAMPPRECISION;\ } // Stereo Resonant Filters #define BEGIN_MIX_STFLT_INTERFACE(func)\ BEGIN_MIX_INTERFACE(func)\ MIX_BEGIN_STEREO_FILTER #define END_MIX_STFLT_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ MIX_END_STEREO_FILTER\ } #define BEGIN_RAMPMIX_STFLT_INTERFACE(func)\ BEGIN_MIX_INTERFACE(func)\ LONG nRampRightVol = pChannel->nRampRightVol;\ LONG nRampLeftVol = pChannel->nRampLeftVol;\ MIX_BEGIN_STEREO_FILTER #define END_RAMPMIX_STFLT_INTERFACE()\ SNDMIX_ENDSAMPLELOOP\ MIX_END_STEREO_FILTER\ pChannel->nRampRightVol = nRampRightVol;\ pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ pChannel->nRampLeftVol = nRampLeftVol;\ pChannel->nLeftVol = nRampLeftVol >> VOLUMERAMPPRECISION;\ } ///////////////////////////////////////////////////// // static void MPPASMCALL X86_InitMixBuffer(int *pBuffer, UINT nSamples); static void MPPASMCALL X86_EndChannelOfs(MODCHANNEL *pChannel, int *pBuffer, UINT nSamples); void MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs); ///////////////////////////////////////////////////// // Mono samples functions BEGIN_MIX_INTERFACE(Mono8BitMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8NOIDO SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono16BitMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16NOIDO SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono8BitLinearMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8LINEAR SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono16BitLinearMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16LINEAR SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono8BitSplineMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8SPLINE SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono16BitSplineMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16SPLINE SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono8BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8FIRFILTER SNDMIX_STOREMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Mono16BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16FIRFILTER SNDMIX_STOREMONOVOL END_MIX_INTERFACE() // Volume Ramps BEGIN_RAMPMIX_INTERFACE(Mono8BitRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8NOIDO SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono16BitRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16NOIDO SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono8BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8LINEAR SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono16BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16LINEAR SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono8BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8SPLINE SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono16BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16SPLINE SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono8BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8FIRFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Mono16BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16FIRFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_INTERFACE() ////////////////////////////////////////////////////// // Fast mono mix for leftvol=rightvol (1 less imul) BEGIN_MIX_INTERFACE(FastMono8BitMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8NOIDO SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono16BitMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16NOIDO SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono8BitLinearMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8LINEAR SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono16BitLinearMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16LINEAR SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono8BitSplineMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8SPLINE SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono16BitSplineMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16SPLINE SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono8BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8FIRFILTER SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(FastMono16BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16FIRFILTER SNDMIX_STOREFASTMONOVOL END_MIX_INTERFACE() // Fast Ramps BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8NOIDO SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16NOIDO SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8LINEAR SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16LINEAR SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8SPLINE SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16SPLINE SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8FIRFILTER SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16FIRFILTER SNDMIX_RAMPFASTMONOVOL END_FASTRAMPMIX_INTERFACE() ////////////////////////////////////////////////////// // Stereo samples BEGIN_MIX_INTERFACE(Stereo8BitMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8NOIDO SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo16BitMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16NOIDO SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo8BitLinearMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8LINEAR SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo16BitLinearMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16LINEAR SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo8BitSplineMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8SPLINE SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo16BitSplineMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16SPLINE SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo8BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8FIRFILTER SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() BEGIN_MIX_INTERFACE(Stereo16BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16FIRFILTER SNDMIX_STORESTEREOVOL END_MIX_INTERFACE() // Volume Ramps BEGIN_RAMPMIX_INTERFACE(Stereo8BitRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8NOIDO SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo16BitRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16NOIDO SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo8BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8LINEAR SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo16BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16LINEAR SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo8BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8SPLINE SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo16BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16SPLINE SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo8BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8FIRFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() BEGIN_RAMPMIX_INTERFACE(Stereo16BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16FIRFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_INTERFACE() ////////////////////////////////////////////////////// // Resonant Filter Mix #ifndef NO_FILTER // Mono Filter Mix BEGIN_MIX_FLT_INTERFACE(FilterMono8BitMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8NOIDO SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono16BitMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16NOIDO SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono8BitLinearMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8LINEAR SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono16BitLinearMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16LINEAR SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono8BitSplineMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8SPLINE SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono16BitSplineMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16SPLINE SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono8BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8FIRFILTER SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() BEGIN_MIX_FLT_INTERFACE(FilterMono16BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16FIRFILTER SNDMIX_PROCESSFILTER SNDMIX_STOREMONOVOL END_MIX_FLT_INTERFACE() // Filter + Ramp BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8NOIDO SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16NOIDO SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8LINEAR SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16LINEAR SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8SPLINE SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16SPLINE SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETMONOVOL8FIRFILTER SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETMONOVOL16FIRFILTER SNDMIX_PROCESSFILTER SNDMIX_RAMPMONOVOL END_RAMPMIX_FLT_INTERFACE() // Stereo Filter Mix BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8NOIDO SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16NOIDO SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitLinearMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8LINEAR SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitLinearMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16LINEAR SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitSplineMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8SPLINE SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitSplineMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16SPLINE SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8FIRFILTER SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitFirFilterMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16FIRFILTER SNDMIX_PROCESSSTEREOFILTER SNDMIX_STORESTEREOVOL END_MIX_STFLT_INTERFACE() // Stereo Filter + Ramp BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8NOIDO SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16NOIDO SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8LINEAR SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitLinearRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16LINEAR SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8SPLINE SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitSplineRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16SPLINE SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP8 SNDMIX_GETSTEREOVOL8FIRFILTER SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitFirFilterRampMix) SNDMIX_BEGINSAMPLELOOP16 SNDMIX_GETSTEREOVOL16FIRFILTER SNDMIX_PROCESSSTEREOFILTER SNDMIX_RAMPSTEREOVOL END_RAMPMIX_STFLT_INTERFACE() #else // Mono #define FilterMono8BitMix Mono8BitMix #define FilterMono16BitMix Mono16BitMix #define FilterMono8BitLinearMix Mono8BitLinearMix #define FilterMono16BitLinearMix Mono16BitLinearMix #define FilterMono8BitSplineMix Mono8BitSplineMix #define FilterMono16BitSplineMix Mono16BitSplineMix #define FilterMono8BitFirFilterMix Mono8BitFirFilterMix #define FilterMono16BitFirFilterMix Mono16BitFirFilterMix #define FilterMono8BitRampMix Mono8BitRampMix #define FilterMono16BitRampMix Mono16BitRampMix #define FilterMono8BitLinearRampMix Mono8BitLinearRampMix #define FilterMono16BitLinearRampMix Mono16BitLinearRampMix #define FilterMono8BitSplineRampMix Mono8BitSplineRampMix #define FilterMono16BitSplineRampMix Mono16BitSplineRampMix #define FilterMono8BitFirFilterRampMix Mono8BitFirFilterRampMix #define FilterMono16BitFirFilterRampMix Mono16BitFirFilterRampMix // Stereo #define FilterStereo8BitMix Stereo8BitMix #define FilterStereo16BitMix Stereo16BitMix #define FilterStereo8BitLinearMix Stereo8BitLinearMix #define FilterStereo16BitLinearMix Stereo16BitLinearMix #define FilterStereo8BitSplineMix Stereo8BitSplineMix #define FilterStereo16BitSplineMix Stereo16BitSplineMix #define FilterStereo8BitFirFilterMix Stereo8BitFirFilterMix #define FilterStereo16BitFirFilterMix Stereo16BitFirFilterMix #define FilterStereo8BitRampMix Stereo8BitRampMix #define FilterStereo16BitRampMix Stereo16BitRampMix #define FilterStereo8BitLinearRampMix Stereo8BitLinearRampMix #define FilterStereo16BitLinearRampMix Stereo16BitLinearRampMix #define FilterStereo8BitSplineRampMix Stereo8BitSplineRampMix #define FilterStereo16BitSplineRampMix Stereo16BitSplineRampMix #define FilterStereo8BitFirFilterRampMix Stereo8BitFirFilterRampMix #define FilterStereo16BitFirFilterRampMix Stereo16BitFirFilterRampMix #endif /////////////////////////////////////////////////////////////////////////////// // // Mix function tables // // // Index is as follow: // [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) // [b2] ramp // [b3] filter // [b5-b4] src type // #define MIXNDX_16BIT 0x01 #define MIXNDX_STEREO 0x02 #define MIXNDX_RAMP 0x04 #define MIXNDX_FILTER 0x08 #define MIXNDX_LINEARSRC 0x10 #define MIXNDX_SPLINESRC 0x20 #define MIXNDX_FIRSRC 0x30 static const LPMIXINTERFACE gpMixFunctionTable[2*2*16] = { // No SRC Mono8BitMix, Mono16BitMix, Stereo8BitMix, Stereo16BitMix, Mono8BitRampMix, Mono16BitRampMix, Stereo8BitRampMix, Stereo16BitRampMix, // No SRC, Filter FilterMono8BitMix, FilterMono16BitMix, FilterStereo8BitMix, FilterStereo16BitMix, FilterMono8BitRampMix, FilterMono16BitRampMix, FilterStereo8BitRampMix, FilterStereo16BitRampMix, // Linear SRC Mono8BitLinearMix, Mono16BitLinearMix, Stereo8BitLinearMix, Stereo16BitLinearMix, Mono8BitLinearRampMix, Mono16BitLinearRampMix, Stereo8BitLinearRampMix,Stereo16BitLinearRampMix, // Linear SRC, Filter FilterMono8BitLinearMix, FilterMono16BitLinearMix, FilterStereo8BitLinearMix, FilterStereo16BitLinearMix, FilterMono8BitLinearRampMix, FilterMono16BitLinearRampMix, FilterStereo8BitLinearRampMix, FilterStereo16BitLinearRampMix, // FirFilter SRC Mono8BitSplineMix, Mono16BitSplineMix, Stereo8BitSplineMix, Stereo16BitSplineMix, Mono8BitSplineRampMix, Mono16BitSplineRampMix, Stereo8BitSplineRampMix,Stereo16BitSplineRampMix, // Spline SRC, Filter FilterMono8BitSplineMix, FilterMono16BitSplineMix, FilterStereo8BitSplineMix, FilterStereo16BitSplineMix, FilterMono8BitSplineRampMix, FilterMono16BitSplineRampMix, FilterStereo8BitSplineRampMix, FilterStereo16BitSplineRampMix, // FirFilter SRC Mono8BitFirFilterMix, Mono16BitFirFilterMix, Stereo8BitFirFilterMix, Stereo16BitFirFilterMix, Mono8BitFirFilterRampMix, Mono16BitFirFilterRampMix, Stereo8BitFirFilterRampMix, Stereo16BitFirFilterRampMix, // FirFilter SRC, Filter FilterMono8BitFirFilterMix, FilterMono16BitFirFilterMix, FilterStereo8BitFirFilterMix, FilterStereo16BitFirFilterMix, FilterMono8BitFirFilterRampMix, FilterMono16BitFirFilterRampMix, FilterStereo8BitFirFilterRampMix, FilterStereo16BitFirFilterRampMix }; static const LPMIXINTERFACE gpFastMixFunctionTable[2*2*16] = { // No SRC FastMono8BitMix, FastMono16BitMix, Stereo8BitMix, Stereo16BitMix, FastMono8BitRampMix, FastMono16BitRampMix, Stereo8BitRampMix, Stereo16BitRampMix, // No SRC, Filter FilterMono8BitMix, FilterMono16BitMix, FilterStereo8BitMix, FilterStereo16BitMix, FilterMono8BitRampMix, FilterMono16BitRampMix, FilterStereo8BitRampMix, FilterStereo16BitRampMix, // Linear SRC FastMono8BitLinearMix, FastMono16BitLinearMix, Stereo8BitLinearMix, Stereo16BitLinearMix, FastMono8BitLinearRampMix, FastMono16BitLinearRampMix, Stereo8BitLinearRampMix, Stereo16BitLinearRampMix, // Linear SRC, Filter FilterMono8BitLinearMix, FilterMono16BitLinearMix, FilterStereo8BitLinearMix, FilterStereo16BitLinearMix, FilterMono8BitLinearRampMix, FilterMono16BitLinearRampMix, FilterStereo8BitLinearRampMix, FilterStereo16BitLinearRampMix, // Spline SRC Mono8BitSplineMix, Mono16BitSplineMix, Stereo8BitSplineMix, Stereo16BitSplineMix, Mono8BitSplineRampMix, Mono16BitSplineRampMix, Stereo8BitSplineRampMix, Stereo16BitSplineRampMix, // Spline SRC, Filter FilterMono8BitSplineMix, FilterMono16BitSplineMix, FilterStereo8BitSplineMix, FilterStereo16BitSplineMix, FilterMono8BitSplineRampMix, FilterMono16BitSplineRampMix, FilterStereo8BitSplineRampMix, FilterStereo16BitSplineRampMix, // FirFilter SRC Mono8BitFirFilterMix, Mono16BitFirFilterMix, Stereo8BitFirFilterMix, Stereo16BitFirFilterMix, Mono8BitFirFilterRampMix, Mono16BitFirFilterRampMix, Stereo8BitFirFilterRampMix, Stereo16BitFirFilterRampMix, // FirFilter SRC, Filter FilterMono8BitFirFilterMix, FilterMono16BitFirFilterMix, FilterStereo8BitFirFilterMix, FilterStereo16BitFirFilterMix, FilterMono8BitFirFilterRampMix, FilterMono16BitFirFilterRampMix, FilterStereo8BitFirFilterRampMix, FilterStereo16BitFirFilterRampMix, }; ///////////////////////////////////////////////////////////////////////// static LONG MPPFASTCALL GetSampleCount(MODCHANNEL *pChn, LONG nSamples) //--------------------------------------------------------------------- { LONG nLoopStart = (pChn->dwFlags & CHN_LOOP) ? pChn->nLoopStart : 0; LONG nInc = pChn->nInc; if ((nSamples <= 0) || (!nInc) || (!pChn->nLength)) return 0; // Under zero ? if ((LONG)pChn->nPos < nLoopStart) { if (nInc < 0) { // Invert loop for bidi loops LONG nDelta = ((nLoopStart - pChn->nPos) << 16) - (pChn->nPosLo & 0xffff); pChn->nPos = nLoopStart | (nDelta>>16); pChn->nPosLo = nDelta & 0xffff; if (((LONG)pChn->nPos < nLoopStart) || (pChn->nPos >= (nLoopStart+pChn->nLength)/2)) { pChn->nPos = nLoopStart; pChn->nPosLo = 0; } nInc = -nInc; pChn->nInc = nInc; pChn->dwFlags &= ~(CHN_PINGPONGFLAG); // go forward if ((!(pChn->dwFlags & CHN_LOOP)) || (pChn->nPos >= pChn->nLength)) { pChn->nPos = pChn->nLength; pChn->nPosLo = 0; return 0; } } else { // We probably didn't hit the loop end yet // (first loop), so we do nothing if ((LONG)pChn->nPos < 0) pChn->nPos = 0; } } else // Past the end if (pChn->nPos >= pChn->nLength) { if (!(pChn->dwFlags & CHN_LOOP)) return 0; // not looping -> stop this channel if (pChn->dwFlags & CHN_PINGPONGLOOP) { // Invert loop if (nInc > 0) { nInc = -nInc; pChn->nInc = nInc; } pChn->dwFlags |= CHN_PINGPONGFLAG; // adjust loop position LONG nDeltaHi = (pChn->nPos - pChn->nLength); LONG nDeltaLo = 0x10000 - (pChn->nPosLo & 0xffff); pChn->nPos = pChn->nLength - nDeltaHi - (nDeltaLo>>16); pChn->nPosLo = nDeltaLo & 0xffff; if ((pChn->nPos <= pChn->nLoopStart) || (pChn->nPos >= pChn->nLength)) pChn->nPos = pChn->nLength-1; } else { if (nInc < 0) // This is a bug { nInc = -nInc; pChn->nInc = nInc; } // Restart at loop start pChn->nPos += nLoopStart - pChn->nLength; if ((LONG)pChn->nPos < nLoopStart) pChn->nPos = pChn->nLoopStart; } } LONG nPos = pChn->nPos; // too big increment, and/or too small loop length if (nPos < nLoopStart) { if ((nPos < 0) || (nInc < 0)) return 0; } if ((nPos < 0) || (nPos >= (LONG)pChn->nLength)) return 0; LONG nPosLo = (USHORT)pChn->nPosLo, nSmpCount = nSamples; if (nInc < 0) { LONG nInv = -nInc; LONG maxsamples = 16384 / ((nInv>>16)+1); if (maxsamples < 2) maxsamples = 2; if (nSamples > maxsamples) nSamples = maxsamples; LONG nDeltaHi = (nInv>>16) * (nSamples - 1); LONG nDeltaLo = (nInv&0xffff) * (nSamples - 1); LONG nPosDest = nPos - nDeltaHi + ((nPosLo - nDeltaLo) >> 16); if (nPosDest < nLoopStart) { nSmpCount = (ULONG)(((((LONGLONG)nPos - nLoopStart) << 16) + nPosLo - 1) / nInv) + 1; } } else { LONG maxsamples = 16384 / ((nInc>>16)+1); if (maxsamples < 2) maxsamples = 2; if (nSamples > maxsamples) nSamples = maxsamples; LONG nDeltaHi = (nInc>>16) * (nSamples - 1); LONG nDeltaLo = (nInc&0xffff) * (nSamples - 1); LONG nPosDest = nPos + nDeltaHi + ((nPosLo + nDeltaLo)>>16); if (nPosDest >= (LONG)pChn->nLength) { nSmpCount = (ULONG)(((((LONGLONG)pChn->nLength - nPos) << 16) - nPosLo - 1) / nInc) + 1; } } if (nSmpCount <= 1) return 1; if (nSmpCount > nSamples) return nSamples; return nSmpCount; } UINT CSoundFile::CreateStereoMix(int count) //----------------------------------------- { LPLONG pOfsL, pOfsR; DWORD nchused, nchmixed; if (!count) return 0; if (gnChannels > 2) X86_InitMixBuffer(MixRearBuffer, count*2); nchused = nchmixed = 0; for (UINT nChn=0; nChnpCurrentSample) continue; pOfsR = &gnDryROfsVol; pOfsL = &gnDryLOfsVol; nFlags = 0; if (pChannel->dwFlags & CHN_16BIT) nFlags |= MIXNDX_16BIT; if (pChannel->dwFlags & CHN_STEREO) nFlags |= MIXNDX_STEREO; #ifndef NO_FILTER if (pChannel->dwFlags & CHN_FILTER) nFlags |= MIXNDX_FILTER; #endif if (!(pChannel->dwFlags & CHN_NOIDO)) { // use hq-fir mixer? if( (gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE)) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) nFlags += MIXNDX_FIRSRC; else if( (gdwSoundSetup & (SNDMIX_HQRESAMPLER)) == SNDMIX_HQRESAMPLER ) nFlags += MIXNDX_SPLINESRC; else nFlags += MIXNDX_LINEARSRC; // use } if ((nFlags < 0x40) && (pChannel->nLeftVol == pChannel->nRightVol) && ((!pChannel->nRampLength) || (pChannel->nLeftRamp == pChannel->nRightRamp))) { pMixFuncTable = gpFastMixFunctionTable; } else { pMixFuncTable = gpMixFunctionTable; } nsamples = count; #ifndef MODPLUG_NO_REVERB pbuffer = (gdwSoundSetup & SNDMIX_REVERB) ? MixReverbBuffer : MixSoundBuffer; if (pChannel->dwFlags & CHN_NOREVERB) pbuffer = MixSoundBuffer; if (pChannel->dwFlags & CHN_REVERB) pbuffer = MixReverbBuffer; if (pbuffer == MixReverbBuffer) { if (!gnReverbSend) memset(MixReverbBuffer, 0, count * 8); gnReverbSend += count; } #else pbuffer = MixSoundBuffer; #endif nchused++; //////////////////////////////////////////////////// SampleLooping: UINT nrampsamples = nsamples; if (pChannel->nRampLength > 0) { if ((LONG)nrampsamples > pChannel->nRampLength) nrampsamples = pChannel->nRampLength; } if ((nSmpCount = GetSampleCount(pChannel, nrampsamples)) <= 0) { // Stopping the channel pChannel->pCurrentSample = NULL; pChannel->nLength = 0; pChannel->nPos = 0; pChannel->nPosLo = 0; pChannel->nRampLength = 0; X86_EndChannelOfs(pChannel, pbuffer, nsamples); *pOfsR += pChannel->nROfs; *pOfsL += pChannel->nLOfs; pChannel->nROfs = pChannel->nLOfs = 0; pChannel->dwFlags &= ~CHN_PINGPONGFLAG; continue; } // Should we mix this channel ? UINT naddmix; if (((nchmixed >= m_nMaxMixChannels) && (!(gdwSoundSetup & SNDMIX_DIRECTTODISK))) || ((!pChannel->nRampLength) && (!(pChannel->nLeftVol|pChannel->nRightVol)))) { LONG delta = (pChannel->nInc * (LONG)nSmpCount) + (LONG)pChannel->nPosLo; pChannel->nPosLo = delta & 0xFFFF; pChannel->nPos += (delta >> 16); pChannel->nROfs = pChannel->nLOfs = 0; pbuffer += nSmpCount*2; naddmix = 0; } else // Do mixing { // Choose function for mixing LPMIXINTERFACE pMixFunc; pMixFunc = (pChannel->nRampLength) ? pMixFuncTable[nFlags|MIXNDX_RAMP] : pMixFuncTable[nFlags]; int *pbufmax = pbuffer + (nSmpCount*2); pChannel->nROfs = - *(pbufmax-2); pChannel->nLOfs = - *(pbufmax-1); pMixFunc(pChannel, pbuffer, pbufmax); pChannel->nROfs += *(pbufmax-2); pChannel->nLOfs += *(pbufmax-1); pbuffer = pbufmax; naddmix = 1; } nsamples -= nSmpCount; if (pChannel->nRampLength) { pChannel->nRampLength -= nSmpCount; if (pChannel->nRampLength <= 0) { pChannel->nRampLength = 0; pChannel->nRightVol = pChannel->nNewRightVol; pChannel->nLeftVol = pChannel->nNewLeftVol; pChannel->nRightRamp = pChannel->nLeftRamp = 0; if ((pChannel->dwFlags & CHN_NOTEFADE) && (!(pChannel->nFadeOutVol))) { pChannel->nLength = 0; pChannel->pCurrentSample = NULL; } } } if (nsamples > 0) goto SampleLooping; nchmixed += naddmix; } return nchused; } #ifdef MSC_VER #pragma warning (disable:4100) #endif // Clip and convert to 8 bit // The C version was written by Rani Assaf , I believe DWORD MPPASMCALL X86_Convert32To8(LPVOID lp8, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) { int vumin = *lpMin, vumax = *lpMax; unsigned char *p = (unsigned char *)lp8; for (UINT i=0; i MIXING_CLIPMAX) n = MIXING_CLIPMAX; if (n < vumin) vumin = n; else if (n > vumax) vumax = n; p[i] = (n >> (24-MIXING_ATTENUATION)) ^ 0x80; // 8-bit unsigned } *lpMin = vumin; *lpMax = vumax; return lSampleCount; } // Clip and convert to 16 bit // The C version was written by Rani Assaf , I believe DWORD MPPASMCALL X86_Convert32To16(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) { int vumin = *lpMin, vumax = *lpMax; signed short *p = (signed short *)lp16; for (UINT i=0; i MIXING_CLIPMAX) n = MIXING_CLIPMAX; if (n < vumin) vumin = n; else if (n > vumax) vumax = n; p[i] = n >> (16-MIXING_ATTENUATION); // 16-bit signed } *lpMin = vumin; *lpMax = vumax; return lSampleCount * 2; } // Clip and convert to 24 bit DWORD MPPASMCALL X86_Convert32To24(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) { UINT i ; int vumin = *lpMin, vumax = *lpMax; int n,p ; unsigned char* buf = (unsigned char*)lp16 ; for ( i=0; i MIXING_CLIPMAX) n = MIXING_CLIPMAX; if (n < vumin) vumin = n; else if (n > vumax) vumax = n; p = n >> (8-MIXING_ATTENUATION) ; // 24-bit signed #ifdef WORDS_BIGENDIAN buf[i*3+0] = p & 0xFF0000 >> 24; buf[i*3+1] = p & 0x00FF00 >> 16 ; buf[i*3+2] = p & 0x0000FF ; #else buf[i*3+0] = p & 0x0000FF ; buf[i*3+1] = p & 0x00FF00 >> 16; buf[i*3+2] = p & 0xFF0000 >> 24; #endif } *lpMin = vumin; *lpMax = vumax; return lSampleCount * 3; } // Clip and convert to 32 bit DWORD MPPASMCALL X86_Convert32To32(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) { UINT i ; int vumin = *lpMin, vumax = *lpMax; int32_t *p = (int32_t *)lp16; for ( i=0; i MIXING_CLIPMAX) n = MIXING_CLIPMAX; if (n < vumin) vumin = n; else if (n > vumax) vumax = n; p[i] = n << MIXING_ATTENUATION; // 32-bit signed } *lpMin = vumin; *lpMax = vumax; return lSampleCount * 4; } // Will fill in later. static void MPPASMCALL X86_InitMixBuffer(int *pBuffer, UINT nSamples) { memset(pBuffer, 0, nSamples * sizeof(int)); } // Multichannel not supported. void MPPASMCALL X86_InterleaveFrontRear(int *, int *, DWORD) { } VOID MPPASMCALL X86_MonoFromStereo(int *pMixBuf, UINT nSamples) { UINT j; for(UINT i = 0; i < nSamples; i++) { j = i << 1; pMixBuf[i] = (pMixBuf[j] + pMixBuf[j + 1]) >> 1; } } #define OFSDECAYSHIFT 8 #define OFSDECAYMASK 0xFF void MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs) //---------------------------------------------------------------------------- { int rofs = *lpROfs; int lofs = *lpLOfs; if ((!rofs) && (!lofs)) { X86_InitMixBuffer(pBuffer, nSamples*2); return; } for (UINT i=0; i>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; int x_l = (lofs + (((-lofs)>>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; rofs -= x_r; lofs -= x_l; pBuffer[i*2] = x_r; pBuffer[i*2+1] = x_l; } *lpROfs = rofs; *lpLOfs = lofs; } // Will fill in later. static void MPPASMCALL X86_EndChannelOfs(MODCHANNEL *pChannel, int *pBuffer, UINT nSamples) { int rofs = pChannel->nROfs; int lofs = pChannel->nLOfs; if ((!rofs) && (!lofs)) return; for (UINT i=0; i>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; int x_l = (lofs + (((-lofs)>>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; rofs -= x_r; lofs -= x_l; pBuffer[i*2] += x_r; pBuffer[i*2+1] += x_l; } pChannel->nROfs = rofs; pChannel->nLOfs = lofs; } openjazz-20240919/ext/psmplug/load_psm.cpp000066400000000000000000000710201467303304200204210ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque */ /////////////////////////////////////////////////// // // PSM module loader // /////////////////////////////////////////////////// #include "stdafx.h" #include "sndfile.h" //#define PSM_LOG #define PSM_ID_NEW 0x204d5350 #define PSM_ID_OLD 0xfe4d5350 #define IFFID_FILE 0x454c4946 #define IFFID_TITL 0x4c544954 #define IFFID_SDFT 0x54464453 #define IFFID_PBOD 0x444f4250 #define IFFID_SONG 0x474e4f53 #define IFFID_PATT 0x54544150 #define IFFID_DSMP 0x504d5344 #define IFFID_OPLH 0x484c504f #pragma pack(1) typedef struct _PSMCHUNK { DWORD id; DWORD len; DWORD listid; } PSMCHUNK; typedef struct _PSMSONGHDR { CHAR songname[8]; // "MAINSONG" BYTE reserved1; BYTE reserved2; BYTE channels; } PSMSONGHDR; typedef struct _PSMPATTERN { DWORD size; DWORD name; WORD rows; WORD reserved1; BYTE data[4]; } PSMPATTERN; typedef struct _PSMSAMPLE { BYTE flags; CHAR songname[8]; DWORD smpid; CHAR samplename[34]; DWORD reserved1; BYTE reserved2; BYTE insno; BYTE reserved3; DWORD length; DWORD loopstart; DWORD loopend; WORD reserved4; BYTE defvol; DWORD reserved5; DWORD samplerate; BYTE reserved6[19]; } PSMSAMPLE; #pragma pack() DWORD read_DWORD(LPCBYTE lpStream){ DWORD s; memcpy(&s,lpStream,sizeof(s)); return bswapLE32(s); } static void swap_PSMCHUNK(PSMCHUNK* p){ p->id = bswapLE32(p->id); p->len = bswapLE32(p->len); p->listid = bswapLE32(p->listid); } static void swap_PSMPATTERN(PSMPATTERN* p){ p->size = bswapLE32(p->size); p->name = bswapLE32(p->name); p->rows = bswapLE16(p->rows); } static void swap_PSMSAMPLE(PSMSAMPLE* p){ p->smpid = bswapLE32(p->smpid); p->length = bswapLE32(p->length); p->loopstart = bswapLE32(p->loopstart); p->loopend = bswapLE32(p->loopend); p->samplerate = bswapLE32(p->samplerate); } BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) //----------------------------------------------------------- { PSMCHUNK *pfh = (PSMCHUNK *)lpStream; DWORD dwMemPos, dwSongPos; DWORD patptrs[MAX_PATTERNS]; BYTE samplemap[MAX_SAMPLES]; UINT nPatterns; if (dwMemLength < 256) return FALSE; // Swap chunk swap_PSMCHUNK(pfh); // Chunk0: "PSM ",filesize,"FILE" if (pfh->id == PSM_ID_OLD) { #ifdef PSM_LOG Log("Old PSM format not supported\n"); #endif return FALSE; } if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE; m_nType = MOD_TYPE_PSM; m_nChannels = 16; m_nSamples = 0; nPatterns = 0; dwMemPos = 12; dwSongPos = 0; for (UINT iChPan=0; iChPan<16; iChPan++) { UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40; ChnSettings[iChPan].nPan = pan; } while (dwMemPos+8 < dwMemLength) { PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); swap_PSMCHUNK(pchunk); if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break; dwMemPos += 8; PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); ULONG len = pchunk->len; if (len) switch(pchunk->id) { // "TITL": Song title case IFFID_TITL: if (!pdata[0]) { pdata++; len--; } memcpy(m_szNames[0], pdata, (len>31) ? 31 : len); m_szNames[0][31] = 0; break; // "PBOD": Pattern case IFFID_PBOD: if ((len >= 12) && (nPatterns < MAX_PATTERNS)) { patptrs[nPatterns++] = dwMemPos-8; } break; // "SONG": Song description case IFFID_SONG: if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos)) { dwSongPos = dwMemPos - 8; } break; // "DSMP": Sample Data case IFFID_DSMP: if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES)) { m_nSamples++; MODINSTRUMENT *pins = &Ins[m_nSamples]; PSMSAMPLE *psmp = (PSMSAMPLE *)pdata; swap_PSMSAMPLE(psmp); memcpy(m_szNames[m_nSamples], psmp->samplename, 31); m_szNames[m_nSamples][31] = 0; samplemap[m_nSamples-1] = (BYTE)m_nSamples; // Init sample pins->nGlobalVol = 0x40; pins->nC4Speed = psmp->samplerate; pins->nLength = psmp->length; pins->nLoopStart = psmp->loopstart; pins->nLoopEnd = psmp->loopend; pins->nPan = 128; pins->nVolume = (psmp->defvol+1) * 2; pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0; if (pins->nLoopStart > 0) pins->nLoopStart--; // Point to sample data pdata += 0x60; len -= 0x60; // Load sample data if ((pins->nLength > 3) && (len > 3)) { ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len); } else { pins->nLength = 0; } } break; #if 0 default: { CHAR s[8], s2[64]; *(DWORD *)s = pchunk->id; s[4] = 0; wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos); OutputDebugString(s2); } #endif } dwMemPos += pchunk->len; } // Step #1: convert song structure PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8); if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE; m_nChannels = pSong->channels; // Valid song header -> convert attached chunks { DWORD dwSongEnd = dwSongPos + 8 + read_DWORD(lpStream+dwSongPos+4); dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR) while (dwMemPos + 8 < dwSongEnd) { PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); swap_PSMCHUNK(pchunk); dwMemPos += 8; if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break; PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); ULONG len = pchunk->len; switch(pchunk->id) { case IFFID_OPLH: if (len >= 0x20) { UINT pos = len - 3; while (pos > 5) { BOOL bFound = FALSE; pos -= 5; DWORD dwName = read_DWORD(pdata+pos); for (UINT i=0; i 0) && (pdata[pos+1] <= 0x10) && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0)) { m_nDefaultSpeed = pdata[pos+1]; m_nDefaultTempo = pdata[pos+3]; break; } } UINT iOrd = 0; while ((pos+5len; } } // Step #2: convert patterns for (UINT nPat=0; nPatrows; if (len > pPsmPat->size) len = pPsmPat->size; if ((nRows < 64) || (nRows > 256)) nRows = 64; PatternSize[nPat] = nRows; if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break; MODCOMMAND *m = Patterns[nPat]; BYTE *p = pPsmPat->data; MODCOMMAND *sp, dummy; UINT pos = 0; UINT row = 0; UINT rowlim; #ifdef PSM_LOG Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream)); #endif UINT flags, ch; rowlim = bswapLE16(pPsmPat->reserved1)-2; while ((row < nRows) && (pos+3 < len)) { if ((pos+1) >= rowlim) { pos = rowlim; rowlim = (((int)p[pos+1])<<8) | ((int)p[pos+0]); m += m_nChannels; row++; rowlim += pos; pos += 2; } if (row >= nRows) continue; flags = p[pos++]; ch = p[pos++]; if (ch >= m_nChannels) { sp = &dummy; } else { sp = &m[ch]; } // Note + Instr if ((flags & 0x80) && (pos+1 < len)) { UINT note = p[pos++]; note = (note>>4)*12+(note&0x0f)+12+1; if (note > 0x80) note = 0; sp->note = note; } if ((flags & 0x40) && (pos+1 < len)) { UINT nins = p[pos++]; #ifdef PSM_LOG //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins); if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins); #endif sp->instr = samplemap[nins]; } // Volume if ((flags & 0x20) && (pos < len)) { sp->volcmd = VOLCMD_VOLUME; sp->vol = p[pos++] / 2; } // Effect if ((flags & 0x10) && (pos+1 < len)) { UINT command = p[pos++]; UINT param = p[pos++]; // Convert effects switch(command) { // 01: fine volslide up case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; if (param == 15) param=31; break; // 02: volslide up case 0x02: command = CMD_VOLUMESLIDE; param>>=1; param<<=4; break; // 03: fine volslide down case 0x03: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; if (param == 240) param=241; break; // 04: fine volslide down case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break; // 0C: portamento up case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break; // 0E: portamento down case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break; // 0F: tone portamento case 0x0F: command = CMD_TONEPORTAMENTO; param = param/4; break; // 15: vibrato case 0x15: command = CMD_VIBRATO; break; // 29: sample offset case 0x29: pos += 2; break; // 2A: retrigger note case 0x2A: command = CMD_RETRIG; break; // 33: Position Jump case 0x33: command = CMD_POSITIONJUMP; break; // 34: Pattern break case 0x34: command = CMD_PATTERNBREAK; break; // 3D: speed case 0x3D: command = CMD_SPEED; break; // 3E: tempo case 0x3E: command = CMD_TEMPO; break; // Unknown default: #ifdef PSM_LOG Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param); #endif command = param = 0; } sp->command = (BYTE)command; sp->param = (BYTE)param; } } #ifdef PSM_LOG if (pos < len) { Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos); } #endif } // Done (finally!) return TRUE; } ////////////////////////////////////////////////////////////// // // PSM Old Format // /* CONST c_PSM_MaxOrder = $FF; c_PSM_MaxSample = $FF; c_PSM_MaxChannel = $0F; TYPE PPSM_Header = ^TPSM_Header; TPSM_Header = RECORD PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 } PSM_SongName : ARRAY[01..58] OF CHAR; PSM_Byte00 : BYTE; PSM_Byte1A : BYTE; PSM_Unknown00 : BYTE; PSM_Unknown01 : BYTE; PSM_Unknown02 : BYTE; PSM_Speed : BYTE; PSM_Tempo : BYTE; PSM_Unknown03 : BYTE; PSM_Unknown04 : WORD; PSM_OrderLength : WORD; PSM_PatternNumber : WORD; PSM_SampleNumber : WORD; PSM_ChannelNumber : WORD; PSM_ChannelUsed : WORD; PSM_OrderPosition : LONGINT; PSM_ChannelSettingPosition : LONGINT; PSM_PatternPosition : LONGINT; PSM_SamplePosition : LONGINT; { *** perhaps there are some more infos in a larger header, but i have not decoded it and so it apears here NOT } END; PPSM_Sample = ^TPSM_Sample; TPSM_Sample = RECORD PSM_SampleFileName : ARRAY[01..12] OF CHAR; PSM_SampleByte00 : BYTE; PSM_SampleName : ARRAY[01..22] OF CHAR; PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE; PSM_SamplePosition : LONGINT; PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE; PSM_SampleNumber : BYTE; PSM_SampleFlags : WORD; PSM_SampleLength : LONGINT; PSM_SampleLoopBegin : LONGINT; PSM_SampleLoopEnd : LONGINT; PSM_Unknown03 : BYTE; PSM_SampleVolume : BYTE; PSM_SampleC5Speed : WORD; END; PPSM_SampleList = ^TPSM_SampleList; TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample; PPSM_Order = ^TPSM_Order; TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE; PPSM_ChannelSettings = ^TPSM_ChannelSettings; TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE; CONST PSM_NotesInPattern : BYTE = $00; PSM_ChannelInPattern : BYTE = $00; CONST c_PSM_SetSpeed = 60; FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT; BEGIN END; PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD); VAR Witz : ARRAY[00..04] OF WORD; I1,I2 : WORD; I3,I4 : WORD; TopicalByte : ^BYTE; Pattern : PUnpackedPattern; ChannelP : BYTE; NoteP : BYTE; InfoByte : BYTE; CodeByte : BYTE; InfoWord : WORD; Effect : BYTE; Opperand : BYTE; Panning : BYTE; Volume : BYTE; PrevInfo : BYTE; InfoIndex : BYTE; BEGIN Pattern := @Destination; TopicalByte := @Source; { *** Initialize patttern } FOR I2 := 0 TO c_Maximum_NoteIndex DO FOR I3 := 0 TO c_Maximum_ChannelIndex DO BEGIN Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF; Pattern^[I2,I3,c_Pattern_SampleIndex] := $00; Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF; Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF; Pattern^[I2,I3,c_Pattern_EffectIndex] := $00; Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00; END; { *** Byte-pointer on first pattern-entry } ChannelP := $00; NoteP := $00; InfoByte := $00; PrevInfo := $00; InfoIndex := $02; { *** read notes in pattern } PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); { *** unpack pattern } WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO BEGIN { *** Read info-byte } InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); IF InfoByte <> $00 THEN BEGIN ChannelP := InfoByte AND $0F; IF InfoByte AND 128 = 128 THEN { note and sample } BEGIN { *** read note } CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); DEC(CodeByte); CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2; Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte; { *** read sample } CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte; END; IF InfoByte AND 64 = 64 THEN { Volume } BEGIN CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte; END; IF InfoByte AND 32 = 32 THEN { effect AND opperand } BEGIN Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength); CASE Effect OF c_PSM_SetSpeed: BEGIN Effect := c_I_Set_Speed; END; ELSE BEGIN Effect := c_I_NoEffect; Opperand := $00; END; END; Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect; Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand; END; END ELSE INC(NoteP); END; END; PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD); { *** caution : Module has to be inited before!!!! } VAR Header : PPSM_Header; Sample : PPSM_SampleList; Order : PPSM_Order; ChannelSettings : PPSM_ChannelSettings; MultiPurposeBuffer : PByteArray; PatternBuffer : PUnpackedPattern; TopicalParaPointer : WORD; InFile : FILE; I1,I2 : WORD; I3,I4 : WORD; TempW : WORD; TempB : BYTE; TempP : PByteArray; TempI : INTEGER; { *** copy-vars for loop-extension } CopySource : LONGINT; CopyDestination : LONGINT; CopyLength : LONGINT; BEGIN { *** try to open file } ASSIGN(InFile,FileName); {$I-} RESET(InFile,1); {$I+} IF IORESULT <> $00 THEN BEGIN EXIT; END; {$I-} { *** seek start of module } IF FILESIZE(InFile) < FilePosition THEN BEGIN EXIT; END; SEEK(InFile,FilePosition); { *** look for enough memory for temporary variables } IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) + SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) + SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern) THEN BEGIN EXIT; END; { *** init dynamic variables } NEW(Header); NEW(Sample); NEW(Order); NEW(ChannelSettings); NEW(MultiPurposeBuffer); NEW(PatternBuffer); { *** read header } BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header)); { *** test if this is a DSM-file } IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN BEGIN ErrorCode := c_NoValidFileFormat; CLOSE(InFile); EXIT; END; { *** read order } SEEK(InFile,FilePosition + Header^.PSM_OrderPosition); BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength); { *** read channelsettings } SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition); BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings)); { *** read samplelist } SEEK(InFile,FilePosition + Header^.PSM_SamplePosition); BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample)); { *** copy header to intern NTMIK-structure } Module^.Module_Sign := 'MF'; Module^.Module_FileFormatVersion := $0100; Module^.Module_SampleNumber := Header^.PSM_SampleNumber; Module^.Module_PatternNumber := Header^.PSM_PatternNumber; Module^.Module_OrderLength := Header^.PSM_OrderLength; Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1; Module^.Module_Initial_GlobalVolume := 64; Module^.Module_Initial_MasterVolume := $C0; Module^.Module_Initial_Speed := Header^.PSM_Speed; Module^.Module_Initial_Tempo := Header^.PSM_Tempo; { *** paragraph 01 start } Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) + c_Module_Flags_Stereo * BYTE(1) + c_Module_Flags_ForceAmigaLimits * BYTE(0) + c_Module_Flags_Panning * BYTE(1) + c_Module_Flags_Surround * BYTE(1) + c_Module_Flags_QualityMixing * BYTE(1) + c_Module_Flags_FastVolumeSlides * BYTE(0) + c_Module_Flags_SpecialCustomData * BYTE(0) + c_Module_Flags_SongName * BYTE(1); I1 := $01; WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO BEGIN Module^.Module_Name[I1] := Header^.PSM_SongName[I1]; INC(I1); END; Module^.Module_Name[c_Module_SongNameLength] := #00; { *** Init channelsettings } FOR I1 := 0 TO c_Maximum_ChannelIndex DO BEGIN IF I1 < Header^.PSM_ChannelUsed THEN BEGIN { *** channel enabled } Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) + c_ChannelSettings_Code_ChannelEnabled * BYTE(1) + c_ChannelSettings_Code_ChannelDigital * BYTE(1); Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := c_ChannelSettings_Controls_EnhancedMode * BYTE(1) + c_ChannelSettings_Controls_SurroundMode * BYTE(0); END ELSE BEGIN { *** channel disabled } Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00; END; END; { *** init and copy order } FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF); MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength); { *** read pattern } SEEK(InFile,FilePosition + Header^.PSM_PatternPosition); NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1; FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO BEGIN NTMIK_LoadPatternProcedure; { *** read length } BLOCKREAD(InFile,TempW,2); { *** read pattern } BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2); { *** unpack pattern and set notes per channel to 64 } PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW); NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern); TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00]; GETMEM(Module^.Module_PatternPointer^[I1],TempW); MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW); { *** next pattern } END; { *** read samples } NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber; FOR I1 := 1 TO Header^.PSM_SampleNumber DO BEGIN NTMIK_LoadSampleProcedure; { *** get index for sample } I3 := Sample^[I1].PSM_SampleNumber; { *** clip PSM-sample } IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength; { *** init intern sample } NEW(Module^.Module_SamplePointer^[I3]); FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00); FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32); FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32); { *** copy informations to intern sample } I2 := $01; WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO BEGIN Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2]; INC(I2); END; Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF'; Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100; Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000; Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000; Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume; Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00; Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed; Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength; Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin; Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd; { *** now it's time for the flags } Module^.Module_SamplePointer^[I3]^.Sample_Flags := c_Sample_Flags_DigitalSample * BYTE(1) + c_Sample_Flags_8BitSample * BYTE(1) + c_Sample_Flags_UnsignedSampleData * BYTE(1) + c_Sample_Flags_Packed * BYTE(0) + c_Sample_Flags_LoopCounter * BYTE(0) + c_Sample_Flags_SampleName * BYTE(1) + c_Sample_Flags_LoopActive * BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15)); { *** alloc memory for sample-data } E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector, Module^.Module_SamplePointer^[I3]^.Sample_Position, Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize); { *** read out data } EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector; EPT(TempP).p_Offset := $0000; SEEK(InFile,Sample^[I1].PSM_SamplePosition); E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length); { *** 'coz the samples are signed in a DSM-file -> PC-fy them } IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN BEGIN CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length; { *** decode sample } ASM DB 066h; MOV CX,WORD PTR CopyLength { *** load sample selector } MOV ES,WORD PTR TempP[00002h] DB 066h; XOR SI,SI DB 066h; XOR DI,DI XOR AH,AH { *** conert all bytes } @@MainLoop: DB 026h; DB 067h; LODSB ADD AL,AH MOV AH,AL DB 067h; STOSB DB 066h; LOOP @@MainLoop END; { *** make samples unsigned } ASM DB 066h; MOV CX,WORD PTR CopyLength { *** load sample selector } MOV ES,WORD PTR TempP[00002h] DB 066h; XOR SI,SI DB 066h; XOR DI,DI { *** conert all bytes } @@MainLoop: DB 026h; DB 067h; LODSB SUB AL,080h DB 067h; STOSB DB 066h; LOOP @@MainLoop END; { *** Create Loop-Extension } IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN BEGIN CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin; CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd; CopyLength := CopyDestination - CopySource; ASM { *** load sample-selector } MOV ES,WORD PTR TempP[00002h] DB 066h; MOV DI,WORD PTR CopyDestination { *** calculate number of full sample-loops to copy } XOR DX,DX MOV AX,c_LoopExtensionSize MOV BX,WORD PTR CopyLength DIV BX OR AX,AX JE @@NoFullLoop { *** copy some full-loops (size=bx) } MOV CX,AX @@InnerLoop: PUSH CX DB 066h; MOV SI,WORD PTR CopySource MOV CX,BX DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } POP CX LOOP @@InnerLoop @@NoFullLoop: { *** calculate number of rest-bytes to copy } DB 066h; MOV SI,WORD PTR CopySource MOV CX,DX DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } END; END ELSE BEGIN CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length; ASM { *** load sample-selector } MOV ES,WORD PTR TempP[00002h] DB 066h; MOV DI,WORD PTR CopyDestination { *** clear extension } MOV CX,c_LoopExtensionSize MOV AL,080h DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] } END; END; END; { *** next sample } END; { *** init period-ranges } NTMIK_MaximumPeriod := $0000D600 SHR 1; NTMIK_MinimumPeriod := $0000D600 SHR 8; { *** close file } CLOSE(InFile); { *** dispose all dynamic variables } DISPOSE(Header); DISPOSE(Sample); DISPOSE(Order); DISPOSE(ChannelSettings); DISPOSE(MultiPurposeBuffer); DISPOSE(PatternBuffer); { *** set errorcode to noerror } ErrorCode := c_NoError; END; */ openjazz-20240919/ext/psmplug/psmplug.cpp000066400000000000000000000142711467303304200203170ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Kenton Varda (C interface wrapper) */ #include "stdafx.h" #include "psmplug.h" #include "sndfile.h" struct _ModPlugFile { CSoundFile mSoundFile; }; namespace ModPlug { ModPlug_Settings gSettings = { MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION, 2, // mChannels 16, // mBits 44100, // mFrequency MODPLUG_RESAMPLE_LINEAR, //mResamplingMode 128, // mStereoSeparation 32, // mMaxMixChannels 0, 0, 0, 0, 0, 0, 0 }; int gSampleSize; void UpdateSettings(bool updateBasicConfig) { if(gSettings.mFlags & MODPLUG_ENABLE_REVERB) { CSoundFile::SetReverbParameters(gSettings.mReverbDepth, gSettings.mReverbDelay); } if(gSettings.mFlags & MODPLUG_ENABLE_MEGABASS) { CSoundFile::SetXBassParameters(gSettings.mBassAmount, gSettings.mBassRange); } else // modplug seems to ignore the SetWaveConfigEx() setting for bass boost CSoundFile::SetXBassParameters(0, 0); if(gSettings.mFlags & MODPLUG_ENABLE_SURROUND) { CSoundFile::SetSurroundParameters(gSettings.mSurroundDepth, gSettings.mSurroundDelay); } if(updateBasicConfig) { CSoundFile::SetWaveConfig(gSettings.mFrequency, gSettings.mBits, gSettings.mChannels); CSoundFile::SetMixConfig(gSettings.mStereoSeparation, gSettings.mMaxMixChannels); gSampleSize = gSettings.mBits / 8 * gSettings.mChannels; } CSoundFile::SetWaveConfigEx(gSettings.mFlags & MODPLUG_ENABLE_SURROUND, !(gSettings.mFlags & MODPLUG_ENABLE_OVERSAMPLING), gSettings.mFlags & MODPLUG_ENABLE_REVERB, true, gSettings.mFlags & MODPLUG_ENABLE_MEGABASS, gSettings.mFlags & MODPLUG_ENABLE_NOISE_REDUCTION, false); CSoundFile::SetResamplingMode(gSettings.mResamplingMode); } } ModPlugFile* ModPlug_Load(const void* data, int size) { ModPlugFile* result = new ModPlugFile; ModPlug::UpdateSettings(true); if(result->mSoundFile.Create((const BYTE*)data, size)) { result->mSoundFile.SetRepeatCount(ModPlug::gSettings.mLoopCount); return result; } else { delete result; return NULL; } } void ModPlug_Unload(ModPlugFile* file) { file->mSoundFile.Destroy(); delete file; } int ModPlug_Read(ModPlugFile* file, void* buffer, int size) { return file->mSoundFile.Read(buffer, size) * ModPlug::gSampleSize; } const char* ModPlug_GetName(ModPlugFile* file) { return file->mSoundFile.GetTitle(); } int ModPlug_GetLength(ModPlugFile* file) { return file->mSoundFile.GetSongTime() * 1000; } void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) { file->mSoundFile.gpSndMixHook = (LPSNDMIXHOOKPROC)proc ; return; } void ModPlug_UnloadMixerCallback(ModPlugFile* file) { file->mSoundFile.gpSndMixHook = NULL; return ; } unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) { return (unsigned int)file->mSoundFile.m_nMasterVolume; } void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) { (void)file->mSoundFile.SetMasterVolume( (UINT)cvol, FALSE ); return ; } unsigned int ModPlug_GetMusicTempoFactor(ModPlugFile* file) { return file->mSoundFile.m_nTempoFactor; } void ModPlug_SetMusicTempoFactor(ModPlugFile* file, unsigned int ctemp) { (void)file->mSoundFile.SetTempoFactor((UINT)ctemp); return; } int ModPlug_GetCurrentSpeed(ModPlugFile* file) { return file->mSoundFile.m_nMusicSpeed; } int ModPlug_GetCurrentTempo(ModPlugFile* file) { return file->mSoundFile.m_nMusicTempo; } int ModPlug_GetCurrentOrder(ModPlugFile* file) { return file->mSoundFile.GetCurrentOrder(); } int ModPlug_GetCurrentPattern(ModPlugFile* file) { return file->mSoundFile.GetCurrentPattern(); } int ModPlug_GetCurrentRow(ModPlugFile* file) { return file->mSoundFile.m_nRow; } int ModPlug_GetPlayingChannels(ModPlugFile* file) { return ( file->mSoundFile.m_nMixChannels < file->mSoundFile.m_nMaxMixChannels ? file->mSoundFile.m_nMixChannels : file->mSoundFile.m_nMaxMixChannels ); } void ModPlug_SeekOrder(ModPlugFile* file,int order) { file->mSoundFile.SetCurrentOrder(order); } int ModPlug_GetModuleType(ModPlugFile* file) { return file->mSoundFile.m_nType; } char* ModPlug_GetMessage(ModPlugFile* file) { return file->mSoundFile.m_lpszSongComments; } unsigned int ModPlug_NumInstruments(ModPlugFile* file) { return file->mSoundFile.m_nInstruments; } unsigned int ModPlug_NumSamples(ModPlugFile* file) { return file->mSoundFile.m_nSamples; } unsigned int ModPlug_NumPatterns(ModPlugFile* file) { return file->mSoundFile.GetNumPatterns(); } unsigned int ModPlug_NumChannels(ModPlugFile* file) { return file->mSoundFile.GetNumChannels(); } unsigned int ModPlug_SampleName(ModPlugFile* file,unsigned int qual,char* buff) { return file->mSoundFile.GetSampleName(qual,buff); } unsigned int ModPlug_InstrumentName(ModPlugFile* file,unsigned int qual,char* buff) { return file->mSoundFile.GetInstrumentName(qual,buff); } ModPlugNote* ModPlug_GetPattern(ModPlugFile* file,int pattern,unsigned int* numrows) { if ( pattern= 0) { if (file->mSoundFile.Patterns[pattern]) { if (numrows) *numrows=(unsigned int)file->mSoundFile.PatternSize[pattern]; return (ModPlugNote*)file->mSoundFile.Patterns[pattern]; } } return NULL; } void ModPlug_Seek(ModPlugFile* file, int millisecond) { int maxpos; int maxtime = file->mSoundFile.GetSongTime() * 1000; float postime; if(millisecond > maxtime) millisecond = maxtime; maxpos = file->mSoundFile.GetMaxPosition(); postime = 0.0f; if (maxtime != 0) postime = (float)maxpos / (float)maxtime; file->mSoundFile.SetCurrentPos((int)(millisecond * postime)); } void ModPlug_GetSettings(ModPlug_Settings* settings) { memcpy(settings, &ModPlug::gSettings, sizeof(ModPlug_Settings)); } void ModPlug_SetSettings(const ModPlug_Settings* settings) { memcpy(&ModPlug::gSettings, settings, sizeof(ModPlug_Settings)); ModPlug::UpdateSettings(false); // do not update basic config. } openjazz-20240919/ext/psmplug/psmplug.h000066400000000000000000000142361467303304200177650ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Kenton Varda (C interface wrapper) */ #ifndef PSMPLUG_H #define PSMPLUG_H #ifdef __cplusplus extern "C" { #endif struct _ModPlugFile; typedef struct _ModPlugFile ModPlugFile; struct _ModPlugNote { unsigned char Note; unsigned char Instrument; unsigned char VolumeEffect; unsigned char Effect; unsigned char Volume; unsigned char Parameter; }; typedef struct _ModPlugNote ModPlugNote; typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); /* Load a mod file. [data] should point to a block of memory containing the complete * file, and [size] should be the size of that block. * Return the loaded mod file on success, or NULL on failure. */ ModPlugFile* ModPlug_Load(const void* data, int size); /* Unload a mod file. */ void ModPlug_Unload(ModPlugFile* file); /* Read sample data into the buffer. Returns the number of bytes read. If the end * of the mod has been reached, zero is returned. */ int ModPlug_Read(ModPlugFile* file, void* buffer, int size); /* Get the name of the mod. The returned buffer is stored within the ModPlugFile * structure and will remain valid until you unload the file. */ const char* ModPlug_GetName(ModPlugFile* file); /* Get the length of the mod, in milliseconds. Note that this result is not always * accurate, especially in the case of mods with loops. */ int ModPlug_GetLength(ModPlugFile* file); /* Seek to a particular position in the song. Note that seeking and MODs don't mix very * well. Some mods will be missing instruments for a short time after a seek, as ModPlug * does not scan the sequence backwards to find out which instruments were supposed to be * playing at that time. (Doing so would be difficult and not very reliable.) Also, * note that seeking is not very exact in some mods -- especially those for which * ModPlug_GetLength() does not report the full length. */ void ModPlug_Seek(ModPlugFile* file, int millisecond); enum _ModPlug_Flags { MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ }; enum _ModPlug_ResamplingMode { MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ }; typedef struct _ModPlug_Settings { int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then * down-mixes to the settings you choose. */ int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ int mBits; /* Bits per sample - 8, 16, or 32 */ int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ int mStereoSeparation; /* Stereo separation, 1 - 256 */ int mMaxMixChannels; /* Maximum number of mixing channels (polyphony), 32 - 256 */ int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ int mBassAmount; /* XBass level 0(quiet)-100(loud) */ int mBassRange; /* XBass cutoff in Hz 10-100 */ int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ int mLoopCount; /* Number of times to loop. Zero prevents looping. * -1 loops forever. */ } ModPlug_Settings; /* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, * sampling rate, and loop count, will take effect immediately. Those options which don't * take effect immediately will take effect the next time you load a mod. */ void ModPlug_GetSettings(ModPlug_Settings* settings); void ModPlug_SetSettings(const ModPlug_Settings* settings); /* New ModPlug API Functions */ /* NOTE: Master Volume (1-512) */ unsigned int ModPlug_GetMasterVolume(ModPlugFile* file); void ModPlug_SetMasterVolume(ModPlugFile* file, unsigned int cvol); /* PSMPlug additions */ /* NOTE: Tempo Factor (1-256) */ unsigned int ModPlug_GetMusicTempoFactor(ModPlugFile* file); void ModPlug_SetMusicTempoFactor(ModPlugFile* file, unsigned int ctemp); int ModPlug_GetCurrentSpeed(ModPlugFile* file); int ModPlug_GetCurrentTempo(ModPlugFile* file); int ModPlug_GetCurrentOrder(ModPlugFile* file); int ModPlug_GetCurrentPattern(ModPlugFile* file); int ModPlug_GetCurrentRow(ModPlugFile* file); int ModPlug_GetPlayingChannels(ModPlugFile* file); void ModPlug_SeekOrder(ModPlugFile* file,int order); int ModPlug_GetModuleType(ModPlugFile* file); char* ModPlug_GetMessage(ModPlugFile* file); unsigned int ModPlug_NumInstruments(ModPlugFile* file); unsigned int ModPlug_NumSamples(ModPlugFile* file); unsigned int ModPlug_NumPatterns(ModPlugFile* file); unsigned int ModPlug_NumChannels(ModPlugFile* file); unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); /* * Retrieve pattern note-data */ ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); /* * ================= * Mixer callback * ================= * * Use this callback if you want to 'modify' the mixed data of LibModPlug. * * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; * * 'buffer': A buffer of mixed samples * 'channels': N. of channels in the buffer * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) * * (Samples are signed 32-bit integers) */ void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; #ifdef __cplusplus } /* extern "C" */ #endif #endif openjazz-20240919/ext/psmplug/snd_dsp.cpp000066400000000000000000000344071467303304200202650ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque */ #include "stdafx.h" #include "sndfile.h" // Delayed Surround Filters #define nDolbyHiFltAttn 6 #define nDolbyHiFltMask 3 #define DOLBYATTNROUNDUP 31 // Bass Expansion #define XBASS_DELAY 14 // 2.5 ms // Buffer Sizes #define XBASSBUFFERSIZE 64 // 2 ms at 50KHz #define FILTERBUFFERSIZE 64 // 1.25 ms #define SURROUNDBUFFERSIZE ((MAX_SAMPLE_RATE * 50) / 1000) #define REVERBBUFFERSIZE ((MAX_SAMPLE_RATE * 200) / 1000) #define REVERBBUFFERSIZE2 ((REVERBBUFFERSIZE*13) / 17) #define REVERBBUFFERSIZE3 ((REVERBBUFFERSIZE*7) / 13) #define REVERBBUFFERSIZE4 ((REVERBBUFFERSIZE*7) / 19) // DSP Effects: PUBLIC members UINT CSoundFile::m_nXBassDepth = 6; UINT CSoundFile::m_nXBassRange = XBASS_DELAY; UINT CSoundFile::m_nReverbDepth = 1; UINT CSoundFile::m_nReverbDelay = 100; UINT CSoundFile::m_nProLogicDepth = 12; UINT CSoundFile::m_nProLogicDelay = 20; //////////////////////////////////////////////////////////////////// // DSP Effects internal state // Bass Expansion: low-pass filter static LONG nXBassSum = 0; static LONG nXBassBufferPos = 0; static LONG nXBassDlyPos = 0; static LONG nXBassMask = 0; // Noise Reduction: simple low-pass filter static LONG nLeftNR = 0; static LONG nRightNR = 0; // Surround Encoding: 1 delay line + low-pass filter + high-pass filter static LONG nSurroundSize = 0; static LONG nSurroundPos = 0; static LONG nDolbyDepth = 0; static LONG nDolbyLoDlyPos = 0; static LONG nDolbyLoFltPos = 0; static LONG nDolbyLoFltSum = 0; static LONG nDolbyHiFltPos = 0; static LONG nDolbyHiFltSum = 0; // Reverb: 4 delay lines + high-pass filter + low-pass filter #ifndef MODPLUG_NO_REVERB static LONG nReverbSize = 0; static LONG nReverbBufferPos = 0; static LONG nReverbSize2 = 0; static LONG nReverbBufferPos2 = 0; static LONG nReverbSize3 = 0; static LONG nReverbBufferPos3 = 0; static LONG nReverbSize4 = 0; static LONG nReverbBufferPos4 = 0; static LONG nReverbLoFltSum = 0; static LONG nReverbLoFltPos = 0; static LONG nReverbLoDlyPos = 0; static LONG nFilterAttn = 0; static LONG gRvbLowPass[8]; static LONG gRvbLPPos = 0; static LONG gRvbLPSum = 0; static LONG ReverbLoFilterBuffer[XBASSBUFFERSIZE]; static LONG ReverbLoFilterDelay[XBASSBUFFERSIZE]; static LONG ReverbBuffer[REVERBBUFFERSIZE]; static LONG ReverbBuffer2[REVERBBUFFERSIZE2]; static LONG ReverbBuffer3[REVERBBUFFERSIZE3]; static LONG ReverbBuffer4[REVERBBUFFERSIZE4]; #endif static LONG XBassBuffer[XBASSBUFFERSIZE]; static LONG XBassDelay[XBASSBUFFERSIZE]; static LONG DolbyLoFilterBuffer[XBASSBUFFERSIZE]; static LONG DolbyLoFilterDelay[XBASSBUFFERSIZE]; static LONG DolbyHiFilterBuffer[FILTERBUFFERSIZE]; static LONG SurroundBuffer[SURROUNDBUFFERSIZE]; // Access the main temporary mix buffer directly: avoids an extra pointer extern int MixSoundBuffer[MIXBUFFERSIZE*4]; extern int MixReverbBuffer[MIXBUFFERSIZE*2]; static UINT GetMaskFromSize(UINT len) //----------------------------------- { UINT n = 2; while (n <= len) n <<= 1; return ((n >> 1) - 1); } void CSoundFile::InitializeDSP(BOOL bReset) //----------------------------------------- { if (!m_nReverbDelay) m_nReverbDelay = 100; if (!m_nXBassRange) m_nXBassRange = XBASS_DELAY; if (!m_nProLogicDelay) m_nProLogicDelay = 20; if (m_nXBassDepth > 8) m_nXBassDepth = 8; if (m_nXBassDepth < 2) m_nXBassDepth = 2; if (bReset) { // Noise Reduction nLeftNR = nRightNR = 0; } // Pro-Logic Surround nSurroundPos = nSurroundSize = 0; nDolbyLoFltPos = nDolbyLoFltSum = nDolbyLoDlyPos = 0; nDolbyHiFltPos = nDolbyHiFltSum = 0; if (gdwSoundSetup & SNDMIX_SURROUND) { memset(DolbyLoFilterBuffer, 0, sizeof(DolbyLoFilterBuffer)); memset(DolbyHiFilterBuffer, 0, sizeof(DolbyHiFilterBuffer)); memset(DolbyLoFilterDelay, 0, sizeof(DolbyLoFilterDelay)); memset(SurroundBuffer, 0, sizeof(SurroundBuffer)); nSurroundSize = (gdwMixingFreq * m_nProLogicDelay) / 1000; if (nSurroundSize > SURROUNDBUFFERSIZE) nSurroundSize = SURROUNDBUFFERSIZE; if (m_nProLogicDepth < 8) nDolbyDepth = (32 >> m_nProLogicDepth) + 32; else nDolbyDepth = (m_nProLogicDepth < 16) ? (8 + (m_nProLogicDepth - 8) * 7) : 64; nDolbyDepth >>= 2; } // Reverb Setup #ifndef MODPLUG_NO_REVERB if (gdwSoundSetup & SNDMIX_REVERB) { UINT nrs = (gdwMixingFreq * m_nReverbDelay) / 1000; UINT nfa = m_nReverbDepth+1; if (nrs > REVERBBUFFERSIZE) nrs = REVERBBUFFERSIZE; if ((bReset) || (nrs != (UINT)nReverbSize) || (nfa != (UINT)nFilterAttn)) { nFilterAttn = nfa; nReverbSize = nrs; nReverbBufferPos = nReverbBufferPos2 = nReverbBufferPos3 = nReverbBufferPos4 = 0; nReverbLoFltSum = nReverbLoFltPos = nReverbLoDlyPos = 0; gRvbLPSum = gRvbLPPos = 0; nReverbSize2 = (nReverbSize * 13) / 17; if (nReverbSize2 > REVERBBUFFERSIZE2) nReverbSize2 = REVERBBUFFERSIZE2; nReverbSize3 = (nReverbSize * 7) / 13; if (nReverbSize3 > REVERBBUFFERSIZE3) nReverbSize3 = REVERBBUFFERSIZE3; nReverbSize4 = (nReverbSize * 7) / 19; if (nReverbSize4 > REVERBBUFFERSIZE4) nReverbSize4 = REVERBBUFFERSIZE4; memset(ReverbLoFilterBuffer, 0, sizeof(ReverbLoFilterBuffer)); memset(ReverbLoFilterDelay, 0, sizeof(ReverbLoFilterDelay)); memset(ReverbBuffer, 0, sizeof(ReverbBuffer)); memset(ReverbBuffer2, 0, sizeof(ReverbBuffer2)); memset(ReverbBuffer3, 0, sizeof(ReverbBuffer3)); memset(ReverbBuffer4, 0, sizeof(ReverbBuffer4)); memset(gRvbLowPass, 0, sizeof(gRvbLowPass)); } } else nReverbSize = 0; #endif BOOL bResetBass = FALSE; // Bass Expansion Reset if (gdwSoundSetup & SNDMIX_MEGABASS) { UINT nXBassSamples = (gdwMixingFreq * m_nXBassRange) / 10000; if (nXBassSamples > XBASSBUFFERSIZE) nXBassSamples = XBASSBUFFERSIZE; UINT mask = GetMaskFromSize(nXBassSamples); if ((bReset) || (mask != (UINT)nXBassMask)) { nXBassMask = mask; bResetBass = TRUE; } } else { nXBassMask = 0; bResetBass = TRUE; } if (bResetBass) { nXBassSum = nXBassBufferPos = nXBassDlyPos = 0; memset(XBassBuffer, 0, sizeof(XBassBuffer)); memset(XBassDelay, 0, sizeof(XBassDelay)); } } void CSoundFile::ProcessStereoDSP(int count) //------------------------------------------ { #ifndef MODPLUG_NO_REVERB // Reverb if (gdwSoundSetup & SNDMIX_REVERB) { int *pr = MixSoundBuffer, *pin = MixReverbBuffer, rvbcount = count; do { int echo = ReverbBuffer[nReverbBufferPos] + ReverbBuffer2[nReverbBufferPos2] + ReverbBuffer3[nReverbBufferPos3] + ReverbBuffer4[nReverbBufferPos4]; // echo = reverb signal // Delay line and remove Low Frequencies // v = original signal int echodly = ReverbLoFilterDelay[nReverbLoDlyPos]; // echodly = delayed signal ReverbLoFilterDelay[nReverbLoDlyPos] = echo >> 1; nReverbLoDlyPos++; nReverbLoDlyPos &= 0x1F; int n = nReverbLoFltPos; nReverbLoFltSum -= ReverbLoFilterBuffer[n]; int tmp = echo / 128; ReverbLoFilterBuffer[n] = tmp; nReverbLoFltSum += tmp; echodly -= nReverbLoFltSum; nReverbLoFltPos = (n + 1) & 0x3F; // Reverb int v = (pin[0]+pin[1]) >> nFilterAttn; pr[0] += pin[0] + echodly; pr[1] += pin[1] + echodly; v += echodly >> 2; ReverbBuffer3[nReverbBufferPos3] = v; ReverbBuffer4[nReverbBufferPos4] = v; v += echodly >> 4; v >>= 1; gRvbLPSum -= gRvbLowPass[gRvbLPPos]; gRvbLPSum += v; gRvbLowPass[gRvbLPPos] = v; gRvbLPPos++; gRvbLPPos &= 7; int vlp = gRvbLPSum >> 2; ReverbBuffer[nReverbBufferPos] = vlp; ReverbBuffer2[nReverbBufferPos2] = vlp; if (++nReverbBufferPos >= nReverbSize) nReverbBufferPos = 0; if (++nReverbBufferPos2 >= nReverbSize2) nReverbBufferPos2 = 0; if (++nReverbBufferPos3 >= nReverbSize3) nReverbBufferPos3 = 0; if (++nReverbBufferPos4 >= nReverbSize4) nReverbBufferPos4 = 0; pr += 2; pin += 2; } while (--rvbcount); } #endif // Dolby Pro-Logic Surround if (gdwSoundSetup & SNDMIX_SURROUND) { int *pr = MixSoundBuffer, n = nDolbyLoFltPos; for (int r=count; r; r--) { int v = (pr[0]+pr[1]+DOLBYATTNROUNDUP) >> (nDolbyHiFltAttn+1); v *= (int)nDolbyDepth; // Low-Pass Filter nDolbyHiFltSum -= DolbyHiFilterBuffer[nDolbyHiFltPos]; DolbyHiFilterBuffer[nDolbyHiFltPos] = v; nDolbyHiFltSum += v; v = nDolbyHiFltSum; nDolbyHiFltPos++; nDolbyHiFltPos &= nDolbyHiFltMask; // Surround int secho = SurroundBuffer[nSurroundPos]; SurroundBuffer[nSurroundPos] = v; // Delay line and remove low frequencies v = DolbyLoFilterDelay[nDolbyLoDlyPos]; // v = delayed signal DolbyLoFilterDelay[nDolbyLoDlyPos] = secho; // secho = signal nDolbyLoDlyPos++; nDolbyLoDlyPos &= 0x1F; nDolbyLoFltSum -= DolbyLoFilterBuffer[n]; int tmp = secho / 64; DolbyLoFilterBuffer[n] = tmp; nDolbyLoFltSum += tmp; v -= nDolbyLoFltSum; n++; n &= 0x3F; // Add echo pr[0] += v; pr[1] -= v; if (++nSurroundPos >= nSurroundSize) nSurroundPos = 0; pr += 2; } nDolbyLoFltPos = n; } // Bass Expansion if (gdwSoundSetup & SNDMIX_MEGABASS) { int *px = MixSoundBuffer; int xba = m_nXBassDepth+1, xbamask = (1 << xba) - 1; int n = nXBassBufferPos; for (int x=count; x; x--) { nXBassSum -= XBassBuffer[n]; int tmp0 = px[0] + px[1]; int tmp = (tmp0 + ((tmp0 >> 31) & xbamask)) >> xba; XBassBuffer[n] = tmp; nXBassSum += tmp; int v = XBassDelay[nXBassDlyPos]; XBassDelay[nXBassDlyPos] = px[0]; px[0] = v + nXBassSum; v = XBassDelay[nXBassDlyPos+1]; XBassDelay[nXBassDlyPos+1] = px[1]; px[1] = v + nXBassSum; nXBassDlyPos = (nXBassDlyPos + 2) & nXBassMask; px += 2; n++; n &= nXBassMask; } nXBassBufferPos = n; } // Noise Reduction if (gdwSoundSetup & SNDMIX_NOISEREDUCTION) { int n1 = nLeftNR, n2 = nRightNR; int *pnr = MixSoundBuffer; for (int nr=count; nr; nr--) { int vnr = pnr[0] >> 1; pnr[0] = vnr + n1; n1 = vnr; vnr = pnr[1] >> 1; pnr[1] = vnr + n2; n2 = vnr; pnr += 2; } nLeftNR = n1; nRightNR = n2; } } void CSoundFile::ProcessMonoDSP(int count) //---------------------------------------- { #ifndef MODPLUG_NO_REVERB // Reverb if (gdwSoundSetup & SNDMIX_REVERB) { int *pr = MixSoundBuffer, rvbcount = count, *pin = MixReverbBuffer; do { int echo = ReverbBuffer[nReverbBufferPos] + ReverbBuffer2[nReverbBufferPos2] + ReverbBuffer3[nReverbBufferPos3] + ReverbBuffer4[nReverbBufferPos4]; // echo = reverb signal // Delay line and remove Low Frequencies // v = original signal int echodly = ReverbLoFilterDelay[nReverbLoDlyPos]; // echodly = delayed signal ReverbLoFilterDelay[nReverbLoDlyPos] = echo >> 1; nReverbLoDlyPos++; nReverbLoDlyPos &= 0x1F; int n = nReverbLoFltPos; nReverbLoFltSum -= ReverbLoFilterBuffer[n]; int tmp = echo / 128; ReverbLoFilterBuffer[n] = tmp; nReverbLoFltSum += tmp; echodly -= nReverbLoFltSum; nReverbLoFltPos = (n + 1) & 0x3F; // Reverb int v = pin[0] >> (nFilterAttn-1); *pr++ += pin[0] + echodly; pin++; v += echodly >> 2; ReverbBuffer3[nReverbBufferPos3] = v; ReverbBuffer4[nReverbBufferPos4] = v; v += echodly >> 4; v >>= 1; gRvbLPSum -= gRvbLowPass[gRvbLPPos]; gRvbLPSum += v; gRvbLowPass[gRvbLPPos] = v; gRvbLPPos++; gRvbLPPos &= 7; int vlp = gRvbLPSum >> 2; ReverbBuffer[nReverbBufferPos] = vlp; ReverbBuffer2[nReverbBufferPos2] = vlp; if (++nReverbBufferPos >= nReverbSize) nReverbBufferPos = 0; if (++nReverbBufferPos2 >= nReverbSize2) nReverbBufferPos2 = 0; if (++nReverbBufferPos3 >= nReverbSize3) nReverbBufferPos3 = 0; if (++nReverbBufferPos4 >= nReverbSize4) nReverbBufferPos4 = 0; } while (--rvbcount); } #endif // Bass Expansion if (gdwSoundSetup & SNDMIX_MEGABASS) { int *px = MixSoundBuffer; int xba = m_nXBassDepth, xbamask = (1 << xba)-1; int n = nXBassBufferPos; for (int x=count; x; x--) { nXBassSum -= XBassBuffer[n]; int tmp0 = *px; int tmp = (tmp0 + ((tmp0 >> 31) & xbamask)) >> xba; XBassBuffer[n] = tmp; nXBassSum += tmp; int v = XBassDelay[nXBassDlyPos]; XBassDelay[nXBassDlyPos] = *px; *px++ = v + nXBassSum; nXBassDlyPos = (nXBassDlyPos + 2) & nXBassMask; n++; n &= nXBassMask; } nXBassBufferPos = n; } // Noise Reduction if (gdwSoundSetup & SNDMIX_NOISEREDUCTION) { int n = nLeftNR; int *pnr = MixSoundBuffer; for (int nr=count; nr; pnr++, nr--) { int vnr = *pnr >> 1; *pnr = vnr + n; n = vnr; } nLeftNR = n; } } ///////////////////////////////////////////////////////////////// // Clean DSP Effects interface // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] BOOL CSoundFile::SetReverbParameters(UINT nDepth, UINT nDelay) //------------------------------------------------------------ { if (nDepth > 100) nDepth = 100; UINT gain = nDepth / 20; if (gain > 4) gain = 4; m_nReverbDepth = 4 - gain; if (nDelay < 40) nDelay = 40; if (nDelay > 250) nDelay = 250; m_nReverbDelay = nDelay; return TRUE; } // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 20-100] BOOL CSoundFile::SetXBassParameters(UINT nDepth, UINT nRange) //----------------------------------------------------------- { if (nDepth > 100) nDepth = 100; UINT gain = nDepth / 20; if (gain > 4) gain = 4; m_nXBassDepth = 8 - gain; // filter attenuation 1/256 .. 1/16 UINT range = nRange / 5; if (range > 5) range -= 5; else range = 0; if (nRange > 16) nRange = 16; m_nXBassRange = 21 - range; // filter average on 0.5-1.6ms return TRUE; } // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-50ms] BOOL CSoundFile::SetSurroundParameters(UINT nDepth, UINT nDelay) //-------------------------------------------------------------- { UINT gain = (nDepth * 16) / 100; if (gain > 16) gain = 16; if (gain < 1) gain = 1; m_nProLogicDepth = gain; if (nDelay < 4) nDelay = 4; if (nDelay > 50) nDelay = 50; m_nProLogicDelay = nDelay; return TRUE; } BOOL CSoundFile::SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ) //---------------------------------------------------------------------------------------------------------------------------- { DWORD d = gdwSoundSetup & ~(SNDMIX_SURROUND | SNDMIX_NORESAMPLING | SNDMIX_REVERB | SNDMIX_HQRESAMPLER | SNDMIX_MEGABASS | SNDMIX_NOISEREDUCTION | SNDMIX_EQ); if (bSurround) d |= SNDMIX_SURROUND; if (bNoOverSampling) d |= SNDMIX_NORESAMPLING; if (bReverb) d |= SNDMIX_REVERB; if (hqido) d |= SNDMIX_HQRESAMPLER; if (bMegaBass) d |= SNDMIX_MEGABASS; if (bNR) d |= SNDMIX_NOISEREDUCTION; if (bEQ) d |= SNDMIX_EQ; gdwSoundSetup = d; InitPlayer(FALSE); return TRUE; } openjazz-20240919/ext/psmplug/snd_flt.cpp000066400000000000000000000034221467303304200202550ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque */ #include "stdafx.h" #include "sndfile.h" // AWE32: cutoff = reg[0-255] * 31.25 + 100 -> [100Hz-8060Hz] // EMU10K1 docs: cutoff = reg[0-127]*62+100 #define FILTER_PRECISION 8192 #ifndef NO_FILTER #include DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const //----------------------------------------------------------------------- { float Fc; if (m_dwSongFlags & SONG_EXFILTERRANGE) Fc = 110.0f * pow(2.0f, 0.25f + ((float)(nCutOff*(flt_modifier+256)))/(21.0f*512.0f)); else Fc = 110.0f * pow(2.0f, 0.25f + ((float)(nCutOff*(flt_modifier+256)))/(24.0f*512.0f)); LONG freq = (LONG)Fc; if (freq < 120) return 120; if (freq > 10000) return 10000; if (freq*2 > (LONG)gdwMixingFreq) freq = gdwMixingFreq>>1; return (DWORD)freq; } // Simple 2-poles resonant filter void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const //---------------------------------------------------------------------------------------- { float fc = (float)CutOffToFrequency(pChn->nCutOff, flt_modifier); float fs = (float)gdwMixingFreq; float fg, fb0, fb1; fc *= (float)(2.0*3.14159265358/fs); float dmpfac = pow(10.0f, -((24.0f / 128.0f)*(float)pChn->nResonance) / 20.0f); float d = (1.0f-2.0f*dmpfac)* fc; if (d>2.0) d = 2.0; d = (2.0f*dmpfac - d)/fc; float e = pow(1.0f/fc,2.0f); fg=1/(1+d+e); fb0=(d+e+e)/(1+d+e); fb1=-e/(1+d+e); pChn->nFilter_A0 = (int)(fg * FILTER_PRECISION); pChn->nFilter_B0 = (int)(fb0 * FILTER_PRECISION); pChn->nFilter_B1 = (int)(fb1 * FILTER_PRECISION); if (bReset) { pChn->nFilter_Y1 = pChn->nFilter_Y2 = 0; pChn->nFilter_Y3 = pChn->nFilter_Y4 = 0; } pChn->dwFlags |= CHN_FILTER; } #endif // NO_FILTER openjazz-20240919/ext/psmplug/snd_fx.cpp000066400000000000000000001771211467303304200201150ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque */ #include "stdafx.h" #include #include "sndfile.h" #include "tables.h" #ifdef MSC_VER #pragma warning(disable:4244) #endif //////////////////////////////////////////////////////////// // Length DWORD CSoundFile::GetLength(BOOL bAdjust, BOOL bTotal) //---------------------------------------------------- { UINT dwElapsedTime=0, nRow=0, nCurrentPattern=0, nNextPattern=0, nPattern=0; UINT nMusicSpeed=m_nDefaultSpeed, nMusicTempo=m_nDefaultTempo, nNextRow=0; UINT nMaxRow = 0, nMaxPattern = 0, nNextStartRow = 0; LONG nGlbVol = m_nDefaultGlobalVolume, nOldGlbVolSlide = 0; BYTE samples[MAX_CHANNELS]; BYTE instr[MAX_CHANNELS]; BYTE notes[MAX_CHANNELS]; BYTE vols[MAX_CHANNELS]; BYTE oldparam[MAX_CHANNELS]; BYTE chnvols[MAX_CHANNELS]; DWORD patloop[MAX_CHANNELS]; memset(instr, 0, sizeof(instr)); memset(notes, 0, sizeof(notes)); memset(vols, 0xFF, sizeof(vols)); memset(patloop, 0, sizeof(patloop)); memset(oldparam, 0, sizeof(oldparam)); memset(chnvols, 64, sizeof(chnvols)); memset(samples, 0, sizeof(samples)); for (UINT icv=0; icv= MAX_PATTERNS) { // End of song ? if ((nPattern == 0xFF) || (nCurrentPattern >= MAX_ORDERS)) { goto EndMod; } else { nCurrentPattern++; nPattern = (nCurrentPattern < MAX_ORDERS) ? Order[nCurrentPattern] : 0xFF; } nNextPattern = nCurrentPattern; } // Weird stuff? if ((nPattern >= MAX_PATTERNS) || (!Patterns[nPattern]) || PatternSize[nPattern] == 0) break; // Should never happen if (nRow >= PatternSize[nPattern]) nRow = 0; // Update next position nNextRow = nRow + 1; if (nNextRow >= PatternSize[nPattern]) { nNextPattern = nCurrentPattern + 1; nNextRow = nNextStartRow; nNextStartRow = 0; } if (!nRow) { for (UINT ipck=0; ipck nMaxPattern) || ((nCurrentPattern == nMaxPattern) && (nRow >= nMaxRow))) { if (bAdjust) { m_nMusicSpeed = nMusicSpeed; m_nMusicTempo = nMusicTempo; } break; } } MODCHANNEL *pChn = Chn; MODCOMMAND *p = Patterns[nPattern] + nRow * m_nChannels; for (UINT nChn=0; nChncommand; UINT param = p->param; UINT note = p->note; if (p->instr) { instr[nChn] = p->instr; notes[nChn] = 0; vols[nChn] = 0xFF; } if ((note) && (note <= NOTE_MAX)) notes[nChn] = note; if (p->volcmd == VOLCMD_VOLUME) { vols[nChn] = p->vol; } if (command) switch (command) { // Position Jump case CMD_POSITIONJUMP: if (param <= nCurrentPattern) goto EndMod; nNextPattern = param; nNextRow = 0; nNextStartRow = 0; if (bAdjust) { pChn->nPatternLoopCount = 0; pChn->nPatternLoop = 0; } break; // Pattern Break case CMD_PATTERNBREAK: nNextRow = param; nNextPattern = nCurrentPattern + 1; nNextStartRow = 0; if (bAdjust) { pChn->nPatternLoopCount = 0; pChn->nPatternLoop = 0; } break; // Set Speed case CMD_SPEED: if (!param) break; if ((param <= 0x20) || (m_nType != MOD_TYPE_MOD)) { if (param < 128) nMusicSpeed = param; } break; // Set Tempo case CMD_TEMPO: if ((bAdjust) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) { if (param) pChn->nOldTempo = param; else param = pChn->nOldTempo; } if (param >= 0x20) nMusicTempo = param; else // Tempo Slide if ((param & 0xF0) == 0x10) { nMusicTempo += param & 0x0F; if (nMusicTempo > 255) nMusicTempo = 255; } else { nMusicTempo -= param & 0x0F; if (nMusicTempo < 32) nMusicTempo = 32; } break; // Pattern Delay case CMD_S3MCMDEX: if ((param & 0xF0) == 0x60) { nSpeedCount = param & 0x0F; break; } else if ((param & 0xF0) == 0xB0) { param &= 0x0F; param |= 0x60; } // FALLTHROUGH case CMD_MODCMDEX: if ((param & 0xF0) == 0xE0) nSpeedCount = (param & 0x0F) * nMusicSpeed; else if ((param & 0xF0) == 0x60) { if (param & 0x0F) dwElapsedTime += (dwElapsedTime - patloop[nChn]) * (param & 0x0F); else { patloop[nChn] = dwElapsedTime; if (m_nType & MOD_TYPE_XM) nNextStartRow = nRow; } } break; } if (!bAdjust) continue; switch(command) { // Portamento Up/Down case CMD_PORTAMENTOUP: case CMD_PORTAMENTODOWN: if (param) pChn->nOldPortaUpDown = param; break; // Tone-Portamento case CMD_TONEPORTAMENTO: if (param) pChn->nPortamentoSlide = param << 2; break; // Offset case CMD_OFFSET: if (param) pChn->nOldOffset = param; break; // Volume Slide case CMD_VOLUMESLIDE: case CMD_TONEPORTAVOL: case CMD_VIBRATOVOL: if (param) pChn->nOldVolumeSlide = param; break; // Set Volume case CMD_VOLUME: vols[nChn] = param; break; // Global Volume case CMD_GLOBALVOLUME: if (!(m_nType & (MOD_TYPE_IT))) param <<= 1; if (param > 128) param = 128; nGlbVol = param << 1; break; // Global Volume Slide case CMD_GLOBALVOLSLIDE: if (param) nOldGlbVolSlide = param; else param = nOldGlbVolSlide; if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { param >>= 4; if (m_nType != MOD_TYPE_IT) param <<= 1; nGlbVol += param << 1; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { param = (param & 0x0F) << 1; if (m_nType != MOD_TYPE_IT) param <<= 1; nGlbVol -= param; } else if (param & 0xF0) { param >>= 4; param <<= 1; if (m_nType != MOD_TYPE_IT) param <<= 1; nGlbVol += param * nMusicSpeed; } else { param = (param & 0x0F) << 1; if (m_nType != MOD_TYPE_IT) param <<= 1; nGlbVol -= param * nMusicSpeed; } if (nGlbVol < 0) nGlbVol = 0; if (nGlbVol > 256) nGlbVol = 256; break; case CMD_CHANNELVOLUME: if (param <= 64) chnvols[nChn] = param; break; case CMD_CHANNELVOLSLIDE: if (param) oldparam[nChn] = param; else param = oldparam[nChn]; pChn->nOldChnVolSlide = param; if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { param = (param >> 4) + chnvols[nChn]; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if (chnvols[nChn] > (int)(param & 0x0F)) param = chnvols[nChn] - (param & 0x0F); else param = 0; } else if (param & 0x0F) { param = (param & 0x0F) * nMusicSpeed; param = (chnvols[nChn] > param) ? chnvols[nChn] - param : 0; } else param = ((param & 0xF0) >> 4) * nMusicSpeed + chnvols[nChn]; if (param > 64) param = 64; chnvols[nChn] = param; break; } } nSpeedCount += nMusicSpeed; dwElapsedTime += (2500 * nSpeedCount) / nMusicTempo; } EndMod: if ((bAdjust) && (!bTotal)) { m_nGlobalVolume = nGlbVol; m_nOldGlbVolSlide = nOldGlbVolSlide; for (UINT n=0; n 64) vols[n] = 64; Chn[n].nVolume = vols[n] << 2; } } } return (dwElapsedTime+500) / 1000; } ////////////////////////////////////////////////////////////////////////////////////////////////// // Effects void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta, BOOL bUpdVol, BOOL bResetEnv) //-------------------------------------------------------------------------------------------------------- { BOOL bInstrumentChanged = FALSE; if (instr >= MAX_INSTRUMENTS) return; INSTRUMENTHEADER *penv = Headers[instr]; MODINSTRUMENT *psmp = &Ins[instr]; UINT note = pChn->nNewNote; if ((penv) && (note) && (note <= 128)) { if (penv->NoteMap[note-1] >= 0xFE) return; UINT n = penv->Keyboard[note-1]; psmp = ((n) && (n < MAX_SAMPLES)) ? &Ins[n] : NULL; } else if (m_nInstruments) { if (note >= 0xFE) return; psmp = NULL; } // Update Volume if (bUpdVol) pChn->nVolume = (psmp) ? psmp->nVolume : 0; // bInstrumentChanged is used for IT carry-on env option if (penv != pChn->pHeader) { bInstrumentChanged = TRUE; pChn->pHeader = penv; } else { // Special XM hack if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (penv) && (pChn->pInstrument) && (psmp != pChn->pInstrument)) { // FT2 doesn't change the sample in this case, // but still uses the sample info from the old one (bug?) return; } } // Instrument adjust pChn->nNewIns = 0; if (psmp) { if (penv) { pChn->nInsVol = (psmp->nGlobalVol * penv->nGlobalVol) >> 6; if (penv->dwFlags & ENV_SETPANNING) pChn->nPan = penv->nPan; pChn->nNNA = penv->nNNA; } else { pChn->nInsVol = psmp->nGlobalVol; } if (psmp->uFlags & CHN_PANNING) pChn->nPan = psmp->nPan; } // Reset envelopes if (bResetEnv) { if ((!bPorta) || (!(m_nType & MOD_TYPE_IT)) || (m_dwSongFlags & SONG_ITCOMPATMODE) || (!pChn->nLength) || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol))) { pChn->dwFlags |= CHN_FASTVOLRAMP; if ((m_nType & MOD_TYPE_IT) && (!bInstrumentChanged) && (penv) && (!(pChn->dwFlags & (CHN_KEYOFF|CHN_NOTEFADE)))) { if (!(penv->dwFlags & ENV_VOLCARRY)) pChn->nVolEnvPosition = 0; if (!(penv->dwFlags & ENV_PANCARRY)) pChn->nPanEnvPosition = 0; if (!(penv->dwFlags & ENV_PITCHCARRY)) pChn->nPitchEnvPosition = 0; } else { pChn->nVolEnvPosition = 0; pChn->nPanEnvPosition = 0; pChn->nPitchEnvPosition = 0; } pChn->nAutoVibDepth = 0; pChn->nAutoVibPos = 0; } else if ((penv) && (!(penv->dwFlags & ENV_VOLUME))) { pChn->nVolEnvPosition = 0; pChn->nAutoVibDepth = 0; pChn->nAutoVibPos = 0; } } // Invalid sample ? if (!psmp) { pChn->pInstrument = NULL; pChn->nInsVol = 0; return; } // Tone-Portamento doesn't reset the pingpong direction flag if ((bPorta) && (psmp == pChn->pInstrument)) { if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) return; pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE); pChn->dwFlags = (pChn->dwFlags & (0xFFFFFF00 | CHN_PINGPONGFLAG)) | (psmp->uFlags); } else { pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE|CHN_VOLENV|CHN_PANENV|CHN_PITCHENV); pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (psmp->uFlags); if (penv) { if (penv->dwFlags & ENV_VOLUME) pChn->dwFlags |= CHN_VOLENV; if (penv->dwFlags & ENV_PANNING) pChn->dwFlags |= CHN_PANENV; if (penv->dwFlags & ENV_PITCH) pChn->dwFlags |= CHN_PITCHENV; if ((penv->dwFlags & ENV_PITCH) && (penv->dwFlags & ENV_FILTER)) { if (!pChn->nCutOff) pChn->nCutOff = 0x7F; } if (penv->nIFC & 0x80) pChn->nCutOff = penv->nIFC & 0x7F; if (penv->nIFR & 0x80) pChn->nResonance = penv->nIFR & 0x7F; } pChn->nVolSwing = pChn->nPanSwing = 0; } pChn->pInstrument = psmp; pChn->nLength = psmp->nLength; pChn->nLoopStart = psmp->nLoopStart; pChn->nLoopEnd = psmp->nLoopEnd; pChn->nC4Speed = psmp->nC4Speed; pChn->pSample = psmp->pSample; pChn->nTranspose = psmp->RelativeTone; pChn->nFineTune = psmp->nFineTune; if (pChn->dwFlags & CHN_SUSTAINLOOP) { pChn->nLoopStart = psmp->nSustainStart; pChn->nLoopEnd = psmp->nSustainEnd; pChn->dwFlags |= CHN_LOOP; if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; } if ((pChn->dwFlags & CHN_LOOP) && (pChn->nLoopEnd < pChn->nLength)) pChn->nLength = pChn->nLoopEnd; } void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) //--------------------------------------------------------------------------- { if (note < 1) return; MODCHANNEL * const pChn = &Chn[nChn]; MODINSTRUMENT *pins = pChn->pInstrument; INSTRUMENTHEADER *penv = pChn->pHeader; if ((penv) && (note <= 0x80)) { UINT n = penv->Keyboard[note - 1]; if ((n) && (n < MAX_SAMPLES)) pins = &Ins[n]; note = penv->NoteMap[note-1]; } // Key Off if (note >= 0x80) // 0xFE or invalid note => key off { // Key Off KeyOff(nChn); // Note Cut if (note == 0xFE) { pChn->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); if ((!(m_nType & MOD_TYPE_IT)) || (m_nInstruments)) pChn->nVolume = 0; pChn->nFadeOutVol = 0; } return; } if (!pins) return; if ((!bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MED|MOD_TYPE_MT2))) { pChn->nTranspose = pins->RelativeTone; pChn->nFineTune = pins->nFineTune; } if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED)) note += pChn->nTranspose; if (note < 1) note = 1; if (note > 132) note = 132; pChn->nNote = note; if ((!bPorta) || (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) pChn->nNewIns = 0; UINT period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC4Speed); if (period) { if ((!bPorta) || (!pChn->nPeriod)) pChn->nPeriod = period; pChn->nPortamentoDest = period; if ((!bPorta) || ((!pChn->nLength) && (!(m_nType & MOD_TYPE_S3M)))) { pChn->pInstrument = pins; pChn->pSample = pins->pSample; pChn->nLength = pins->nLength; pChn->nLoopEnd = pins->nLength; pChn->nLoopStart = 0; pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (pins->uFlags); if (pChn->dwFlags & CHN_SUSTAINLOOP) { pChn->nLoopStart = pins->nSustainStart; pChn->nLoopEnd = pins->nSustainEnd; pChn->dwFlags &= ~CHN_PINGPONGLOOP; pChn->dwFlags |= CHN_LOOP; if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; } else if (pChn->dwFlags & CHN_LOOP) { pChn->nLoopStart = pins->nLoopStart; pChn->nLoopEnd = pins->nLoopEnd; if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; } pChn->nPos = 0; pChn->nPosLo = 0; if (pChn->nVibratoType < 4) pChn->nVibratoPos = ((m_nType & MOD_TYPE_IT) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS))) ? 0x10 : 0; if (pChn->nTremoloType < 4) pChn->nTremoloPos = 0; } if (pChn->nPos >= pChn->nLength) pChn->nPos = pChn->nLoopStart; } else bPorta = FALSE; if ((!bPorta) || (!(m_nType & MOD_TYPE_IT)) || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol)) || ((m_dwSongFlags & SONG_ITCOMPATMODE) && (pChn->nRowInstr))) { if ((m_nType & MOD_TYPE_IT) && (pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol)) { pChn->nVolEnvPosition = 0; pChn->nPanEnvPosition = 0; pChn->nPitchEnvPosition = 0; pChn->nAutoVibDepth = 0; pChn->nAutoVibPos = 0; pChn->dwFlags &= ~CHN_NOTEFADE; pChn->nFadeOutVol = 65536; } if ((!bPorta) || (!(m_dwSongFlags & SONG_ITCOMPATMODE)) || (pChn->nRowInstr)) { if ((!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (pChn->nRowInstr)) { pChn->dwFlags &= ~CHN_NOTEFADE; pChn->nFadeOutVol = 65536; } } } pChn->dwFlags &= ~(CHN_EXTRALOUD|CHN_KEYOFF); // Enable Ramping if (!bPorta) { pChn->nVUMeter = 0x100; pChn->nLeftVU = pChn->nRightVU = 0xFF; pChn->dwFlags &= ~CHN_FILTER; pChn->dwFlags |= CHN_FASTVOLRAMP; pChn->nRetrigCount = 0; pChn->nTremorCount = 0; if (bResetEnv) { pChn->nVolSwing = pChn->nPanSwing = 0; if (penv) { if (!(penv->dwFlags & ENV_VOLCARRY)) pChn->nVolEnvPosition = 0; if (!(penv->dwFlags & ENV_PANCARRY)) pChn->nPanEnvPosition = 0; if (!(penv->dwFlags & ENV_PITCHCARRY)) pChn->nPitchEnvPosition = 0; if (m_nType & MOD_TYPE_IT) { // Volume Swing if (penv->nVolSwing) { int d = ((LONG)penv->nVolSwing*(LONG)((rand() & 0xFF) - 0x7F)) / 128; pChn->nVolSwing = (signed short)((d * pChn->nVolume + 1)/128); } // Pan Swing if (penv->nPanSwing) { int d = ((LONG)penv->nPanSwing*(LONG)((rand() & 0xFF) - 0x7F)) / 128; pChn->nPanSwing = (signed short)d; } } } pChn->nAutoVibDepth = 0; pChn->nAutoVibPos = 0; } pChn->nLeftVol = pChn->nRightVol = 0; BOOL bFlt = (m_dwSongFlags & SONG_MPTFILTERMODE) ? FALSE : TRUE; // Setup Initial Filter for this note if (penv) { if (penv->nIFR & 0x80) { pChn->nResonance = penv->nIFR & 0x7F; bFlt = TRUE; } if (penv->nIFC & 0x80) { pChn->nCutOff = penv->nIFC & 0x7F; bFlt = TRUE; } } else { pChn->nVolSwing = pChn->nPanSwing = 0; } #ifndef NO_FILTER if ((pChn->nCutOff < 0x7F) && (bFlt)) SetupChannelFilter(pChn, TRUE); #endif // NO_FILTER } } UINT CSoundFile::GetNNAChannel(UINT nChn) const //--------------------------------------------- { const MODCHANNEL *pChn = &Chn[nChn]; // Check for empty channel const MODCHANNEL *pi = &Chn[m_nChannels]; for (UINT i=m_nChannels; inLength) return i; if (!pChn->nFadeOutVol) return 0; // All channels are used: check for lowest volume UINT result = 0; DWORD vol = 64*65536; // 25% DWORD envpos = 0xFFFFFF; const MODCHANNEL *pj = &Chn[m_nChannels]; for (UINT j=m_nChannels; jnFadeOutVol) return j; DWORD v = pj->nVolume; if (pj->dwFlags & CHN_NOTEFADE) v = v * pj->nFadeOutVol; else v <<= 16; if (pj->dwFlags & CHN_LOOP) v >>= 1; if ((v < vol) || ((v == vol) && (pj->nVolEnvPosition > envpos))) { envpos = pj->nVolEnvPosition; vol = v; result = j; } } return result; } void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) //------------------------------------------------------------------------ { MODCHANNEL *pChn = &Chn[nChn]; INSTRUMENTHEADER *penv = pChn->pHeader, *pHeader = 0; signed char *pSample; if (note > 0x80) note = 0; if (note < 1) return; // Always NNA cut - using if ((!(m_nType & (MOD_TYPE_IT|MOD_TYPE_MT2))) || (!m_nInstruments) || (bForceCut)) { if ((m_dwSongFlags & SONG_CPUVERYHIGH) || (!pChn->nLength) || (pChn->dwFlags & CHN_MUTE) || ((!pChn->nLeftVol) && (!pChn->nRightVol))) return; UINT n = GetNNAChannel(nChn); if (!n) return; MODCHANNEL *p = &Chn[n]; // Copy Channel *p = *pChn; p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO); p->nMasterChn = nChn+1; p->nCommand = 0; // Cut the note p->nFadeOutVol = 0; p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); // Stop this channel pChn->nLength = pChn->nPos = pChn->nPosLo = 0; pChn->nROfs = pChn->nLOfs = 0; pChn->nLeftVol = pChn->nRightVol = 0; return; } if (instr >= MAX_INSTRUMENTS) instr = 0; pSample = pChn->pSample; pHeader = pChn->pHeader; if ((instr) && (note)) { pHeader = Headers[instr]; if (pHeader) { UINT n = 0; if (note <= 0x80) { n = pHeader->Keyboard[note-1]; note = pHeader->NoteMap[note-1]; if ((n) && (n < MAX_SAMPLES)) pSample = Ins[n].pSample; } } else pSample = NULL; } if (!penv) return; MODCHANNEL *p = pChn; for (UINT i=nChn; i= m_nChannels) || (p == pChn)) { if (((p->nMasterChn == nChn+1) || (p == pChn)) && (p->pHeader)) { BOOL bOk = FALSE; // Duplicate Check Type switch(p->pHeader->nDCT) { // Note case DCT_NOTE: if ((note) && (p->nNote == note) && (pHeader == p->pHeader)) bOk = TRUE; break; // Sample case DCT_SAMPLE: if ((pSample) && (pSample == p->pSample)) bOk = TRUE; break; // Instrument case DCT_INSTRUMENT: if (pHeader == p->pHeader) bOk = TRUE; break; } // Duplicate Note Action if (bOk) { switch(p->pHeader->nDNA) { // Cut case DNA_NOTECUT: KeyOff(i); p->nVolume = 0; break; // Note Off case DNA_NOTEOFF: KeyOff(i); break; // Note Fade case DNA_NOTEFADE: p->dwFlags |= CHN_NOTEFADE; break; } if (!p->nVolume) { p->nFadeOutVol = 0; p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); } } } } if (pChn->dwFlags & CHN_MUTE) return; // New Note Action if ((pChn->nVolume) && (pChn->nLength)) { UINT n = GetNNAChannel(nChn); if (n) { MODCHANNEL *p = &Chn[n]; // Copy Channel *p = *pChn; p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO); p->nMasterChn = nChn+1; p->nCommand = 0; // Key Off the note switch(pChn->nNNA) { case NNA_NOTEOFF: KeyOff(n); break; case NNA_NOTECUT: p->nFadeOutVol = 0; // FALLTHROUGH case NNA_NOTEFADE: p->dwFlags |= CHN_NOTEFADE; break; } if (!p->nVolume) { p->nFadeOutVol = 0; p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); } // Stop this channel pChn->nLength = pChn->nPos = pChn->nPosLo = 0; pChn->nROfs = pChn->nLOfs = 0; } } } BOOL CSoundFile::ProcessEffects() //------------------------------- { MODCHANNEL *pChn = Chn; int nBreakRow = -1, nPosJump = -1, nPatLoopRow = -1; for (UINT nChn=0; nChnnRowInstr; UINT volcmd = pChn->nRowVolCmd; UINT vol = pChn->nRowVolume; UINT cmd = pChn->nRowCommand; UINT param = pChn->nRowParam; bool bPorta = ((cmd != CMD_TONEPORTAMENTO) && (cmd != CMD_TONEPORTAVOL) && (volcmd != VOLCMD_TONEPORTAMENTO)) ? FALSE : TRUE; UINT nStartTick = 0; pChn->dwFlags &= ~CHN_FASTVOLRAMP; // Process special effects (note delay, pattern delay, pattern loop) if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX)) { if ((!param) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) param = pChn->nOldCmdEx; else pChn->nOldCmdEx = param; // Note Delay ? if ((param & 0xF0) == 0xD0) { nStartTick = param & 0x0F; } else if (!m_nTickCount) { // Pattern Loop ? if ((((param & 0xF0) == 0x60) && (cmd == CMD_MODCMDEX)) || (((param & 0xF0) == 0xB0) && (cmd == CMD_S3MCMDEX))) { int nloop = PatternLoop(pChn, param & 0x0F); if (nloop >= 0) nPatLoopRow = nloop; } else // Pattern Delay if ((param & 0xF0) == 0xE0) { m_nPatternDelay = param & 0x0F; } } } // Handles note/instrument/volume changes if (m_nTickCount == nStartTick) // can be delayed by a note delay effect { UINT note = pChn->nRowNote; if (instr) pChn->nNewIns = instr; // XM: Key-Off + Sample == Note Cut if (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM|MOD_TYPE_MT2)) { if ((note == 0xFF) && ((!pChn->pHeader) || (!(pChn->pHeader->dwFlags & ENV_VOLUME)))) { pChn->dwFlags |= CHN_FASTVOLRAMP; pChn->nVolume = 0; note = instr = 0; } } if ((!note) && (instr)) { if (m_nInstruments) { if (pChn->pInstrument) pChn->nVolume = pChn->pInstrument->nVolume; if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { pChn->dwFlags |= CHN_FASTVOLRAMP; pChn->nVolEnvPosition = 0; pChn->nPanEnvPosition = 0; pChn->nPitchEnvPosition = 0; pChn->nAutoVibDepth = 0; pChn->nAutoVibPos = 0; pChn->dwFlags &= ~CHN_NOTEFADE; pChn->nFadeOutVol = 65536; } } else { if (instr < MAX_SAMPLES) pChn->nVolume = Ins[instr].nVolume; } if (!(m_nType & MOD_TYPE_IT)) instr = 0; } // Invalid Instrument ? if (instr >= MAX_INSTRUMENTS) instr = 0; // Note Cut/Off => ignore instrument if (note >= 0xFE) instr = 0; if ((note) && (note <= 128)) pChn->nNewNote = note; // New Note Action ? if ((note) && (note <= 128) && (!bPorta)) { CheckNNA(nChn, instr, note, FALSE); } // Instrument Change ? if (instr) { MODINSTRUMENT *psmp = pChn->pInstrument; InstrumentChange(pChn, instr, bPorta, TRUE); pChn->nNewIns = 0; // Special IT case: portamento+note causes sample change -> ignore portamento if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) && (psmp != pChn->pInstrument) && (note) && (note < 0x80)) { bPorta = FALSE; } } // New Note ? if (note) { if ((!instr) && (pChn->nNewIns) && (note < 0x80)) { InstrumentChange(pChn, pChn->nNewIns, bPorta, FALSE, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); pChn->nNewIns = 0; } NoteChange(nChn, note, bPorta, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) { pChn->dwFlags |= CHN_FASTVOLRAMP; pChn->nVolEnvPosition = 0; pChn->nPanEnvPosition = 0; pChn->nPitchEnvPosition = 0; pChn->nAutoVibDepth = 0; pChn->nAutoVibPos = 0; } } // Tick-0 only volume commands if (volcmd == VOLCMD_VOLUME) { if (vol > 64) vol = 64; pChn->nVolume = vol << 2; pChn->dwFlags |= CHN_FASTVOLRAMP; } else if (volcmd == VOLCMD_PANNING) { if (vol > 64) vol = 64; pChn->nPan = vol << 2; pChn->dwFlags |= CHN_FASTVOLRAMP; } } // Volume Column Effect (except volume & panning) if ((volcmd > VOLCMD_PANNING) && (m_nTickCount >= nStartTick)) { if (volcmd == VOLCMD_TONEPORTAMENTO) { if (m_nType & MOD_TYPE_IT) TonePortamento(pChn, ImpulseTrackerPortaVolCmd[vol & 0x0F]); else TonePortamento(pChn, vol * 16); } else { if (vol) pChn->nOldVolParam = vol; else vol = pChn->nOldVolParam; switch(volcmd) { case VOLCMD_VOLSLIDEUP: VolumeSlide(pChn, vol << 4); break; case VOLCMD_VOLSLIDEDOWN: VolumeSlide(pChn, vol); break; case VOLCMD_FINEVOLUP: if (m_nType & MOD_TYPE_IT) { if (m_nTickCount == nStartTick) VolumeSlide(pChn, (vol << 4) | 0x0F); } else FineVolumeUp(pChn, vol); break; case VOLCMD_FINEVOLDOWN: if (m_nType & MOD_TYPE_IT) { if (m_nTickCount == nStartTick) VolumeSlide(pChn, 0xF0 | vol); } else FineVolumeDown(pChn, vol); break; case VOLCMD_VIBRATOSPEED: Vibrato(pChn, vol << 4); break; case VOLCMD_VIBRATO: Vibrato(pChn, vol); break; case VOLCMD_PANSLIDELEFT: PanningSlide(pChn, vol); break; case VOLCMD_PANSLIDERIGHT: PanningSlide(pChn, vol << 4); break; case VOLCMD_PORTAUP: PortamentoUp(pChn, vol << 2); break; case VOLCMD_PORTADOWN: PortamentoDown(pChn, vol << 2); break; } } } // Effects if (cmd) switch (cmd) { // Set Volume case CMD_VOLUME: if (!m_nTickCount) { pChn->nVolume = (param < 64) ? param*4 : 256; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; // Portamento Up case CMD_PORTAMENTOUP: if ((!param) && (m_nType & MOD_TYPE_MOD)) break; PortamentoUp(pChn, param); break; // Portamento Down case CMD_PORTAMENTODOWN: if ((!param) && (m_nType & MOD_TYPE_MOD)) break; PortamentoDown(pChn, param); break; // Volume Slide case CMD_VOLUMESLIDE: if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); break; // Tone-Portamento case CMD_TONEPORTAMENTO: TonePortamento(pChn, param); break; // Tone-Portamento + Volume Slide case CMD_TONEPORTAVOL: if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); TonePortamento(pChn, 0); break; // Vibrato case CMD_VIBRATO: Vibrato(pChn, param); break; // Vibrato + Volume Slide case CMD_VIBRATOVOL: if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); Vibrato(pChn, 0); break; // Set Speed case CMD_SPEED: if (!m_nTickCount) SetSpeed(param); break; // Set Tempo case CMD_TEMPO: if (!m_nTickCount) { if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) { if (param) pChn->nOldTempo = param; else param = pChn->nOldTempo; } SetTempo(param); } break; // Set Offset case CMD_OFFSET: if (m_nTickCount) break; if (param) pChn->nOldOffset = param; else param = pChn->nOldOffset; param <<= 8; param |= (UINT)(pChn->nOldHiOffset) << 16; if ((pChn->nRowNote) && (pChn->nRowNote < 0x80)) { if (bPorta) pChn->nPos = param; else pChn->nPos += param; if (pChn->nPos >= pChn->nLength) { if (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) { pChn->nPos = pChn->nLoopStart; if ((m_dwSongFlags & SONG_ITOLDEFFECTS) && (pChn->nLength > 4)) { pChn->nPos = pChn->nLength - 2; } } } } else if ((param < pChn->nLength) && (m_nType & (MOD_TYPE_MTM|MOD_TYPE_DMF))) { pChn->nPos = param; } break; // Arpeggio case CMD_ARPEGGIO: if ((m_nTickCount) || (!pChn->nPeriod) || (!pChn->nNote)) break; if ((!param) && (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)))) break; pChn->nCommand = CMD_ARPEGGIO; if (param) pChn->nArpeggio = param; break; // Retrig case CMD_RETRIG: if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (!(param & 0xF0)) param |= pChn->nRetrigParam & 0xF0; if (!(param & 0x0F)) param |= pChn->nRetrigParam & 0x0F; param |= 0x100; // increment retrig count on first row } if (param) pChn->nRetrigParam = (BYTE)(param & 0xFF); else param = pChn->nRetrigParam; RetrigNote(nChn, param); break; // Tremor case CMD_TREMOR: if (m_nTickCount) break; pChn->nCommand = CMD_TREMOR; if (param) pChn->nTremorParam = param; break; // Set Global Volume case CMD_GLOBALVOLUME: if (m_nTickCount) break; if (m_nType != MOD_TYPE_IT) param <<= 1; if (param > 128) param = 128; m_nGlobalVolume = param << 1; break; // Global Volume Slide case CMD_GLOBALVOLSLIDE: GlobalVolSlide(param); break; // Set 8-bit Panning case CMD_PANNING8: if (m_nTickCount) break; if (!(m_dwSongFlags & SONG_SURROUNDPAN)) pChn->dwFlags &= ~CHN_SURROUND; if (m_nType & (MOD_TYPE_IT|MOD_TYPE_XM|MOD_TYPE_MT2)) { pChn->nPan = param; } else if (param <= 0x80) { pChn->nPan = param << 1; } else if (param == 0xA4) { pChn->dwFlags |= CHN_SURROUND; pChn->nPan = 0x80; } pChn->dwFlags |= CHN_FASTVOLRAMP; break; // Panning Slide case CMD_PANNINGSLIDE: PanningSlide(pChn, param); break; // Tremolo case CMD_TREMOLO: Tremolo(pChn, param); break; // Fine Vibrato case CMD_FINEVIBRATO: FineVibrato(pChn, param); break; // MOD/XM Exx Extended Commands case CMD_MODCMDEX: ExtendedMODCommands(nChn, param); break; // S3M/IT Sxx Extended Commands case CMD_S3MCMDEX: ExtendedS3MCommands(nChn, param); break; // Key Off case CMD_KEYOFF: if (!m_nTickCount) KeyOff(nChn); break; // Extra-fine porta up/down case CMD_XFINEPORTAUPDOWN: switch(param & 0xF0) { case 0x10: ExtraFinePortamentoUp(pChn, param & 0x0F); break; case 0x20: ExtraFinePortamentoDown(pChn, param & 0x0F); break; // Modplug XM Extensions case 0x50: case 0x60: case 0x70: case 0x90: case 0xA0: ExtendedS3MCommands(nChn, param); break; } break; // Set Channel Global Volume case CMD_CHANNELVOLUME: if (m_nTickCount) break; if (param <= 64) { pChn->nGlobalVol = param; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; // Channel volume slide case CMD_CHANNELVOLSLIDE: ChannelVolSlide(pChn, param); break; // Panbrello (IT) case CMD_PANBRELLO: Panbrello(pChn, param); break; // Set Envelope Position case CMD_SETENVPOSITION: if (!m_nTickCount) { pChn->nVolEnvPosition = param; pChn->nPanEnvPosition = param; pChn->nPitchEnvPosition = param; if (pChn->pHeader) { INSTRUMENTHEADER *penv = pChn->pHeader; if ((pChn->dwFlags & CHN_PANENV) && (penv->nPanEnv) && (param > penv->PanPoints[penv->nPanEnv-1])) { pChn->dwFlags &= ~CHN_PANENV; } } } break; // Position Jump case CMD_POSITIONJUMP: nPosJump = param; m_nNextStartRow = 0; break; // Pattern Break case CMD_PATTERNBREAK: nBreakRow = param; m_nNextStartRow = 0; break; // Midi Controller case CMD_MIDI: if (m_nTickCount) break; if (param < 0x80){ ProcessMidiMacro(nChn, &m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro << 5], param); } else { ProcessMidiMacro(nChn, &m_MidiCfg.szMidiZXXExt[(param & 0x7F) << 5], 0); } break; } } // Navigation Effects if (!m_nTickCount) { // Pattern Loop if (nPatLoopRow >= 0) { m_nNextPattern = m_nCurrentPattern; m_nNextRow = nPatLoopRow; if (m_nPatternDelay) m_nNextRow++; } else // Pattern Break / Position Jump only if no loop running if ((nBreakRow >= 0) || (nPosJump >= 0)) { BOOL bNoLoop = FALSE; if (nPosJump < 0) nPosJump = m_nCurrentPattern+1; if (nBreakRow < 0) nBreakRow = 0; // Modplug Tracker & ModPlugin allow backward jumps if ((nPosJump < (int)m_nCurrentPattern) || ((nPosJump == (int)m_nCurrentPattern) && (nBreakRow <= (int)m_nRow))) { if (!IsValidBackwardJump(m_nCurrentPattern, m_nRow, nPosJump, nBreakRow)) { if (m_nRepeatCount) { if (m_nRepeatCount > 0) m_nRepeatCount--; } else { // Backward jump disabled bNoLoop = TRUE; //reset repeat count incase there are multiple loops. //(i.e. Unreal tracks) m_nRepeatCount = m_nInitialRepeatCount; } } } if (((!bNoLoop) && (nPosJump < MAX_ORDERS)) && ((nPosJump != (int)m_nCurrentPattern) || (nBreakRow != (int)m_nRow))) { if (nPosJump != (int)m_nCurrentPattern) { for (UINT i=0; inOldPortaUpDown = param; else param = pChn->nOldPortaUpDown; if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0)) { if (param & 0x0F) { if ((param & 0xF0) == 0xF0) { FinePortamentoUp(pChn, param & 0x0F); } else if ((param & 0xF0) == 0xE0) { ExtraFinePortamentoUp(pChn, param & 0x0F); } } return; } // Regular Slide if (!(m_dwSongFlags & SONG_FIRSTTICK) || (m_nMusicSpeed == 1)) //rewbs.PortaA01fix { DoFreqSlide(pChn, -(int)(param * 4)); } } void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) //----------------------------------------------------------- { if (param) pChn->nOldPortaUpDown = param; else param = pChn->nOldPortaUpDown; if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0)) { if (param & 0x0F) { if ((param & 0xF0) == 0xF0) { FinePortamentoDown(pChn, param & 0x0F); } else if ((param & 0xF0) == 0xE0) { ExtraFinePortamentoDown(pChn, param & 0x0F); } } return; } if (!(m_dwSongFlags & SONG_FIRSTTICK) || (m_nMusicSpeed == 1)) { //rewbs.PortaA01fix DoFreqSlide(pChn, (int)(param << 2)); } } void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) //------------------------------------------------------------- { if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; } if (m_dwSongFlags & SONG_FIRSTTICK) { if ((pChn->nPeriod) && (param)) { if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideDownTable[param & 0x0F], 65536); } else { pChn->nPeriod -= (int)(param * 4); } if (pChn->nPeriod < 1) pChn->nPeriod = 1; } } } void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) //--------------------------------------------------------------- { if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; } if (m_dwSongFlags & SONG_FIRSTTICK) { if ((pChn->nPeriod) && (param)) { if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideUpTable[param & 0x0F], 65536); } else { pChn->nPeriod += (int)(param * 4); } if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; } } } void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) //------------------------------------------------------------------ { if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; } if (m_dwSongFlags & SONG_FIRSTTICK) { if ((pChn->nPeriod) && (param)) { if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { pChn->nPeriod = _muldivr(pChn->nPeriod, FineLinearSlideDownTable[param & 0x0F], 65536); } else { pChn->nPeriod -= (int)(param); } if (pChn->nPeriod < 1) pChn->nPeriod = 1; } } } void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) //-------------------------------------------------------------------- { if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; } if (m_dwSongFlags & SONG_FIRSTTICK) { if ((pChn->nPeriod) && (param)) { if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { pChn->nPeriod = _muldivr(pChn->nPeriod, FineLinearSlideUpTable[param & 0x0F], 65536); } else { pChn->nPeriod += (int)(param); } if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; } } } // Portamento Slide void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) //----------------------------------------------------------- { if (param) pChn->nPortamentoSlide = param * 4; pChn->dwFlags |= CHN_PORTAMENTO; if ((pChn->nPeriod) && (pChn->nPortamentoDest) && (!(m_dwSongFlags & SONG_FIRSTTICK))) { if (pChn->nPeriod < pChn->nPortamentoDest) { LONG delta = (int)pChn->nPortamentoSlide; if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { UINT n = pChn->nPortamentoSlide >> 2; if (n > 255) n = 255; delta = _muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536) - pChn->nPeriod; if (delta < 1) delta = 1; } pChn->nPeriod += delta; if (pChn->nPeriod > pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; } else if (pChn->nPeriod > pChn->nPortamentoDest) { LONG delta = - (int)pChn->nPortamentoSlide; if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { UINT n = pChn->nPortamentoSlide >> 2; if (n > 255) n = 255; delta = _muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536) - pChn->nPeriod; if (delta > -1) delta = -1; } pChn->nPeriod += delta; if (pChn->nPeriod < pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; } } } void CSoundFile::Vibrato(MODCHANNEL *p, UINT param) //------------------------------------------------- { if (param & 0x0F) p->nVibratoDepth = (param & 0x0F) * 4; if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; p->dwFlags |= CHN_VIBRATO; } void CSoundFile::FineVibrato(MODCHANNEL *p, UINT param) //----------------------------------------------------- { if (param & 0x0F) p->nVibratoDepth = param & 0x0F; if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; p->dwFlags |= CHN_VIBRATO; } void CSoundFile::Panbrello(MODCHANNEL *p, UINT param) //--------------------------------------------------- { if (param & 0x0F) p->nPanbrelloDepth = param & 0x0F; if (param & 0xF0) p->nPanbrelloSpeed = (param >> 4) & 0x0F; p->dwFlags |= CHN_PANBRELLO; } void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) //-------------------------------------------------------- { if (param) pChn->nOldVolumeSlide = param; else param = pChn->nOldVolumeSlide; LONG newvolume = pChn->nVolume; if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_AMF)) { if ((param & 0x0F) == 0x0F) { if (param & 0xF0) { FineVolumeUp(pChn, (param >> 4)); return; } else { if ((m_dwSongFlags & SONG_FIRSTTICK) && (!(m_dwSongFlags & SONG_FASTVOLSLIDES))) { newvolume -= 0x0F * 4; } } } else if ((param & 0xF0) == 0xF0) { if (param & 0x0F) { FineVolumeDown(pChn, (param & 0x0F)); return; } else { if ((m_dwSongFlags & SONG_FIRSTTICK) && (!(m_dwSongFlags & SONG_FASTVOLSLIDES))) { newvolume += 0x0F * 4; } } } } if ((!(m_dwSongFlags & SONG_FIRSTTICK)) || (m_dwSongFlags & SONG_FASTVOLSLIDES)) { if (param & 0x0F) newvolume -= (int)((param & 0x0F) * 4); else newvolume += (int)((param & 0xF0) >> 2); if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; } if (newvolume < 0) newvolume = 0; if (newvolume > 256) newvolume = 256; pChn->nVolume = newvolume; } void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) //--------------------------------------------------------- { LONG nPanSlide = 0; if (param) pChn->nOldPanSlide = param; else param = pChn->nOldPanSlide; if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) { if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { if (m_dwSongFlags & SONG_FIRSTTICK) { param = (param & 0xF0) >> 2; nPanSlide = - (int)param; } } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if (m_dwSongFlags & SONG_FIRSTTICK) { nPanSlide = (param & 0x0F) << 2; } } else { if (!(m_dwSongFlags & SONG_FIRSTTICK)) { if (param & 0x0F) nPanSlide = (int)((param & 0x0F) << 2); else nPanSlide = -(int)((param & 0xF0) >> 2); } } } else { if (!(m_dwSongFlags & SONG_FIRSTTICK)) { if (param & 0x0F) nPanSlide = -(int)((param & 0x0F) << 2); else nPanSlide = (int)((param & 0xF0) >> 2); } } if (nPanSlide) { nPanSlide += pChn->nPan; if (nPanSlide < 0) nPanSlide = 0; if (nPanSlide > 256) nPanSlide = 256; pChn->nPan = nPanSlide; } } void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) //--------------------------------------------------------- { if (param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; if (m_dwSongFlags & SONG_FIRSTTICK) { pChn->nVolume += param * 4; if (pChn->nVolume > 256) pChn->nVolume = 256; if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; } } void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) //----------------------------------------------------------- { if (param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; if (m_dwSongFlags & SONG_FIRSTTICK) { pChn->nVolume -= param * 4; if (pChn->nVolume < 0) pChn->nVolume = 0; if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; } } void CSoundFile::Tremolo(MODCHANNEL *p, UINT param) //------------------------------------------------- { if (param & 0x0F) p->nTremoloDepth = (param & 0x0F) << 2; if (param & 0xF0) p->nTremoloSpeed = (param >> 4) & 0x0F; p->dwFlags |= CHN_TREMOLO; } void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) //------------------------------------------------------------ { LONG nChnSlide = 0; if (param) pChn->nOldChnVolSlide = param; else param = pChn->nOldChnVolSlide; if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { if (m_dwSongFlags & SONG_FIRSTTICK) nChnSlide = param >> 4; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if (m_dwSongFlags & SONG_FIRSTTICK) nChnSlide = - (int)(param & 0x0F); } else { if (!(m_dwSongFlags & SONG_FIRSTTICK)) { if (param & 0x0F) nChnSlide = -(int)(param & 0x0F); else nChnSlide = (int)((param & 0xF0) >> 4); } } if (nChnSlide) { nChnSlide += pChn->nGlobalVol; if (nChnSlide < 0) nChnSlide = 0; if (nChnSlide > 64) nChnSlide = 64; pChn->nGlobalVol = nChnSlide; } } void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) //--------------------------------------------------------- { MODCHANNEL *pChn = &Chn[nChn]; UINT command = param & 0xF0; param &= 0x0F; switch(command) { // E0x: Set Filter // E1x: Fine Portamento Up case 0x10: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(pChn, param); break; // E2x: Fine Portamento Down case 0x20: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(pChn, param); break; // E3x: Set Glissando Control case 0x30: pChn->dwFlags &= ~CHN_GLISSANDO; if (param) pChn->dwFlags |= CHN_GLISSANDO; break; // E4x: Set Vibrato WaveForm case 0x40: pChn->nVibratoType = param & 0x07; break; // E5x: Set FineTune case 0x50: if (m_nTickCount) break; pChn->nC4Speed = S3MFineTuneTable[param]; if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) pChn->nFineTune = param*2; else pChn->nFineTune = MOD2XMFineTune(param); if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC4Speed); break; // E6x: Pattern Loop // E7x: Set Tremolo WaveForm case 0x70: pChn->nTremoloType = param & 0x07; break; // E8x: Set 4-bit Panning case 0x80: if (!m_nTickCount) { pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; // E9x: Retrig case 0x90: RetrigNote(nChn, param); break; // EAx: Fine Volume Up case 0xA0: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(pChn, param); break; // EBx: Fine Volume Down case 0xB0: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(pChn, param); break; // ECx: Note Cut case 0xC0: NoteCut(nChn, param); break; // EDx: Note Delay // EEx: Pattern Delay // EFx: MOD: Invert Loop, XM: Set Active Midi Macro case 0xF0: pChn->nActiveMacro = param; break; } } void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) //--------------------------------------------------------- { MODCHANNEL *pChn = &Chn[nChn]; UINT command = param & 0xF0; param &= 0x0F; switch(command) { // S0x: Set Filter // S1x: Set Glissando Control case 0x10: pChn->dwFlags &= ~CHN_GLISSANDO; if (param) pChn->dwFlags |= CHN_GLISSANDO; break; // S2x: Set FineTune case 0x20: if (m_nTickCount) break; pChn->nC4Speed = S3MFineTuneTable[param & 0x0F]; pChn->nFineTune = MOD2XMFineTune(param); if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC4Speed); break; // S3x: Set Vibrato WaveForm case 0x30: pChn->nVibratoType = param & 0x07; break; // S4x: Set Tremolo WaveForm case 0x40: pChn->nTremoloType = param & 0x07; break; // S5x: Set Panbrello WaveForm case 0x50: pChn->nPanbrelloType = param & 0x07; break; // S6x: Pattern Delay for x frames case 0x60: m_nFrameDelay = param; break; // S7x: Envelope Control case 0x70: if (m_nTickCount) break; switch(param) { case 0: case 1: case 2: { MODCHANNEL *bkp = &Chn[m_nChannels]; for (UINT i=m_nChannels; inMasterChn == nChn+1) { if (param == 1) KeyOff(i); else if (param == 2) bkp->dwFlags |= CHN_NOTEFADE; else { bkp->dwFlags |= CHN_NOTEFADE; bkp->nFadeOutVol = 0; } } } } break; case 3: pChn->nNNA = NNA_NOTECUT; break; case 4: pChn->nNNA = NNA_CONTINUE; break; case 5: pChn->nNNA = NNA_NOTEOFF; break; case 6: pChn->nNNA = NNA_NOTEFADE; break; case 7: pChn->dwFlags &= ~CHN_VOLENV; break; case 8: pChn->dwFlags |= CHN_VOLENV; break; case 9: pChn->dwFlags &= ~CHN_PANENV; break; case 10: pChn->dwFlags |= CHN_PANENV; break; case 11: pChn->dwFlags &= ~CHN_PITCHENV; break; case 12: pChn->dwFlags |= CHN_PITCHENV; break; } break; // S8x: Set 4-bit Panning case 0x80: if (!m_nTickCount) { pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; // S9x: Set Surround case 0x90: ExtendedChannelEffect(pChn, param & 0x0F); break; // SAx: Set 64k Offset case 0xA0: if (!m_nTickCount) { pChn->nOldHiOffset = param; if ((pChn->nRowNote) && (pChn->nRowNote < 0x80)) { DWORD pos = param << 16; if (pos < pChn->nLength) pChn->nPos = pos; } } break; // SBx: Pattern Loop // SCx: Note Cut case 0xC0: NoteCut(nChn, param); break; // SDx: Note Delay // case 0xD0: break; // SEx: Pattern Delay for x rows // SFx: S3M: Funk Repeat, IT: Set Active Midi Macro case 0xF0: pChn->nActiveMacro = param; break; } } void CSoundFile::ExtendedChannelEffect(MODCHANNEL *pChn, UINT param) //------------------------------------------------------------------ { // S9x and X9x commands (S3M/XM/IT only) if (m_nTickCount) return; switch(param & 0x0F) { // S90: Surround Off case 0x00: pChn->dwFlags &= ~CHN_SURROUND; break; // S91: Surround On case 0x01: pChn->dwFlags |= CHN_SURROUND; pChn->nPan = 128; break; //////////////////////////////////////////////////////////// // Modplug Extensions // S98: Reverb Off case 0x08: pChn->dwFlags &= ~CHN_REVERB; pChn->dwFlags |= CHN_NOREVERB; break; // S99: Reverb On case 0x09: pChn->dwFlags &= ~CHN_NOREVERB; pChn->dwFlags |= CHN_REVERB; break; // S9A: 2-Channels surround mode case 0x0A: m_dwSongFlags &= ~SONG_SURROUNDPAN; break; // S9B: 4-Channels surround mode case 0x0B: m_dwSongFlags |= SONG_SURROUNDPAN; break; // S9C: IT Filter Mode case 0x0C: m_dwSongFlags &= ~SONG_MPTFILTERMODE; break; // S9D: MPT Filter Mode case 0x0D: m_dwSongFlags |= SONG_MPTFILTERMODE; break; // S9E: Go forward case 0x0E: pChn->dwFlags &= ~(CHN_PINGPONGFLAG); break; // S9F: Go backward (set position at the end for non-looping samples) case 0x0F: if ((!(pChn->dwFlags & CHN_LOOP)) && (!pChn->nPos) && (pChn->nLength)) { pChn->nPos = pChn->nLength - 1; pChn->nPosLo = 0xFFFF; } pChn->dwFlags |= CHN_PINGPONGFLAG; break; } } void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) //--------------------------------------------------------------------------- { MODCHANNEL *pChn = &Chn[nChn]; DWORD dwMacro = (*((LPDWORD)pszMidiMacro)) & 0x7F5F7F5F; // Not Internal Device ? if (dwMacro != 0x30463046 && dwMacro != 0x31463046) { UINT pos = 0, nNib = 0, nBytes = 0; DWORD dwMidiCode = 0, dwByteCode = 0; while (pos+6 <= 32) { CHAR cData = pszMidiMacro[pos++]; if (!cData) break; if ((cData >= '0') && (cData <= '9')) { dwByteCode = (dwByteCode<<4) | (cData-'0'); nNib++; } else if ((cData >= 'A') && (cData <= 'F')) { dwByteCode = (dwByteCode<<4) | (cData-'A'+10); nNib++; } else if ((cData >= 'a') && (cData <= 'f')) { dwByteCode = (dwByteCode<<4) | (cData-'a'+10); nNib++; } else if ((cData == 'z') || (cData == 'Z')) { dwByteCode = param & 0x7f; nNib = 2; } else if ((cData == 'x') || (cData == 'X')) { dwByteCode = param & 0x70; nNib = 2; } else if ((cData == 'y') || (cData == 'Y')) { dwByteCode = (param & 0x0f)<<3; nNib = 2; } else if (nNib >= 2) { nNib = 0; dwMidiCode |= dwByteCode << (nBytes*8); dwByteCode = 0; nBytes++; if (nBytes >= 3) { UINT nMasterCh = (nChn < m_nChannels) ? nChn+1 : pChn->nMasterChn; if ((nMasterCh) && (nMasterCh <= m_nChannels)) { UINT nPlug = ChnSettings[nMasterCh-1].nMixPlugin; if ((nPlug) && (nPlug <= MAX_MIXPLUGINS)) { IMixPlugin *pPlugin = m_MixPlugins[nPlug-1].pMixPlugin; if ((pPlugin) && (m_MixPlugins[nPlug-1].pMixState)) { pPlugin->MidiSend(dwMidiCode); } } } nBytes = 0; dwMidiCode = 0; } } } return; } // Internal device pszMidiMacro += 4; // Filter ? if (pszMidiMacro[0] == '0') { CHAR cData1 = pszMidiMacro[2]; DWORD dwParam = 0; if ((cData1 == 'z') || (cData1 == 'Z')) { dwParam = param; } else { CHAR cData2 = pszMidiMacro[3]; if ((cData1 >= '0') && (cData1 <= '9')) dwParam += (cData1 - '0') << 4; else if ((cData1 >= 'A') && (cData1 <= 'F')) dwParam += (cData1 - 'A' + 0x0A) << 4; if ((cData2 >= '0') && (cData2 <= '9')) dwParam += (cData2 - '0'); else if ((cData2 >= 'A') && (cData2 <= 'F')) dwParam += (cData2 - 'A' + 0x0A); } switch(pszMidiMacro[1]) { // F0.F0.00.xx: Set CutOff case '0': { int oldcutoff = pChn->nCutOff; if (dwParam < 0x80) pChn->nCutOff = (BYTE)dwParam; #ifndef NO_FILTER oldcutoff -= pChn->nCutOff; if (oldcutoff < 0) oldcutoff = -oldcutoff; if ((pChn->nVolume > 0) || (oldcutoff < 0x10) || (!(pChn->dwFlags & CHN_FILTER)) || (!(pChn->nLeftVol|pChn->nRightVol))) SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE); #endif // NO_FILTER } break; // F0.F0.01.xx: Set Resonance case '1': if (dwParam < 0x80) pChn->nResonance = (BYTE)dwParam; #ifndef NO_FILTER SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE); #endif // NO_FILTER break; } } } void CSoundFile::RetrigNote(UINT nChn, UINT param) //------------------------------------------------ { // Retrig: bit 8 is set if it's the new XM retrig MODCHANNEL *pChn = &Chn[nChn]; UINT nRetrigSpeed = param & 0x0F; UINT nRetrigCount = pChn->nRetrigCount; BOOL bDoRetrig = FALSE; if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) { if (!nRetrigSpeed) nRetrigSpeed = 1; if ((nRetrigCount) && (!(nRetrigCount % nRetrigSpeed))) bDoRetrig = TRUE; nRetrigCount++; } else { UINT realspeed = nRetrigSpeed; if ((param & 0x100) && (pChn->nRowVolCmd == VOLCMD_VOLUME) && (pChn->nRowParam & 0xF0)) realspeed++; if ((m_nTickCount) || (param & 0x100)) { if (!realspeed) realspeed = 1; if ((!(param & 0x100)) && (m_nMusicSpeed) && (!(m_nTickCount % realspeed))) bDoRetrig = TRUE; nRetrigCount++; } else if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) nRetrigCount = 0; if (nRetrigCount >= realspeed) { if ((m_nTickCount) || ((param & 0x100) && (!pChn->nRowNote))) bDoRetrig = TRUE; } } if (bDoRetrig) { UINT dv = (param >> 4) & 0x0F; if (dv) { int vol = pChn->nVolume; if (retrigTable1[dv]) vol = (vol * retrigTable1[dv]) >> 4; else vol += ((int)retrigTable2[dv]) << 2; if (vol < 0) vol = 0; if (vol > 256) vol = 256; pChn->nVolume = vol; pChn->dwFlags |= CHN_FASTVOLRAMP; } UINT nNote = pChn->nNewNote; LONG nOldPeriod = pChn->nPeriod; if ((nNote) && (nNote <= NOTE_MAX) && (pChn->nLength)) CheckNNA(nChn, 0, nNote, TRUE); BOOL bResetEnv = FALSE; if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if ((pChn->nRowInstr) && (param < 0x100)) { InstrumentChange(pChn, pChn->nRowInstr, FALSE, FALSE); bResetEnv = TRUE; } if (param < 0x100) bResetEnv = TRUE; } NoteChange(nChn, nNote, FALSE, bResetEnv); if ((m_nType & MOD_TYPE_IT) && (!pChn->nRowNote) && (nOldPeriod)) pChn->nPeriod = nOldPeriod; if (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) nRetrigCount = 0; } pChn->nRetrigCount = (BYTE)nRetrigCount; } void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) //------------------------------------------------------------- { // IT Linear slides if (!pChn->nPeriod) return; if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) { if (nFreqSlide < 0) { UINT n = (- nFreqSlide) >> 2; if (n > 255) n = 255; pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536); } else { UINT n = (nFreqSlide) >> 2; if (n > 255) n = 255; pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536); } } else { pChn->nPeriod += nFreqSlide; } if (pChn->nPeriod < 1) { pChn->nPeriod = 1; if (m_nType & MOD_TYPE_IT) { pChn->dwFlags |= CHN_NOTEFADE; pChn->nFadeOutVol = 0; } } } void CSoundFile::NoteCut(UINT nChn, UINT nTick) //--------------------------------------------- { if (m_nTickCount == nTick) { MODCHANNEL *pChn = &Chn[nChn]; // if (m_nInstruments) KeyOff(pChn); ? pChn->nVolume = 0; pChn->dwFlags |= CHN_FASTVOLRAMP; } } void CSoundFile::KeyOff(UINT nChn) //-------------------------------- { MODCHANNEL *pChn = &Chn[nChn]; BOOL bKeyOn = (pChn->dwFlags & CHN_KEYOFF) ? FALSE : TRUE; pChn->dwFlags |= CHN_KEYOFF; //if ((!pChn->pHeader) || (!(pChn->dwFlags & CHN_VOLENV))) if ((pChn->pHeader) && (!(pChn->dwFlags & CHN_VOLENV))) { pChn->dwFlags |= CHN_NOTEFADE; } if (!pChn->nLength) return; if ((pChn->dwFlags & CHN_SUSTAINLOOP) && (pChn->pInstrument) && (bKeyOn)) { MODINSTRUMENT *psmp = pChn->pInstrument; if (psmp->uFlags & CHN_LOOP) { if (psmp->uFlags & CHN_PINGPONGLOOP) pChn->dwFlags |= CHN_PINGPONGLOOP; else pChn->dwFlags &= ~(CHN_PINGPONGLOOP|CHN_PINGPONGFLAG); pChn->dwFlags |= CHN_LOOP; pChn->nLength = psmp->nLength; pChn->nLoopStart = psmp->nLoopStart; pChn->nLoopEnd = psmp->nLoopEnd; if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; } else { pChn->dwFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP|CHN_PINGPONGFLAG); pChn->nLength = psmp->nLength; } } if (pChn->pHeader) { INSTRUMENTHEADER *penv = pChn->pHeader; if (((penv->dwFlags & ENV_VOLLOOP) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) && (penv->nFadeOut)) pChn->dwFlags |= CHN_NOTEFADE; } } ////////////////////////////////////////////////////////// // CSoundFile: Global Effects void CSoundFile::SetSpeed(UINT param) //----------------------------------- { UINT max = (m_nType == MOD_TYPE_IT) ? 256 : 128; // Modplug Tracker and Mod-Plugin don't do this check // Big Hack!!! if ((!param) || (param >= 0x80) || ((m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM|MOD_TYPE_MT2)) && (param >= 0x1E))) { if (IsSongFinished(m_nCurrentPattern, m_nRow+1)) { GlobalFadeSong(1000); } } if ((m_nType & MOD_TYPE_S3M) && (param > 0x80)) param -= 0x80; if ((param) && (param <= max)) m_nMusicSpeed = param; } void CSoundFile::SetTempo(UINT param) //----------------------------------- { if (param < 0x20) { // Tempo Slide if ((param & 0xF0) == 0x10) { m_nMusicTempo += (param & 0x0F) * 2; if (m_nMusicTempo > 255) m_nMusicTempo = 255; } else { m_nMusicTempo -= (param & 0x0F) * 2; if ((LONG)m_nMusicTempo < 32) m_nMusicTempo = 32; } } else { m_nMusicTempo = param; } } int CSoundFile::PatternLoop(MODCHANNEL *pChn, UINT param) //------------------------------------------------------- { if (param) { if (pChn->nPatternLoopCount) { pChn->nPatternLoopCount--; if (!pChn->nPatternLoopCount) return -1; } else { MODCHANNEL *p = Chn; for (UINT i=0; inPatternLoopCount) return -1; } pChn->nPatternLoopCount = param; } return pChn->nPatternLoop; } else { pChn->nPatternLoop = m_nRow; if (m_nType & MOD_TYPE_XM) m_nNextStartRow = m_nRow; } return -1; } void CSoundFile::GlobalVolSlide(UINT param) //----------------------------------------- { LONG nGlbSlide = 0; if (param) m_nOldGlbVolSlide = param; else param = m_nOldGlbVolSlide; if (((param & 0x0F) == 0x0F) && (param & 0xF0)) { if (m_dwSongFlags & SONG_FIRSTTICK) nGlbSlide = (param >> 4) * 2; } else if (((param & 0xF0) == 0xF0) && (param & 0x0F)) { if (m_dwSongFlags & SONG_FIRSTTICK) nGlbSlide = - (int)((param & 0x0F) * 2); } else { if (!(m_dwSongFlags & SONG_FIRSTTICK)) { if (param & 0xF0) nGlbSlide = (int)((param & 0xF0) >> 4) * 2; else nGlbSlide = -(int)((param & 0x0F) * 2); } } if (nGlbSlide) { if (m_nType != MOD_TYPE_IT) nGlbSlide *= 2; nGlbSlide += m_nGlobalVolume; if (nGlbSlide < 0) nGlbSlide = 0; if (nGlbSlide > 256) nGlbSlide = 256; m_nGlobalVolume = nGlbSlide; } } DWORD CSoundFile::IsSongFinished(UINT nStartOrder, UINT nStartRow) const //---------------------------------------------------------------------- { UINT nOrd; for (nOrd=nStartOrder; nOrd= MAX_PATTERNS) break; p = Patterns[nPat]; if (p) { UINT len = PatternSize[nPat] * m_nChannels; UINT pos = (nOrd == nStartOrder) ? nStartRow : 0; pos *= m_nChannels; while (pos < len) { UINT cmd; if ((p[pos].note) || (p[pos].volcmd)) return 0; cmd = p[pos].command; if (cmd == CMD_MODCMDEX) { UINT cmdex = p[pos].param & 0xF0; if ((!cmdex) || (cmdex == 0x60) || (cmdex == 0xE0) || (cmdex == 0xF0)) cmd = 0; } if ((cmd) && (cmd != CMD_SPEED) && (cmd != CMD_TEMPO)) return 0; pos++; } } } } return (nOrd < MAX_ORDERS) ? nOrd : MAX_ORDERS-1; } BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const //---------------------------------------------------------------------------------------------------------- { while ((nJumpOrder < MAX_PATTERNS) && (Order[nJumpOrder] == 0xFE)) nJumpOrder++; if ((nStartOrder >= MAX_PATTERNS) || (nJumpOrder >= MAX_PATTERNS)) return FALSE; // Treat only case with jumps in the same pattern if (nJumpOrder > nStartOrder) return TRUE; if ((nJumpOrder < nStartOrder) || (nJumpRow >= PatternSize[nStartOrder]) || (!Patterns[nStartOrder]) || (nStartRow >= 256) || (nJumpRow >= 256)) return FALSE; // See if the pattern is being played backward BYTE row_hist[256]; memset(row_hist, 0, sizeof(row_hist)); UINT nRows = PatternSize[nStartOrder], row = nJumpRow; if (nRows > 256) nRows = 256; row_hist[nStartRow] = TRUE; while ((row < 256) && (!row_hist[row])) { if (row >= nRows) return TRUE; row_hist[row] = TRUE; MODCOMMAND *p = Patterns[nStartOrder] + row * m_nChannels; row++; int breakrow = -1, posjump = 0; for (UINT i=0; icommand == CMD_POSITIONJUMP) { if (p->param < nStartOrder) return FALSE; if (p->param > nStartOrder) return TRUE; posjump = TRUE; } else if (p->command == CMD_PATTERNBREAK) { breakrow = p->param; } } if (breakrow >= 0) { if (!posjump) return TRUE; row = breakrow; } if (row >= nRows) return TRUE; } return FALSE; } ////////////////////////////////////////////////////// // Note/Period/Frequency functions UINT CSoundFile::GetNoteFromPeriod(UINT period) const //--------------------------------------------------- { if (!period) return 0; if (m_nType & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_MTM|MOD_TYPE_669|MOD_TYPE_OKT|MOD_TYPE_AMF0)) { period >>= 2; for (UINT i=0; i<6*12; i++) { if (period >= ProTrackerPeriodTable[i]) { if ((period != ProTrackerPeriodTable[i]) && (i)) { UINT p1 = ProTrackerPeriodTable[i-1]; UINT p2 = ProTrackerPeriodTable[i]; if (p1 - period < (period - p2)) return i+36; } return i+1+36; } } return 6*12+36; } else { for (UINT i=1; i 0) && (n <= (LONG)period)) return i; } return NOTE_MAX; } } UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const //------------------------------------------------------------------------------- { if ((!note) || (note > 0xF0)) return 0; if (m_nType & (MOD_TYPE_IT|MOD_TYPE_S3M|MOD_TYPE_STM|MOD_TYPE_MDL|MOD_TYPE_ULT|MOD_TYPE_WAV |MOD_TYPE_FAR|MOD_TYPE_DMF|MOD_TYPE_PTM|MOD_TYPE_AMS|MOD_TYPE_DBM|MOD_TYPE_AMF|MOD_TYPE_PSM)) { note--; if (m_dwSongFlags & SONG_LINEARSLIDES) { return (FreqS3MTable[note % 12] << 5) >> (note / 12); } else { int divider; if (!nC4Speed) nC4Speed = 8363; // if C4Speed is large, then up shifting may produce a zero divider divider = nC4Speed << (note / 12); if (!divider) divider = 1e6; return _muldiv(8363, (FreqS3MTable[note % 12] << 5), divider); } } else if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (note < 13) note = 13; note -= 13; if (m_dwSongFlags & SONG_LINEARSLIDES) { LONG l = ((NOTE_MAX - note) << 6) - (nFineTune / 2); if (l < 1) l = 1; return (UINT)l; } else { int finetune = nFineTune; UINT rnote = (note % 12) << 3; UINT roct = note / 12; int rfine = finetune / 16; int i = rnote + rfine + 8; if (i < 0) i = 0; if (i >= 104) i = 103; UINT per1 = XMPeriodTable[i]; if ( finetune < 0 ) { rfine--; finetune = -finetune; } else rfine++; i = rnote+rfine+8; if (i < 0) i = 0; if (i >= 104) i = 103; UINT per2 = XMPeriodTable[i]; rfine = finetune & 0x0F; per1 *= 16-rfine; per2 *= rfine; return ((per1 + per2) << 1) >> roct; } } else { note--; nFineTune = XM2MODFineTune(nFineTune); if ((nFineTune) || (note < 36) || (note >= 36+6*12)) return (ProTrackerTunedPeriods[nFineTune*12 + note % 12] << 5) >> (note / 12); else return (ProTrackerPeriodTable[note-36] << 2); } } UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const //----------------------------------------------------------------------------------- { if (!period) return 0; if (m_nType & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_MTM|MOD_TYPE_669|MOD_TYPE_OKT|MOD_TYPE_AMF0)) { return (3546895L*4) / period; } else if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) { if (m_dwSongFlags & SONG_LINEARSLIDES) return XMLinearTable[period % 768] >> (period / 768); else return 8363 * 1712L / period; } else { if (m_dwSongFlags & SONG_LINEARSLIDES) { if (!nC4Speed) nC4Speed = 8363; return _muldiv(nC4Speed, 1712L << 8, (period << 8)+nPeriodFrac); } else { return _muldiv(8363, 1712L << 8, (period << 8)+nPeriodFrac); } } } openjazz-20240919/ext/psmplug/sndfile.cpp000066400000000000000000000762361467303304200202650ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque , * Adam Goode (endian and char fixes for PPC) */ #include #include "stdafx.h" #include "sndfile.h" ////////////////////////////////////////////////////////// // CSoundFile CSoundFile::CSoundFile() //---------------------- { m_nType = MOD_TYPE_NONE; m_dwSongFlags = 0; m_nChannels = 0; m_nMixChannels = 0; m_nSamples = 0; m_nInstruments = 0; m_nPatternNames = 0; m_lpszPatternNames = NULL; m_lpszSongComments = NULL; m_nFreqFactor = m_nTempoFactor = 128; m_nMasterVolume = 128; m_nMinPeriod = 0x20; m_nMaxPeriod = 0x7FFF; m_nRepeatCount = 0; memset(Chn, 0, sizeof(Chn)); memset(ChnMix, 0, sizeof(ChnMix)); memset(Ins, 0, sizeof(Ins)); memset(ChnSettings, 0, sizeof(ChnSettings)); memset(Headers, 0, sizeof(Headers)); memset(Order, 0xFF, sizeof(Order)); memset(Patterns, 0, sizeof(Patterns)); memset(m_szNames, 0, sizeof(m_szNames)); memset(m_MixPlugins, 0, sizeof(m_MixPlugins)); } CSoundFile::~CSoundFile() //----------------------- { Destroy(); } BOOL CSoundFile::Create(LPCBYTE lpStream, DWORD dwMemLength) //---------------------------------------------------------- { int i; m_nType = MOD_TYPE_NONE; m_dwSongFlags = 0; m_nChannels = 0; m_nMixChannels = 0; m_nSamples = 0; m_nInstruments = 0; m_nFreqFactor = m_nTempoFactor = 128; m_nMasterVolume = 128; m_nDefaultGlobalVolume = 256; m_nGlobalVolume = 256; m_nOldGlbVolSlide = 0; m_nDefaultSpeed = 6; m_nDefaultTempo = 125; m_nPatternDelay = 0; m_nFrameDelay = 0; m_nNextRow = 0; m_nRow = 0; m_nNextStartRow = 0; m_nPattern = 0; m_nCurrentPattern = 0; m_nNextPattern = 0; m_nRestartPos = 0; m_nMinPeriod = 16; m_nMaxPeriod = 32767; m_nSongPreAmp = 0x30; m_nPatternNames = 0; m_nMaxOrderPosition = 0; m_lpszPatternNames = NULL; m_lpszSongComments = NULL; memset(Ins, 0, sizeof(Ins)); memset(ChnMix, 0, sizeof(ChnMix)); memset(Chn, 0, sizeof(Chn)); memset(Headers, 0, sizeof(Headers)); memset(Order, 0xFF, sizeof(Order)); memset(Patterns, 0, sizeof(Patterns)); memset(m_szNames, 0, sizeof(m_szNames)); memset(m_MixPlugins, 0, sizeof(m_MixPlugins)); ResetMidiCfg(); for (UINT npt=0; npt=0) && (p[j]<=' ')) p[j--] = 0; while (j>=0) { if (((BYTE)p[j]) < ' ') p[j] = ' '; j--; } } // Adjust channels for (i=0; i 64) ChnSettings[i].nVolume = 64; if (ChnSettings[i].nPan > 256) ChnSettings[i].nPan = 128; Chn[i].nPan = ChnSettings[i].nPan; Chn[i].nGlobalVol = ChnSettings[i].nVolume; Chn[i].dwFlags = ChnSettings[i].dwFlags; Chn[i].nVolume = 256; Chn[i].nCutOff = 0x7F; } // Checking instruments MODINSTRUMENT *pins = Ins; for (i=0; ipSample) { if (pins->nLoopEnd > pins->nLength) pins->nLoopEnd = pins->nLength; if (pins->nLoopStart + 3 >= pins->nLoopEnd) { pins->nLoopStart = 0; pins->nLoopEnd = 0; } if (pins->nSustainEnd > pins->nLength) pins->nSustainEnd = pins->nLength; if (pins->nSustainStart + 3 >= pins->nSustainEnd) { pins->nSustainStart = 0; pins->nSustainEnd = 0; } } else { pins->nLength = 0; pins->nLoopStart = 0; pins->nLoopEnd = 0; pins->nSustainStart = 0; pins->nSustainEnd = 0; } if (!pins->nLoopEnd) pins->uFlags &= ~CHN_LOOP; if (!pins->nSustainEnd) pins->uFlags &= ~CHN_SUSTAINLOOP; if (pins->nGlobalVol > 64) pins->nGlobalVol = 64; } // Check invalid instruments while ((m_nInstruments > 0) && (!Headers[m_nInstruments])) m_nInstruments--; // Set default values if (m_nSongPreAmp < 0x20) m_nSongPreAmp = 0x20; if (m_nDefaultTempo < 32) m_nDefaultTempo = 125; if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; m_nMusicSpeed = m_nDefaultSpeed; m_nMusicTempo = m_nDefaultTempo; m_nGlobalVolume = m_nDefaultGlobalVolume; m_nNextPattern = 0; m_nCurrentPattern = 0; m_nPattern = 0; m_nBufferCount = 0; m_nTickCount = m_nMusicSpeed; m_nNextRow = 0; m_nRow = 0; m_nNextStartRow = 0; if ((m_nRestartPos >= MAX_ORDERS) || (Order[m_nRestartPos] >= MAX_PATTERNS)) m_nRestartPos = 0; // Load plugins if (gpMixPluginCreateProc) { for (UINT iPlug=0; iPlugRestoreAllParameters(); } } } } if (m_nType) { UINT maxpreamp = 0x10+(m_nChannels*8); if (maxpreamp > 100) maxpreamp = 100; if (m_nSongPreAmp > maxpreamp) m_nSongPreAmp = maxpreamp; return TRUE; } return FALSE; } BOOL CSoundFile::Destroy() //------------------------ { int i; for (i=0; ipSample) { FreeSample(pins->pSample); pins->pSample = NULL; } } for (i=0; iRelease(); m_MixPlugins[i].pMixPlugin = NULL; } } m_nType = MOD_TYPE_NONE; m_nChannels = m_nSamples = m_nInstruments = 0; return TRUE; } ////////////////////////////////////////////////////////////////////////// // Memory Allocation MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) //------------------------------------------------------------ { MODCOMMAND *p = new MODCOMMAND[rows*nchns]; if (p) memset(p, 0, rows*nchns*sizeof(MODCOMMAND)); return p; } void CSoundFile::FreePattern(LPVOID pat) //-------------------------------------- { if (pat) delete [] (signed char*)pat; } signed char* CSoundFile::AllocateSample(UINT nbytes) //------------------------------------------- { signed char * p = (signed char *)GlobalAllocPtr(GHND, (nbytes+39) & ~7); if (p) p += 16; return p; } void CSoundFile::FreeSample(LPVOID p) //----------------------------------- { if (p) { GlobalFreePtr(((LPSTR)p)-16); } } ////////////////////////////////////////////////////////////////////////// // Misc functions void CSoundFile::ResetMidiCfg() //----------------------------- { memset(&m_MidiCfg, 0, sizeof(m_MidiCfg)); lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_START*32], "FF"); lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_STOP*32], "FC"); lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_NOTEON*32], "9c n v"); lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_NOTEOFF*32], "9c n 0"); lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM*32], "Cc p"); lstrcpy(&m_MidiCfg.szMidiSFXExt[0], "F0F000z"); for (int iz=0; iz<16; iz++) wsprintf(&m_MidiCfg.szMidiZXXExt[iz*32], "F0F001%02X", iz*8); } UINT CSoundFile::GetNumChannels() const //------------------------------------- { UINT n = 0; for (UINT i=0; i 1) && (s)) s[1] = '\x0A'; while ((*p) && (i+2 < len)) { BYTE c = (BYTE)*p++; if ((c == 0x0D) || ((c == ' ') && (ln >= linesize))) { if (s) { s[i++] = '\x0D'; s[i++] = '\x0A'; } else i+= 2; ln=0; } else if (c >= 0x20) { if (s) s[i++] = c; else i++; ln++; } } if (s) s[i] = 0; return i; } UINT CSoundFile::GetRawSongComments(LPSTR s, UINT len, UINT linesize) //------------------------------------------------------------------- { LPCSTR p = m_lpszSongComments; if (!p) return 0; UINT i = 0, ln=0; while ((*p) && (i < len-1)) { BYTE c = (BYTE)*p++; if ((c == 0x0D) || (c == 0x0A)) { if (ln) { while (ln < linesize) { if (s) s[i] = ' '; i++; ln++; } ln = 0; } } else if ((c == ' ') && (!ln)) { UINT k=0; while ((p[k]) && (p[k] >= ' ')) k++; if (k <= linesize) { if (s) s[i] = ' '; i++; ln++; } } else { if (s) s[i] = c; i++; ln++; if (ln == linesize) ln = 0; } } if (ln) { while ((ln < linesize) && (i < len)) { if (s) s[i] = ' '; i++; ln++; } } if (s) s[i] = 0; return i; } BOOL CSoundFile::SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX) //---------------------------------------------------------------------------- { BOOL bReset = FALSE; DWORD d = gdwSoundSetup & ~SNDMIX_ENABLEMMX; if (bMMX) d |= SNDMIX_ENABLEMMX; if ((gdwMixingFreq != nRate) || (gnBitsPerSample != nBits) || (gnChannels != nChannels) || (d != gdwSoundSetup)) bReset = TRUE; gnChannels = nChannels; gdwSoundSetup = d; gdwMixingFreq = nRate; gnBitsPerSample = nBits; InitPlayer(bReset); return TRUE; } BOOL CSoundFile::SetMixConfig(UINT nStereoSeparation, UINT nMaxMixChannels) //------------------------------------------------------------------------- { if (nMaxMixChannels < 2) return FALSE; m_nMaxMixChannels = nMaxMixChannels; m_nStereoSeparation = nStereoSeparation; return TRUE; } BOOL CSoundFile::SetResamplingMode(UINT nMode) //-------------------------------------------- { DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); switch(nMode) { case SRCMODE_NEAREST: d |= SNDMIX_NORESAMPLING; break; case SRCMODE_LINEAR: break; case SRCMODE_SPLINE: d |= SNDMIX_HQRESAMPLER; break; case SRCMODE_POLYPHASE: d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); break; default: return FALSE; } gdwSoundSetup = d; return TRUE; } BOOL CSoundFile::SetMasterVolume(UINT nVol, BOOL bAdjustAGC) //---------------------------------------------------------- { if (nVol < 1) nVol = 1; if (nVol > 0x200) nVol = 0x200; // x4 maximum if ((nVol < m_nMasterVolume) && (nVol) && (gdwSoundSetup & SNDMIX_AGC) && (bAdjustAGC)) { gnAGC = gnAGC * m_nMasterVolume / nVol; if (gnAGC > AGC_UNITY) gnAGC = AGC_UNITY; } m_nMasterVolume = nVol; return TRUE; } BOOL CSoundFile::SetTempoFactor(UINT nTemp) //---------------------------------------------------------- { if (nTemp < 1) nTemp = 1; if (nTemp > 0x100) nTemp = 0x100; m_nTempoFactor = nTemp; return TRUE; } UINT CSoundFile::GetNumPatterns() const //------------------------------------- { UINT i = 0; while ((i < MAX_ORDERS) && (Order[i] < 0xFF)) i++; return i; } UINT CSoundFile::GetNumInstruments() const //---------------------------------------- { UINT n=0; for (UINT i=0; i= MAX_ORDERS) || (Order[nPattern] >= MAX_PATTERNS) || (nPos >= PatternSize[Order[nPattern]])) { nPos = 0; nPattern = 0; } UINT nRow = nPos; if ((nRow) && (Order[nPattern] < MAX_PATTERNS)) { MODCOMMAND *p = Patterns[Order[nPattern]]; if ((p) && (nRow < PatternSize[Order[nPattern]])) { BOOL bOk = FALSE; while ((!bOk) && (nRow > 0)) { UINT n = nRow * m_nChannels; for (UINT k=0; k= MAX_ORDERS) || (Order[nPos] >= MAX_PATTERNS)) return; for (UINT j=0; j= MAX_PATTERNS) || (!Patterns[nPat])) { m_dwSongFlags &= ~SONG_PATTERNLOOP; } else { if ((nRow < 0) || (nRow >= PatternSize[nPat])) nRow = 0; m_nPattern = nPat; m_nRow = m_nNextRow = nRow; m_nTickCount = m_nMusicSpeed; m_nPatternDelay = 0; m_nFrameDelay = 0; m_nBufferCount = 0; m_dwSongFlags |= SONG_PATTERNLOOP; } } UINT CSoundFile::GetSampleName(UINT nSample,LPSTR s) const //-------------------------------------------------------- { char sztmp[40] = ""; // changed from CHAR if (nSample < MAX_SAMPLES) memcpy(sztmp, m_szNames[nSample], 32); sztmp[31] = 0; if (s) strcpy(s, sztmp); return strlen(sztmp); } UINT CSoundFile::GetInstrumentName(UINT nInstr,LPSTR s) const //----------------------------------------------------------- { char sztmp[40] = ""; // changed from CHAR if ((nInstr >= MAX_INSTRUMENTS) || (!Headers[nInstr])) { if (s) *s = 0; return 0; } INSTRUMENTHEADER *penv = Headers[nInstr]; memcpy(sztmp, penv->name, 32); sztmp[31] = 0; if (s) strcpy(s, sztmp); return strlen(sztmp); } // Flags: // 0 = signed 8-bit PCM data (default) // 1 = unsigned 8-bit PCM data // 2 = 8-bit ADPCM data with linear table // 3 = 4-bit ADPCM data // 4 = 16-bit ADPCM data with linear table // 5 = signed 16-bit PCM data // 6 = unsigned 16-bit PCM data UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR lpMemFile, DWORD dwMemLength) //------------------------------------------------------------------------------ { UINT len = 0, mem; // Disable >2Gb samples,(preventing buffer overflow in AllocateSample) if ((!pIns) || ((int)pIns->nLength < 4) || (!lpMemFile)) return 0; if (pIns->nLength > MAX_SAMPLE_LENGTH) pIns->nLength = MAX_SAMPLE_LENGTH; mem = pIns->nLength+6; pIns->uFlags &= ~(CHN_16BIT|CHN_STEREO); if (nFlags & RSF_16BIT) { mem *= 2; pIns->uFlags |= CHN_16BIT; } if (nFlags & RSF_STEREO) { mem *= 2; pIns->uFlags |= CHN_STEREO; } if ((pIns->pSample = AllocateSample(mem)) == NULL) { pIns->nLength = 0; return 0; } switch(nFlags) { // 1: 8-bit unsigned PCM data case RS_PCM8U: { len = pIns->nLength; if (len > dwMemLength) len = pIns->nLength = dwMemLength; signed char *pSample = pIns->pSample; for (UINT j=0; jnLength; if (len > dwMemLength) break; signed char *pSample = pIns->pSample; const signed char *p = (const signed char *)lpMemFile; int delta = 0; for (UINT j=0; jnLength + 1) / 2; if (len > dwMemLength - 16 || dwMemLength < 16) break; memcpy(CompressionTable, lpMemFile, 16); lpMemFile += 16; signed char *pSample = pIns->pSample; signed char delta = 0; for (UINT j=0; j> 4); delta = (signed char)GetDeltaValue((int)delta, b0); pSample[0] = delta; delta = (signed char)GetDeltaValue((int)delta, b1); pSample[1] = delta; pSample += 2; } len += 16; } break; // 4: 16-bit ADPCM data with linear table case RS_PCM16D: { len = pIns->nLength * 2; if (len > dwMemLength) break; int16_t *pSample = (int16_t *)pIns->pSample; int16_t *p = (int16_t *)lpMemFile; int delta16 = 0; for (UINT j=0; jnLength * 2; if (len <= dwMemLength) memcpy(pIns->pSample, lpMemFile, len); int16_t *pSample = (int16_t *)pIns->pSample; for (UINT j=0; jnLength * 2; if (len > dwMemLength) len = dwMemLength & ~1; if (len > 1) { signed char *pSample = (signed char *)pIns->pSample; signed char *pSrc = (signed char *)lpMemFile; for (UINT j=0; jnLength * 2; if (len > dwMemLength) break; int16_t *pSample = (int16_t *)pIns->pSample; int16_t *pSrc = (int16_t *)lpMemFile; for (UINT j=0; jnLength * 2; if (len*2 <= dwMemLength) { signed char *pSample = (signed char *)pIns->pSample; signed char *pSrc = (signed char *)lpMemFile; for (UINT j=0; jnLength; signed char *psrc = (signed char *)lpMemFile; signed char *pSample = (signed char *)pIns->pSample; if (len*2 > dwMemLength) break; for (UINT j=0; jnLength; int16_t *psrc = (int16_t *)lpMemFile; int16_t *pSample = (int16_t *)pIns->pSample; if (len*4 > dwMemLength) break; for (UINT j=0; jnLength; if (len*2 > dwMemLength) len = dwMemLength >> 1; LPBYTE psrc = (LPBYTE)lpMemFile; LPBYTE pSample = (LPBYTE)pIns->pSample; for (UINT j=0; jnLength; if (len*4 > dwMemLength) len = dwMemLength >> 2; int16_t *psrc = (int16_t *)lpMemFile; int16_t *pSample = (int16_t *)pIns->pSample; for (UINT j=0; jnLength * 2; if (len > dwMemLength) break; int8_t *pSample = (int8_t *)pIns->pSample; int8_t delta8 = 0; for (UINT j=0; jpSample; for (UINT j=0; jnLength; if (len > dwMemLength) len = pIns->nLength = dwMemLength; memcpy(pIns->pSample, lpMemFile, len); } if (len > dwMemLength) { if (pIns->pSample) { pIns->nLength = 0; FreeSample(pIns->pSample); pIns->pSample = NULL; } return 0; } AdjustSampleLoop(pIns); return len; } void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) //---------------------------------------------------- { if (!pIns->pSample) return; if (pIns->nLength > MAX_SAMPLE_LENGTH) pIns->nLength = MAX_SAMPLE_LENGTH; if (pIns->nLoopEnd > pIns->nLength) pIns->nLoopEnd = pIns->nLength; if (pIns->nLoopStart > pIns->nLength+2) pIns->nLoopStart = pIns->nLength+2; if (pIns->nLoopStart+2 >= pIns->nLoopEnd) { pIns->nLoopStart = pIns->nLoopEnd = 0; pIns->uFlags &= ~CHN_LOOP; } UINT len = pIns->nLength; if (pIns->uFlags & CHN_16BIT) { int16_t *pSample = (int16_t *)pIns->pSample; // Adjust end of sample if (pIns->uFlags & CHN_STEREO) { pSample[len*2+6] = pSample[len*2+4] = pSample[len*2+2] = pSample[len*2] = 0; pSample[len*2+7] = pSample[len*2+5] = pSample[len*2+3] = pSample[len*2+1] = 0; } else { pSample[len+4] = pSample[len+3] = pSample[len+2] = pSample[len+1] = pSample[len] = 0; } if ((pIns->uFlags & (CHN_LOOP|CHN_PINGPONGLOOP|CHN_STEREO)) == CHN_LOOP) { // Fix bad loops if ((pIns->nLoopEnd+3 >= pIns->nLength) || (m_nType & MOD_TYPE_S3M)) { pSample[pIns->nLoopEnd] = pSample[pIns->nLoopStart]; pSample[pIns->nLoopEnd+1] = pSample[pIns->nLoopStart+1]; pSample[pIns->nLoopEnd+2] = pSample[pIns->nLoopStart+2]; pSample[pIns->nLoopEnd+3] = pSample[pIns->nLoopStart+3]; pSample[pIns->nLoopEnd+4] = pSample[pIns->nLoopStart+4]; } } } else { signed char *pSample = pIns->pSample; // Crappy samples (except chiptunes) ? if ((pIns->nLength > 0x100) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_S3M)) && (!(pIns->uFlags & CHN_STEREO))) { int smpend = pSample[pIns->nLength-1], smpfix = 0, kscan; for (kscan=pIns->nLength-1; kscan>0; kscan--) { smpfix = pSample[kscan-1]; if (smpfix != smpend) break; } int delta = smpfix - smpend; if (((!(pIns->uFlags & CHN_LOOP)) || (kscan > (int)pIns->nLoopEnd)) && ((delta < -8) || (delta > 8))) { while (kscan<(int)pIns->nLength) { if (!(kscan & 7)) { if (smpfix > 0) smpfix--; if (smpfix < 0) smpfix++; } pSample[kscan] = (signed char)smpfix; kscan++; } } } // Adjust end of sample if (pIns->uFlags & CHN_STEREO) { pSample[len*2+6] = pSample[len*2+4] = pSample[len*2+2] = pSample[len*2] = 0; pSample[len*2+7] = pSample[len*2+5] = pSample[len*2+3] = pSample[len*2+1] = 0; } else { pSample[len+4] = pSample[len+3] = pSample[len+2] = pSample[len+1] = pSample[len] = 0; } if ((pIns->uFlags & (CHN_LOOP|CHN_PINGPONGLOOP|CHN_STEREO)) == CHN_LOOP) { if ((pIns->nLoopEnd+3 >= pIns->nLength) || (m_nType & (MOD_TYPE_MOD|MOD_TYPE_S3M))) { pSample[pIns->nLoopEnd] = pSample[pIns->nLoopStart]; pSample[pIns->nLoopEnd+1] = pSample[pIns->nLoopStart+1]; pSample[pIns->nLoopEnd+2] = pSample[pIns->nLoopStart+2]; pSample[pIns->nLoopEnd+3] = pSample[pIns->nLoopStart+3]; pSample[pIns->nLoopEnd+4] = pSample[pIns->nLoopStart+4]; } } } } ///////////////////////////////////////////////////////////// // Transpose <-> Frequency conversions // returns 8363*2^((transp*128+ftune)/(12*128)) DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) //----------------------------------------------------------- { return (DWORD)(8363*pow(2, (double)(transp*128+ftune)/(1536))); } // returns 12*128*log2(freq/8363) int CSoundFile::FrequencyToTranspose(DWORD freq) //---------------------------------------------- { return int(1536*(log(freq/8363.0)/log(2.0))); } void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) //-------------------------------------------------------- { int f2t = FrequencyToTranspose(psmp->nC4Speed); int transp = f2t >> 7; int ftune = f2t & 0x7F; if (ftune > 80) { transp++; ftune -= 128; } if (transp > 127) transp = 127; if (transp < -127) transp = -127; psmp->RelativeTone = transp; psmp->nFineTune = ftune; } void CSoundFile::CheckCPUUsage(UINT nCPU) //--------------------------------------- { if (nCPU > 100) nCPU = 100; gnCPUUsage = nCPU; if (nCPU < 90) { m_dwSongFlags &= ~SONG_CPUVERYHIGH; } else if ((m_dwSongFlags & SONG_CPUVERYHIGH) && (nCPU >= 94)) { UINT i=MAX_CHANNELS; while (i >= 8) { i--; if (Chn[i].nLength) { Chn[i].nLength = Chn[i].nPos = 0; nCPU -= 2; if (nCPU < 94) break; } } } else if (nCPU > 90) { m_dwSongFlags |= SONG_CPUVERYHIGH; } } BOOL CSoundFile::SetPatternName(UINT nPat, LPCSTR lpszName) //--------------------------------------------------------- { char szName[MAX_PATTERNNAME] = ""; // check input arguments if (nPat >= MAX_PATTERNS) return FALSE; if (lpszName == NULL) return(FALSE); if (lpszName) lstrcpyn(szName, lpszName, MAX_PATTERNNAME-1); szName[MAX_PATTERNNAME-1] = 0; if (!m_lpszPatternNames) m_nPatternNames = 0; if (nPat >= m_nPatternNames) { if (!lpszName[0]) return TRUE; UINT len = (nPat+1)*MAX_PATTERNNAME; char *p = new char[len]; if (!p) return FALSE; memset(p, 0, len); if (m_lpszPatternNames) { memcpy(p, m_lpszPatternNames, m_nPatternNames * MAX_PATTERNNAME); delete [] m_lpszPatternNames; m_lpszPatternNames = NULL; } m_lpszPatternNames = p; m_nPatternNames = nPat + 1; } memcpy(m_lpszPatternNames + nPat * MAX_PATTERNNAME, szName, MAX_PATTERNNAME); return TRUE; } BOOL CSoundFile::GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize) const //--------------------------------------------------------------------------- { if ((!lpszName) || (!cbSize)) return FALSE; lpszName[0] = 0; if (cbSize > MAX_PATTERNNAME) cbSize = MAX_PATTERNNAME; if ((m_lpszPatternNames) && (nPat < m_nPatternNames)) { memcpy(lpszName, m_lpszPatternNames + nPat * MAX_PATTERNNAME, cbSize); lpszName[cbSize-1] = 0; return TRUE; } return FALSE; } UINT CSoundFile::DetectUnusedSamples(BOOL *pbIns) //----------------------------------------------- { UINT nExt = 0; if (!pbIns) return 0; if (m_nInstruments) { memset(pbIns, 0, MAX_SAMPLES * sizeof(BOOL)); for (UINT ipat=0; ipatnote) && (p->note <= NOTE_MAX)) { if ((p->instr) && (p->instr < MAX_INSTRUMENTS)) { INSTRUMENTHEADER *penv = Headers[p->instr]; if (penv) { UINT n = penv->Keyboard[p->note-1]; if (n < MAX_SAMPLES) pbIns[n] = TRUE; } } else { for (UINT k=1; k<=m_nInstruments; k++) { INSTRUMENTHEADER *penv = Headers[k]; if (penv) { UINT n = penv->Keyboard[p->note-1]; if (n < MAX_SAMPLES) pbIns[n] = TRUE; } } } } } } } for (UINT ichk=1; ichk<=m_nSamples; ichk++) { if ((!pbIns[ichk]) && (Ins[ichk].pSample)) nExt++; } } return nExt; } BOOL CSoundFile::RemoveSelectedSamples(BOOL *pbIns) //------------------------------------------------- { if (!pbIns) return FALSE; for (UINT j=1; j 1)) m_nSamples--; } } return TRUE; } BOOL CSoundFile::DestroySample(UINT nSample) //------------------------------------------ { if ((!nSample) || (nSample >= MAX_SAMPLES)) return FALSE; if (!Ins[nSample].pSample) return TRUE; MODINSTRUMENT *pins = &Ins[nSample]; signed char *pSample = pins->pSample; pins->pSample = NULL; pins->nLength = 0; pins->uFlags &= ~(CHN_16BIT); for (UINT i=0; i, * Adam Goode (endian and char fixes for PPC) */ #ifndef MP_SNDFILE_H #define MP_SNDFILE_H #ifdef UNDER_CE int _strnicmp(const char *str1,const char *str2, int n); #endif #ifndef LPCBYTE typedef const BYTE * LPCBYTE; #endif #define MOD_AMIGAC2 0x1AB #define MAX_SAMPLE_LENGTH 16000000 #define MAX_SAMPLE_RATE 192000 #define MAX_ORDERS 256 #define MAX_PATTERNS 240 #define MAX_SAMPLES 240 #define MAX_INSTRUMENTS MAX_SAMPLES #define MAX_CHANNELS 128 #define MAX_BASECHANNELS 64 #define MAX_ENVPOINTS 32 #define MIN_PERIOD 0x0020 #define MAX_PERIOD 0xFFFF #define MAX_PATTERNNAME 32 #define MAX_CHANNELNAME 20 #define MAX_INFONAME 80 #define MAX_EQ_BANDS 6 #define MAX_MIXPLUGINS 8 #define MOD_TYPE_NONE 0x00 #define MOD_TYPE_MOD 0x01 #define MOD_TYPE_S3M 0x02 #define MOD_TYPE_XM 0x04 #define MOD_TYPE_MED 0x08 #define MOD_TYPE_MTM 0x10 #define MOD_TYPE_IT 0x20 #define MOD_TYPE_669 0x40 #define MOD_TYPE_ULT 0x80 #define MOD_TYPE_STM 0x100 #define MOD_TYPE_FAR 0x200 #define MOD_TYPE_WAV 0x400 #define MOD_TYPE_AMF 0x800 #define MOD_TYPE_AMS 0x1000 #define MOD_TYPE_DSM 0x2000 #define MOD_TYPE_MDL 0x4000 #define MOD_TYPE_OKT 0x8000 #define MOD_TYPE_MID 0x10000 #define MOD_TYPE_DMF 0x20000 #define MOD_TYPE_PTM 0x40000 #define MOD_TYPE_DBM 0x80000 #define MOD_TYPE_MT2 0x100000 #define MOD_TYPE_AMF0 0x200000 #define MOD_TYPE_PSM 0x400000 #define MOD_TYPE_J2B 0x800000 #define MOD_TYPE_ABC 0x1000000 #define MOD_TYPE_PAT 0x2000000 #define MOD_TYPE_UMX 0x80000000 // Fake type #define MAX_MODTYPE 24 // Channel flags: // Bits 0-7: Sample Flags #define CHN_16BIT 0x01 #define CHN_LOOP 0x02 #define CHN_PINGPONGLOOP 0x04 #define CHN_SUSTAINLOOP 0x08 #define CHN_PINGPONGSUSTAIN 0x10 #define CHN_PANNING 0x20 #define CHN_STEREO 0x40 #define CHN_PINGPONGFLAG 0x80 // Bits 8-31: Channel Flags #define CHN_MUTE 0x100 #define CHN_KEYOFF 0x200 #define CHN_NOTEFADE 0x400 #define CHN_SURROUND 0x800 #define CHN_NOIDO 0x1000 #define CHN_HQSRC 0x2000 #define CHN_FILTER 0x4000 #define CHN_VOLUMERAMP 0x8000 #define CHN_VIBRATO 0x10000 #define CHN_TREMOLO 0x20000 #define CHN_PANBRELLO 0x40000 #define CHN_PORTAMENTO 0x80000 #define CHN_GLISSANDO 0x100000 #define CHN_VOLENV 0x200000 #define CHN_PANENV 0x400000 #define CHN_PITCHENV 0x800000 #define CHN_FASTVOLRAMP 0x1000000 #define CHN_EXTRALOUD 0x2000000 #define CHN_REVERB 0x4000000 #define CHN_NOREVERB 0x8000000 #define ENV_VOLUME 0x0001 #define ENV_VOLSUSTAIN 0x0002 #define ENV_VOLLOOP 0x0004 #define ENV_PANNING 0x0008 #define ENV_PANSUSTAIN 0x0010 #define ENV_PANLOOP 0x0020 #define ENV_PITCH 0x0040 #define ENV_PITCHSUSTAIN 0x0080 #define ENV_PITCHLOOP 0x0100 #define ENV_SETPANNING 0x0200 #define ENV_FILTER 0x0400 #define ENV_VOLCARRY 0x0800 #define ENV_PANCARRY 0x1000 #define ENV_PITCHCARRY 0x2000 #define CMD_NONE 0 #define CMD_ARPEGGIO 1 #define CMD_PORTAMENTOUP 2 #define CMD_PORTAMENTODOWN 3 #define CMD_TONEPORTAMENTO 4 #define CMD_VIBRATO 5 #define CMD_TONEPORTAVOL 6 #define CMD_VIBRATOVOL 7 #define CMD_TREMOLO 8 #define CMD_PANNING8 9 #define CMD_OFFSET 10 #define CMD_VOLUMESLIDE 11 #define CMD_POSITIONJUMP 12 #define CMD_VOLUME 13 #define CMD_PATTERNBREAK 14 #define CMD_RETRIG 15 #define CMD_SPEED 16 #define CMD_TEMPO 17 #define CMD_TREMOR 18 #define CMD_MODCMDEX 19 #define CMD_S3MCMDEX 20 #define CMD_CHANNELVOLUME 21 #define CMD_CHANNELVOLSLIDE 22 #define CMD_GLOBALVOLUME 23 #define CMD_GLOBALVOLSLIDE 24 #define CMD_KEYOFF 25 #define CMD_FINEVIBRATO 26 #define CMD_PANBRELLO 27 #define CMD_XFINEPORTAUPDOWN 28 #define CMD_PANNINGSLIDE 29 #define CMD_SETENVPOSITION 30 #define CMD_MIDI 31 // Volume Column commands #define VOLCMD_VOLUME 1 #define VOLCMD_PANNING 2 #define VOLCMD_VOLSLIDEUP 3 #define VOLCMD_VOLSLIDEDOWN 4 #define VOLCMD_FINEVOLUP 5 #define VOLCMD_FINEVOLDOWN 6 #define VOLCMD_VIBRATOSPEED 7 #define VOLCMD_VIBRATO 8 #define VOLCMD_PANSLIDELEFT 9 #define VOLCMD_PANSLIDERIGHT 10 #define VOLCMD_TONEPORTAMENTO 11 #define VOLCMD_PORTAUP 12 #define VOLCMD_PORTADOWN 13 #define RSF_16BIT 0x04 #define RSF_STEREO 0x08 #define RS_PCM8S 0 // 8-bit signed #define RS_PCM8U 1 // 8-bit unsigned #define RS_PCM8D 2 // 8-bit delta values #define RS_ADPCM4 3 // 4-bit ADPCM-packed #define RS_PCM16D 4 // 16-bit delta values #define RS_PCM16S 5 // 16-bit signed #define RS_PCM16U 6 // 16-bit unsigned #define RS_PCM16M 7 // 16-bit motorola order #define RS_STPCM8S (RS_PCM8S|RSF_STEREO) // stereo 8-bit signed #define RS_STPCM8U (RS_PCM8U|RSF_STEREO) // stereo 8-bit unsigned #define RS_STPCM8D (RS_PCM8D|RSF_STEREO) // stereo 8-bit delta values #define RS_STPCM16S (RS_PCM16S|RSF_STEREO) // stereo 16-bit signed #define RS_STPCM16U (RS_PCM16U|RSF_STEREO) // stereo 16-bit unsigned #define RS_STPCM16D (RS_PCM16D|RSF_STEREO) // stereo 16-bit delta values #define RS_STPCM16M (RS_PCM16M|RSF_STEREO) // stereo 16-bit signed big endian // IT 2.14 compressed samples #define RS_IT2148 0x10 #define RS_IT21416 0x14 #define RS_IT2158 0x12 #define RS_IT21516 0x16 // AMS Packed Samples #define RS_AMS8 0x11 #define RS_AMS16 0x15 // DMF Huffman compression #define RS_DMF8 0x13 #define RS_DMF16 0x17 // MDL Huffman compression #define RS_MDL8 0x20 #define RS_MDL16 0x24 #define RS_PTM8DTO16 0x25 // Stereo Interleaved Samples #define RS_STIPCM8S (RS_PCM8S|0x40|RSF_STEREO) // stereo 8-bit signed #define RS_STIPCM8U (RS_PCM8U|0x40|RSF_STEREO) // stereo 8-bit unsigned #define RS_STIPCM16S (RS_PCM16S|0x40|RSF_STEREO) // stereo 16-bit signed #define RS_STIPCM16U (RS_PCM16U|0x40|RSF_STEREO) // stereo 16-bit unsigned #define RS_STIPCM16M (RS_PCM16M|0x40|RSF_STEREO) // stereo 16-bit signed big endian // 24-bit signed #define RS_PCM24S (RS_PCM16S|0x80) // mono 24-bit signed #define RS_STIPCM24S (RS_PCM16S|0x80|RSF_STEREO) // stereo 24-bit signed #define RS_PCM32S (RS_PCM16S|0xC0) // mono 24-bit signed #define RS_STIPCM32S (RS_PCM16S|0xC0|RSF_STEREO) // stereo 24-bit signed // NNA types #define NNA_NOTECUT 0 #define NNA_CONTINUE 1 #define NNA_NOTEOFF 2 #define NNA_NOTEFADE 3 // DCT types #define DCT_NONE 0 #define DCT_NOTE 1 #define DCT_SAMPLE 2 #define DCT_INSTRUMENT 3 // DNA types #define DNA_NOTECUT 0 #define DNA_NOTEOFF 1 #define DNA_NOTEFADE 2 // Mixer Hardware-Dependent features #define SYSMIX_ENABLEMMX 0x01 #define SYSMIX_WINDOWSNT 0x02 #define SYSMIX_SLOWCPU 0x04 #define SYSMIX_FASTCPU 0x08 // Module flags #define SONG_EMBEDMIDICFG 0x0001 #define SONG_FASTVOLSLIDES 0x0002 #define SONG_ITOLDEFFECTS 0x0004 #define SONG_ITCOMPATMODE 0x0008 #define SONG_LINEARSLIDES 0x0010 #define SONG_PATTERNLOOP 0x0020 #define SONG_STEP 0x0040 #define SONG_PAUSED 0x0080 #define SONG_FADINGSONG 0x0100 #define SONG_ENDREACHED 0x0200 #define SONG_GLOBALFADE 0x0400 #define SONG_CPUVERYHIGH 0x0800 #define SONG_FIRSTTICK 0x1000 #define SONG_MPTFILTERMODE 0x2000 #define SONG_SURROUNDPAN 0x4000 #define SONG_EXFILTERRANGE 0x8000 #define SONG_AMIGALIMITS 0x10000 // Global Options (Renderer) #define SNDMIX_REVERSESTEREO 0x0001 #define SNDMIX_NOISEREDUCTION 0x0002 #define SNDMIX_AGC 0x0004 #define SNDMIX_NORESAMPLING 0x0008 #define SNDMIX_HQRESAMPLER 0x0010 #define SNDMIX_MEGABASS 0x0020 #define SNDMIX_SURROUND 0x0040 #define SNDMIX_REVERB 0x0080 #define SNDMIX_EQ 0x0100 #define SNDMIX_SOFTPANNING 0x0200 #define SNDMIX_ULTRAHQSRCMODE 0x0400 // Misc Flags (can safely be turned on or off) #define SNDMIX_DIRECTTODISK 0x10000 #define SNDMIX_ENABLEMMX 0x20000 #define SNDMIX_NOBACKWARDJUMPS 0x40000 #define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader // Reverb Types (GM2 Presets) enum { REVERBTYPE_SMALLROOM, REVERBTYPE_MEDIUMROOM, REVERBTYPE_LARGEROOM, REVERBTYPE_SMALLHALL, REVERBTYPE_MEDIUMHALL, REVERBTYPE_LARGEHALL, NUM_REVERBTYPES }; enum { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_SPLINE, SRCMODE_POLYPHASE, NUM_SRC_MODES }; // Sample Struct typedef struct _MODINSTRUMENT { UINT nLength,nLoopStart,nLoopEnd; UINT nSustainStart, nSustainEnd; signed char *pSample; UINT nC4Speed; WORD nPan; WORD nVolume; WORD nGlobalVol; WORD uFlags; signed char RelativeTone; signed char nFineTune; BYTE nVibType; BYTE nVibSweep; BYTE nVibDepth; BYTE nVibRate; CHAR name[22]; } MODINSTRUMENT; // Instrument Struct typedef struct _INSTRUMENTHEADER { UINT nFadeOut; DWORD dwFlags; WORD nGlobalVol; WORD nPan; WORD VolPoints[MAX_ENVPOINTS]; WORD PanPoints[MAX_ENVPOINTS]; WORD PitchPoints[MAX_ENVPOINTS]; BYTE VolEnv[MAX_ENVPOINTS]; BYTE PanEnv[MAX_ENVPOINTS]; BYTE PitchEnv[MAX_ENVPOINTS]; BYTE Keyboard[128]; BYTE NoteMap[128]; BYTE nVolEnv; BYTE nPanEnv; BYTE nPitchEnv; BYTE nVolLoopStart; BYTE nVolLoopEnd; BYTE nVolSustainBegin; BYTE nVolSustainEnd; BYTE nPanLoopStart; BYTE nPanLoopEnd; BYTE nPanSustainBegin; BYTE nPanSustainEnd; BYTE nPitchLoopStart; BYTE nPitchLoopEnd; BYTE nPitchSustainBegin; BYTE nPitchSustainEnd; BYTE nNNA; BYTE nDCT; BYTE nDNA; BYTE nPanSwing; BYTE nVolSwing; BYTE nIFC; BYTE nIFR; WORD wMidiBank; BYTE nMidiProgram; BYTE nMidiChannel; BYTE nMidiDrumKey; signed char nPPS; unsigned char nPPC; CHAR name[32]; CHAR filename[12]; } INSTRUMENTHEADER; // Channel Struct typedef struct _MODCHANNEL { // First 32-bytes: Most used mixing information: don't change it signed char * pCurrentSample; DWORD nPos; DWORD nPosLo; // actually 16-bit LONG nInc; // 16.16 LONG nRightVol; LONG nLeftVol; LONG nRightRamp; LONG nLeftRamp; // 2nd cache line DWORD nLength; DWORD dwFlags; DWORD nLoopStart; DWORD nLoopEnd; LONG nRampRightVol; LONG nRampLeftVol; LONG nFilter_Y1, nFilter_Y2, nFilter_Y3, nFilter_Y4; LONG nFilter_A0, nFilter_B0, nFilter_B1; LONG nROfs, nLOfs; LONG nRampLength; // Information not used in the mixer signed char * pSample; LONG nNewRightVol, nNewLeftVol; LONG nRealVolume, nRealPan; LONG nVolume, nPan, nFadeOutVol; LONG nPeriod, nC4Speed, nPortamentoDest; INSTRUMENTHEADER *pHeader; MODINSTRUMENT *pInstrument; DWORD nVolEnvPosition, nPanEnvPosition, nPitchEnvPosition; DWORD nMasterChn, nVUMeter; LONG nGlobalVol, nInsVol; LONG nFineTune, nTranspose; LONG nPortamentoSlide, nAutoVibDepth; UINT nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; // 16-bit members signed short nVolSwing, nPanSwing; // 8-bit members BYTE nNote, nNNA; BYTE nNewNote, nNewIns, nCommand, nArpeggio; BYTE nOldVolumeSlide, nOldFineVolUpDown; BYTE nOldPortaUpDown, nOldFinePortaUpDown; BYTE nOldPanSlide, nOldChnVolSlide; BYTE nVibratoType, nVibratoSpeed, nVibratoDepth; BYTE nTremoloType, nTremoloSpeed, nTremoloDepth; BYTE nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; BYTE nOldCmdEx, nOldVolParam, nOldTempo; BYTE nOldOffset, nOldHiOffset; BYTE nCutOff, nResonance; BYTE nRetrigCount, nRetrigParam; BYTE nTremorCount, nTremorParam; BYTE nPatternLoop, nPatternLoopCount; BYTE nRowNote, nRowInstr; BYTE nRowVolCmd, nRowVolume; BYTE nRowCommand, nRowParam; BYTE nLeftVU, nRightVU; BYTE nActiveMacro, nPadding; } MODCHANNEL; typedef struct _MODCHANNELSETTINGS { UINT nPan; UINT nVolume; DWORD dwFlags; UINT nMixPlugin; char szName[MAX_CHANNELNAME]; // changed from CHAR } MODCHANNELSETTINGS; typedef struct _MODCOMMAND { BYTE note; BYTE instr; BYTE volcmd; BYTE command; BYTE vol; BYTE param; } MODCOMMAND, *LPMODCOMMAND; //////////////////////////////////////////////////////////////////// // Mix Plugins #define MIXPLUG_MIXREADY 0x01 // Set when cleared class IMixPlugin { public: virtual ~IMixPlugin() {}; virtual int AddRef() = 0; virtual int Release() = 0; virtual void SaveAllParameters() = 0; virtual void RestoreAllParameters() = 0; virtual void Process(float *pOutL, float *pOutR, unsigned long nSamples) = 0; virtual void Init(unsigned long nFreq, int bReset) = 0; virtual void MidiSend(DWORD dwMidiCode) = 0; virtual void MidiCommand(UINT nMidiCh, UINT nMidiProg, UINT note, UINT vol) = 0; }; #define MIXPLUG_INPUTF_MASTEREFFECT 0x01 // Apply to master mix #define MIXPLUG_INPUTF_BYPASS 0x02 // Bypass effect #define MIXPLUG_INPUTF_WETMIX 0x04 // Wet Mix (dry added) typedef struct _SNDMIXPLUGINSTATE { DWORD dwFlags; // MIXPLUG_XXXX LONG nVolDecayL, nVolDecayR; // Buffer click removal int *pMixBuffer; // Stereo effect send buffer float *pOutBufferL; // Temp storage for int -> float conversion float *pOutBufferR; } SNDMIXPLUGINSTATE, *PSNDMIXPLUGINSTATE; typedef struct _SNDMIXPLUGININFO { DWORD dwPluginId1; DWORD dwPluginId2; DWORD dwInputRouting; // MIXPLUG_INPUTF_XXXX DWORD dwOutputRouting; // 0=mix 0x80+=fx DWORD dwReserved[4]; // Reserved for routing info CHAR szName[32]; CHAR szLibraryName[64]; // original DLL name } SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 typedef struct _SNDMIXPLUGIN { IMixPlugin *pMixPlugin; PSNDMIXPLUGINSTATE pMixState; ULONG nPluginDataSize; PVOID pPluginData; SNDMIXPLUGININFO Info; } SNDMIXPLUGIN, *PSNDMIXPLUGIN; typedef BOOL (*PMIXPLUGINCREATEPROC)(PSNDMIXPLUGIN); //////////////////////////////////////////////////////////////////// enum { MIDIOUT_START=0, MIDIOUT_STOP, MIDIOUT_TICK, MIDIOUT_NOTEON, MIDIOUT_NOTEOFF, MIDIOUT_VOLUME, MIDIOUT_PAN, MIDIOUT_BANKSEL, MIDIOUT_PROGRAM, }; typedef struct MODMIDICFG { char szMidiGlb[9*32]; // changed from CHAR char szMidiSFXExt[16*32]; // changed from CHAR char szMidiZXXExt[128*32]; // changed from CHAR } MODMIDICFG, *LPMODMIDICFG; #define NOTE_MAX 120 //Defines maximum notevalue as well as maximum number of notes. typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels //============== class CSoundFile //============== { public: // Static Members static UINT m_nXBassDepth; static UINT m_nXBassRange; static UINT m_nReverbDepth; static UINT m_nReverbDelay; static UINT gnReverbType; static UINT m_nProLogicDepth; static UINT m_nProLogicDelay; static UINT m_nStereoSeparation; static UINT m_nMaxMixChannels; static LONG m_nStreamVolume; static DWORD gdwSysInfo; static DWORD gdwSoundSetup; static DWORD gdwMixingFreq; static DWORD gnBitsPerSample; static DWORD gnChannels; static UINT gnAGC; static UINT gnVolumeRampSamples; static UINT gnVUMeter; static UINT gnCPUUsage; static LPSNDMIXHOOKPROC gpSndMixHook; static PMIXPLUGINCREATEPROC gpMixPluginCreateProc; public: // for Editing MODCHANNEL Chn[MAX_CHANNELS]; // Channels UINT ChnMix[MAX_CHANNELS]; // Channels to be mixed MODINSTRUMENT Ins[MAX_SAMPLES]; // Instruments INSTRUMENTHEADER *Headers[MAX_INSTRUMENTS]; // Instrument Headers MODCHANNELSETTINGS ChnSettings[MAX_BASECHANNELS]; // Channels settings MODCOMMAND *Patterns[MAX_PATTERNS]; // Patterns WORD PatternSize[MAX_PATTERNS]; // Patterns Lengths BYTE Order[MAX_ORDERS]; // Pattern Orders MODMIDICFG m_MidiCfg; // Midi macro config table SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins UINT m_nDefaultSpeed, m_nDefaultTempo, m_nDefaultGlobalVolume; DWORD m_dwSongFlags; // Song flags SONG_XXXX UINT m_nChannels, m_nMixChannels, m_nMixStat, m_nBufferCount; UINT m_nType, m_nSamples, m_nInstruments; UINT m_nTickCount, m_nTotalCount, m_nPatternDelay, m_nFrameDelay; UINT m_nMusicSpeed, m_nMusicTempo; UINT m_nNextRow, m_nRow, m_nNextStartRow; UINT m_nPattern,m_nCurrentPattern,m_nNextPattern,m_nRestartPos; UINT m_nMasterVolume, m_nGlobalVolume, m_nSongPreAmp; UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide; LONG m_nMinPeriod, m_nMaxPeriod, m_nRepeatCount, m_nInitialRepeatCount; DWORD m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples; UINT m_nMaxOrderPosition; UINT m_nPatternNames; LPSTR m_lpszSongComments, m_lpszPatternNames; char m_szNames[MAX_INSTRUMENTS][32]; // changed from CHAR CHAR CompressionTable[16]; public: CSoundFile(); ~CSoundFile(); public: BOOL Create(LPCBYTE lpStream, DWORD dwMemLength=0); BOOL Destroy(); UINT GetType() const { return m_nType; } UINT GetNumChannels() const; UINT GetLogicalChannels() const { return m_nChannels; } BOOL SetMasterVolume(UINT nVol, BOOL bAdjustAGC=FALSE); UINT GetMasterVolume() const { return m_nMasterVolume; } BOOL SetTempoFactor(UINT nTemp); UINT GetTempoFactor() const { return m_nTempoFactor; } UINT GetNumPatterns() const; UINT GetNumInstruments() const; UINT GetNumSamples() const { return m_nSamples; } UINT GetCurrentPos() const; UINT GetCurrentPattern() const { return m_nPattern; } UINT GetCurrentOrder() const { return m_nCurrentPattern; } UINT GetSongComments(LPSTR s, UINT cbsize, UINT linesize=32); UINT GetRawSongComments(LPSTR s, UINT cbsize, UINT linesize=32); UINT GetMaxPosition() const; void SetCurrentPos(UINT nPos); void SetCurrentOrder(UINT nOrder); void GetTitle(LPSTR s) const { lstrcpyn(s,m_szNames[0],32); } LPCSTR GetTitle() const { return m_szNames[0]; } UINT GetSampleName(UINT nSample,LPSTR s=NULL) const; UINT GetInstrumentName(UINT nInstr,LPSTR s=NULL) const; UINT GetMusicSpeed() const { return m_nMusicSpeed; } UINT GetMusicTempo() const { return m_nMusicTempo; } DWORD GetLength(BOOL bAdjust, BOOL bTotal=FALSE); DWORD GetSongTime() { return GetLength(FALSE, TRUE); } void SetRepeatCount(int n) { m_nRepeatCount = n; m_nInitialRepeatCount = n; } int GetRepeatCount() const { return m_nRepeatCount; } BOOL IsPaused() const { return (m_dwSongFlags & SONG_PAUSED) ? TRUE : FALSE; } void LoopPattern(int nPat, int nRow=0); void CheckCPUUsage(UINT nCPU); BOOL SetPatternName(UINT nPat, LPCSTR lpszName); BOOL GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize=MAX_PATTERNNAME) const; // Module Loaders BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength); // MOD Convert function UINT GetBestSaveFormat() const; UINT GetSaveFormats() const; void ConvertModCommand(MODCOMMAND *) const; void S3MConvert(MODCOMMAND *m, BOOL bIT) const; void S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const; WORD ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const; public: // Real-time sound functions VOID ResetChannels(); UINT Read(LPVOID lpBuffer, UINT cbBuffer); UINT CreateStereoMix(int count); BOOL FadeSong(UINT msec); BOOL GlobalFadeSong(UINT msec); UINT GetTotalTickCount() const { return m_nTotalCount; } VOID ResetTotalTickCount() { m_nTotalCount = 0; } public: // Mixer Config static BOOL InitPlayer(BOOL bReset=FALSE); static BOOL SetMixConfig(UINT nStereoSeparation, UINT nMaxMixChannels); static BOOL SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX=FALSE); static BOOL SetResamplingMode(UINT nMode); // SRCMODE_XXXX static BOOL IsStereo() { return (gnChannels > 1) ? TRUE : FALSE; } static DWORD GetSampleRate() { return gdwMixingFreq; } static DWORD GetBitsPerSample() { return gnBitsPerSample; } static DWORD InitSysInfo(); static DWORD GetSysInfo() { return gdwSysInfo; } //GCCFIX -- added these functions back in! static BOOL SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ); // DSP Effects static void InitializeDSP(BOOL bReset); static void ProcessStereoDSP(int count); static void ProcessMonoDSP(int count); // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] static BOOL SetReverbParameters(UINT nDepth, UINT nDelay); // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] static BOOL SetXBassParameters(UINT nDepth, UINT nRange); // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] static BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); public: BOOL ReadNote(); BOOL ProcessRow(); BOOL ProcessEffects(); UINT GetNNAChannel(UINT nChn) const; void CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut); void NoteChange(UINT nChn, int note, BOOL bPorta=FALSE, BOOL bResetEnv=TRUE); void InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta=FALSE,BOOL bUpdVol=TRUE,BOOL bResetEnv=TRUE); // Channel Effects void PortamentoUp(MODCHANNEL *pChn, UINT param); void PortamentoDown(MODCHANNEL *pChn, UINT param); void FinePortamentoUp(MODCHANNEL *pChn, UINT param); void FinePortamentoDown(MODCHANNEL *pChn, UINT param); void ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param); void ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param); void TonePortamento(MODCHANNEL *pChn, UINT param); void Vibrato(MODCHANNEL *pChn, UINT param); void FineVibrato(MODCHANNEL *pChn, UINT param); void VolumeSlide(MODCHANNEL *pChn, UINT param); void PanningSlide(MODCHANNEL *pChn, UINT param); void ChannelVolSlide(MODCHANNEL *pChn, UINT param); void FineVolumeUp(MODCHANNEL *pChn, UINT param); void FineVolumeDown(MODCHANNEL *pChn, UINT param); void Tremolo(MODCHANNEL *pChn, UINT param); void Panbrello(MODCHANNEL *pChn, UINT param); void RetrigNote(UINT nChn, UINT param); void NoteCut(UINT nChn, UINT nTick); void KeyOff(UINT nChn); int PatternLoop(MODCHANNEL *, UINT param); void ExtendedMODCommands(UINT nChn, UINT param); void ExtendedS3MCommands(UINT nChn, UINT param); void ExtendedChannelEffect(MODCHANNEL *, UINT param); void ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param=0); void SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier=256) const; // Low-Level effect processing void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); // Global Effects void SetTempo(UINT param); void SetSpeed(UINT param); void GlobalVolSlide(UINT param); DWORD IsSongFinished(UINT nOrder, UINT nRow) const; BOOL IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const; // Read/Write sample functions signed char GetDeltaValue(signed char prev, UINT n) const { return (signed char)(prev + CompressionTable[n & 0x0F]); } UINT PackSample(int &sample, int next); BOOL CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result=NULL); UINT ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength); BOOL DestroySample(UINT nSample); BOOL DestroyInstrument(UINT nInstr); BOOL IsSampleUsed(UINT nSample); BOOL IsInstrumentUsed(UINT nInstr); BOOL RemoveInstrumentSamples(UINT nInstr); UINT DetectUnusedSamples(BOOL *); BOOL RemoveSelectedSamples(BOOL *); void AdjustSampleLoop(MODINSTRUMENT *pIns); // I/O from another sound file BOOL ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument); BOOL ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample); // Period/Note functions UINT GetNoteFromPeriod(UINT period) const; UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const; UINT GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac=0) const; // Misc functions MODINSTRUMENT *GetSample(UINT n) { return Ins+n; } void ResetMidiCfg(); UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote); BOOL ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers); UINT SaveMixPlugins(FILE *f=NULL, BOOL bUpdate=TRUE); UINT LoadMixPlugins(const void *pData, UINT nLen); #ifndef NO_FILTER DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-255] => [1-10KHz] #endif // Static helper functions public: static DWORD TransposeToFrequency(int transp, int ftune=0); static int FrequencyToTranspose(DWORD freq); static void FrequencyToTranspose(MODINSTRUMENT *psmp); // System-Dependant functions public: static MODCOMMAND *AllocatePattern(UINT rows, UINT nchns); static signed char* AllocateSample(UINT nbytes); static void FreePattern(LPVOID pat); static void FreeSample(LPVOID p); static UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); }; // inline DWORD BigEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); } // inline WORD BigEndianW(WORD x) { return (WORD)(((x >> 8) & 0xFF) | ((x << 8) & 0xFF00)); } ////////////////////////////////////////////////////////// // WAVE format information #pragma pack(1) // Standard IFF chunks IDs #define IFFID_FORM 0x4d524f46 #define IFFID_RIFF 0x46464952 #define IFFID_WAVE 0x45564157 #define IFFID_LIST 0x5453494C #define IFFID_INFO 0x4F464E49 // IFF Info fields #define IFFID_ICOP 0x504F4349 #define IFFID_IART 0x54524149 #define IFFID_IPRD 0x44525049 #define IFFID_INAM 0x4D414E49 #define IFFID_ICMT 0x544D4349 #define IFFID_IENG 0x474E4549 #define IFFID_ISFT 0x54465349 #define IFFID_ISBJ 0x4A425349 #define IFFID_IGNR 0x524E4749 #define IFFID_ICRD 0x44524349 // Wave IFF chunks IDs #define IFFID_wave 0x65766177 #define IFFID_fmt 0x20746D66 #define IFFID_wsmp 0x706D7377 #define IFFID_pcm 0x206d6370 #define IFFID_data 0x61746164 #define IFFID_smpl 0x6C706D73 #define IFFID_xtra 0x61727478 typedef struct WAVEFILEHEADER { DWORD id_RIFF; // "RIFF" DWORD filesize; // file length-8 DWORD id_WAVE; } WAVEFILEHEADER; typedef struct WAVEFORMATHEADER { DWORD id_fmt; // "fmt " DWORD hdrlen; // 16 WORD format; // 1 WORD channels; // 1:mono, 2:stereo DWORD freqHz; // sampling freq DWORD bytessec; // bytes/sec=freqHz*samplesize WORD samplesize; // sizeof(sample) WORD bitspersample; // bits per sample (8/16) } WAVEFORMATHEADER; typedef struct WAVEDATAHEADER { DWORD id_data; // "data" DWORD length; // length of data } WAVEDATAHEADER; typedef struct WAVESMPLHEADER { // SMPL DWORD smpl_id; // "smpl" -> 0x6C706D73 DWORD smpl_len; // length of smpl: 3Ch (54h with sustain loop) DWORD dwManufacturer; DWORD dwProduct; DWORD dwSamplePeriod; // 1000000000/freqHz DWORD dwBaseNote; // 3Ch = C-4 -> 60 + RelativeTone DWORD dwPitchFraction; DWORD dwSMPTEFormat; DWORD dwSMPTEOffset; DWORD dwSampleLoops; // number of loops DWORD cbSamplerData; } WAVESMPLHEADER; typedef struct SAMPLELOOPSTRUCT { DWORD dwIdentifier; DWORD dwLoopType; // 0=normal, 1=bidi DWORD dwLoopStart; DWORD dwLoopEnd; // Byte offset ? DWORD dwFraction; DWORD dwPlayCount; // Loop Count, 0=infinite } SAMPLELOOPSTRUCT; typedef struct WAVESAMPLERINFO { WAVESMPLHEADER wsiHdr; SAMPLELOOPSTRUCT wsiLoops[2]; } WAVESAMPLERINFO; typedef struct WAVELISTHEADER { DWORD list_id; // "LIST" -> 0x5453494C DWORD list_len; DWORD info; // "INFO" } WAVELISTHEADER; typedef struct WAVEEXTRAHEADER { DWORD xtra_id; // "xtra" -> 0x61727478 DWORD xtra_len; DWORD dwFlags; WORD wPan; WORD wVolume; WORD wGlobalVol; WORD wReserved; BYTE nVibType; BYTE nVibSweep; BYTE nVibDepth; BYTE nVibRate; } WAVEEXTRAHEADER; #pragma pack() /////////////////////////////////////////////////////////// // Low-level Mixing functions #define MIXBUFFERSIZE 512 #define MIXING_ATTENUATION 4 #define MIXING_CLIPMIN (-0x08000000) #define MIXING_CLIPMAX (0x07FFFFFF) #define VOLUMERAMPPRECISION 12 #define FADESONGDELAY 100 #define EQ_BUFFERSIZE (MIXBUFFERSIZE) #define AGC_PRECISION 9 #define AGC_UNITY (1 << AGC_PRECISION) // Calling conventions #ifdef MSC_VER #define MPPASMCALL __cdecl #define MPPFASTCALL __fastcall #else #define MPPASMCALL #define MPPFASTCALL #endif #define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) #define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) int _muldiv(long a, long b, long c); int _muldivr(long a, long b, long c); // Byte swapping functions from the GNU C Library and libsdl /* Swap bytes in 16 bit value. */ #ifdef __GNUC__ # define bswap_16(x) \ (__extension__ \ ({ unsigned short int __bsx = (x); \ ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) #else static __inline unsigned short int bswap_16 (unsigned short int __bsx) { return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); } #endif /* Swap bytes in 32 bit value. */ #ifdef __GNUC__ # define bswap_32(x) \ (__extension__ \ ({ unsigned int __bsx = (x); \ ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) #else static __inline unsigned int bswap_32 (unsigned int __bsx) { return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); } #endif #if (defined ARM) && (defined _WIN32_WCE) static __inline unsigned short int ARM_get16(const void *data) { unsigned short int s; memcpy(&s,data,sizeof(s)); return s; } static __inline unsigned int ARM_get32(const void *data) { unsigned int s; memcpy(&s,data,sizeof(s)); return s; } #define bswapLE16(X) ARM_get16(&X) #define bswapLE32(X) ARM_get32(&X) #define bswapBE16(X) bswap_16(ARM_get16(&X)) #define bswapBE32(X) bswap_32(ARM_get32(&X)) // From libsdl #elif defined(WORDS_BIGENDIAN) && WORDS_BIGENDIAN #define bswapLE16(X) bswap_16(X) #define bswapLE32(X) bswap_32(X) #define bswapBE16(X) (X) #define bswapBE32(X) (X) #else #define bswapLE16(X) (X) #define bswapLE32(X) (X) #define bswapBE16(X) bswap_16(X) #define bswapBE32(X) bswap_32(X) #endif #endif openjazz-20240919/ext/psmplug/sndmix.cpp000066400000000000000000001003011467303304200201200ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque */ #include "stdafx.h" #include "sndfile.h" #include "tables.h" // Volume ramp length, in 1/10 ms #define VOLUMERAMPLEN 146 // 1.46ms = 64 samples at 44.1kHz // VU-Meter #define VUMETER_DECAY 4 // SNDMIX: These are global flags for playback control (first two configurable via SetMixConfig) UINT CSoundFile::m_nStereoSeparation = 128; UINT CSoundFile::m_nMaxMixChannels = 32; LONG CSoundFile::m_nStreamVolume = 0x8000; // Mixing Configuration (SetWaveConfig) DWORD CSoundFile::gdwSysInfo = 0; DWORD CSoundFile::gnChannels = 1; DWORD CSoundFile::gdwSoundSetup = 0; DWORD CSoundFile::gdwMixingFreq = 44100; DWORD CSoundFile::gnBitsPerSample = 16; // Mixing data initialized in UINT CSoundFile::gnAGC = AGC_UNITY; UINT CSoundFile::gnVolumeRampSamples = 64; UINT CSoundFile::gnVUMeter = 0; UINT CSoundFile::gnCPUUsage = 0; LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = NULL; PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = NULL; LONG gnDryROfsVol = 0; LONG gnDryLOfsVol = 0; LONG gnRvbROfsVol = 0; LONG gnRvbLOfsVol = 0; int gbInitPlugins = 0; typedef DWORD (MPPASMCALL * LPCONVERTPROC)(LPVOID, int *, DWORD, LPLONG, LPLONG); extern DWORD MPPASMCALL X86_Convert32To8(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); extern DWORD MPPASMCALL X86_Convert32To16(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); extern DWORD MPPASMCALL X86_Convert32To24(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); extern DWORD MPPASMCALL X86_Convert32To32(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); extern UINT MPPASMCALL X86_AGC(int *pBuffer, UINT nSamples, UINT nAGC); extern VOID MPPASMCALL X86_Dither(int *pBuffer, UINT nSamples, UINT nBits); extern VOID MPPASMCALL X86_InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nSamples); extern VOID MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs); extern VOID MPPASMCALL X86_MonoFromStereo(int *pMixBuf, UINT nSamples); extern int MixSoundBuffer[MIXBUFFERSIZE*4]; extern int MixRearBuffer[MIXBUFFERSIZE*2]; UINT gnReverbSend; // Log tables for pre-amp // We don't want the tracker to get too loud static const UINT PreAmpTable[16] = { 0x60, 0x60, 0x60, 0x70, // 0-7 0x80, 0x88, 0x90, 0x98, // 8-15 0xA0, 0xA4, 0xA8, 0xB0, // 16-23 0xB4, 0xB8, 0xBC, 0xC0, // 24-31 }; static const UINT PreAmpAGCTable[16] = { 0x60, 0x60, 0x60, 0x60, 0x68, 0x70, 0x78, 0x80, 0x84, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0, }; // Return (a*b)/c - no divide error int _muldiv(long a, long b, long c) { return ((uint64_t) a * (uint64_t) b ) / c; } // Return (a*b+c/2)/c - no divide error int _muldivr(long a, long b, long c) { return ((uint64_t) a * (uint64_t) b + (c >> 1)) / c; } BOOL CSoundFile::InitPlayer(BOOL bReset) //-------------------------------------- { if (m_nMaxMixChannels > MAX_CHANNELS) m_nMaxMixChannels = MAX_CHANNELS; if (gdwMixingFreq < 4000) gdwMixingFreq = 4000; if (gdwMixingFreq > MAX_SAMPLE_RATE) gdwMixingFreq = MAX_SAMPLE_RATE; gnVolumeRampSamples = (gdwMixingFreq * VOLUMERAMPLEN) / 100000; if (gnVolumeRampSamples < 8) gnVolumeRampSamples = 8; gnDryROfsVol = gnDryLOfsVol = 0; gnRvbROfsVol = gnRvbLOfsVol = 0; if (bReset) { gnVUMeter = 0; gnCPUUsage = 0; } gbInitPlugins = (bReset) ? 3 : 1; InitializeDSP(bReset); return TRUE; } BOOL CSoundFile::FadeSong(UINT msec) //---------------------------------- { LONG nsamples = _muldiv(msec, gdwMixingFreq, 1000); if (nsamples <= 0) return FALSE; if (nsamples > 0x100000) nsamples = 0x100000; m_nBufferCount = nsamples; LONG nRampLength = m_nBufferCount; // Ramp everything down for (UINT noff=0; noff < m_nMixChannels; noff++) { MODCHANNEL *pramp = &Chn[ChnMix[noff]]; if (!pramp) continue; pramp->nNewLeftVol = pramp->nNewRightVol = 0; pramp->nRightRamp = (-pramp->nRightVol << VOLUMERAMPPRECISION) / nRampLength; pramp->nLeftRamp = (-pramp->nLeftVol << VOLUMERAMPPRECISION) / nRampLength; pramp->nRampRightVol = pramp->nRightVol << VOLUMERAMPPRECISION; pramp->nRampLeftVol = pramp->nLeftVol << VOLUMERAMPPRECISION; pramp->nRampLength = nRampLength; pramp->dwFlags |= CHN_VOLUMERAMP; } m_dwSongFlags |= SONG_FADINGSONG; return TRUE; } BOOL CSoundFile::GlobalFadeSong(UINT msec) //---------------------------------------- { if (m_dwSongFlags & SONG_GLOBALFADE) return FALSE; m_nGlobalFadeMaxSamples = _muldiv(msec, gdwMixingFreq, 1000); m_nGlobalFadeSamples = m_nGlobalFadeMaxSamples; m_dwSongFlags |= SONG_GLOBALFADE; return TRUE; } UINT CSoundFile::Read(LPVOID lpDestBuffer, UINT cbBuffer) //------------------------------------------------------- { LPBYTE lpBuffer = (LPBYTE)lpDestBuffer; LPCONVERTPROC pCvt = X86_Convert32To8; UINT lRead, lMax, lSampleSize, lCount, lSampleCount, nStat=0; LONG nVUMeterMin = 0x7FFFFFFF, nVUMeterMax = -0x7FFFFFFF; UINT nMaxPlugins; { nMaxPlugins = MAX_MIXPLUGINS; while ((nMaxPlugins > 0) && (!m_MixPlugins[nMaxPlugins-1].pMixPlugin)) nMaxPlugins--; } m_nMixStat = 0; lSampleSize = gnChannels; if (gnBitsPerSample == 16) { lSampleSize *= 2; pCvt = X86_Convert32To16; } else if (gnBitsPerSample == 24) { lSampleSize *= 3; pCvt = X86_Convert32To24; } else if (gnBitsPerSample == 32) { lSampleSize *= 4; pCvt = X86_Convert32To32; } lMax = cbBuffer / lSampleSize; if ((!lMax) || (!lpBuffer) || (!m_nChannels)) return 0; lRead = lMax; if (m_dwSongFlags & SONG_ENDREACHED) goto MixDone; while (lRead > 0) { // Update Channel Data if (!m_nBufferCount) { if (m_dwSongFlags & SONG_FADINGSONG) { m_dwSongFlags |= SONG_ENDREACHED; m_nBufferCount = lRead; } else if (!ReadNote()) { if (!FadeSong(FADESONGDELAY)) { m_dwSongFlags |= SONG_ENDREACHED; if (lRead == lMax) goto MixDone; m_nBufferCount = lRead; } } } lCount = m_nBufferCount; if (lCount > MIXBUFFERSIZE) lCount = MIXBUFFERSIZE; if (lCount > lRead) lCount = lRead; if (!lCount) break; lSampleCount = lCount; #ifndef MODPLUG_NO_REVERB gnReverbSend = 0; #endif // Resetting sound buffer X86_StereoFill(MixSoundBuffer, lSampleCount, &gnDryROfsVol, &gnDryLOfsVol); if (gnChannels >= 2) { lSampleCount *= 2; m_nMixStat += CreateStereoMix(lCount); ProcessStereoDSP(lCount); } else { m_nMixStat += CreateStereoMix(lCount); ProcessStereoDSP(lCount); X86_MonoFromStereo(MixSoundBuffer, lCount); } nStat++; UINT lTotalSampleCount = lSampleCount; // Multichannel if (gnChannels > 2) { X86_InterleaveFrontRear(MixSoundBuffer, MixRearBuffer, lSampleCount); lTotalSampleCount *= 2; } // Hook Function if (gpSndMixHook) { gpSndMixHook(MixSoundBuffer, lTotalSampleCount, gnChannels); } // Perform clipping + VU-Meter lpBuffer += pCvt(lpBuffer, MixSoundBuffer, lTotalSampleCount, &nVUMeterMin, &nVUMeterMax); // Buffer ready lRead -= lCount; m_nBufferCount -= lCount; } MixDone: if (lRead) memset(lpBuffer, (gnBitsPerSample == 8) ? 0x80 : 0, lRead * lSampleSize); // VU-Meter nVUMeterMin >>= (24-MIXING_ATTENUATION); nVUMeterMax >>= (24-MIXING_ATTENUATION); if (nVUMeterMax < nVUMeterMin) nVUMeterMax = nVUMeterMin; if ((gnVUMeter = (UINT)(nVUMeterMax - nVUMeterMin)) > 0xFF) gnVUMeter = 0xFF; if (nStat) { m_nMixStat += nStat-1; m_nMixStat /= nStat; } return lMax - lRead; } ///////////////////////////////////////////////////////////////////////////// // Handles navigation/effects BOOL CSoundFile::ProcessRow() //--------------------------- { if (++m_nTickCount >= m_nMusicSpeed * (m_nPatternDelay+1) + m_nFrameDelay) { m_nPatternDelay = 0; m_nFrameDelay = 0; m_nTickCount = 0; m_nRow = m_nNextRow; // Reset Pattern Loop Effect if (m_nCurrentPattern != m_nNextPattern) m_nCurrentPattern = m_nNextPattern; // Check if pattern is valid if (!(m_dwSongFlags & SONG_PATTERNLOOP)) { m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF; if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE; while (m_nPattern >= MAX_PATTERNS) { // End of song ? if ((m_nPattern == 0xFF) || (m_nCurrentPattern >= MAX_ORDERS)) { if (!m_nRepeatCount) return FALSE; //never repeat entire song if (!m_nRestartPos) { m_nMusicSpeed = m_nDefaultSpeed; m_nMusicTempo = m_nDefaultTempo; m_nGlobalVolume = m_nDefaultGlobalVolume; for (UINT i=0; i 0) m_nRepeatCount--; m_nCurrentPattern = m_nRestartPos; m_nRow = 0; if ((Order[m_nCurrentPattern] >= MAX_PATTERNS) || (!Patterns[Order[m_nCurrentPattern]])) return FALSE; } else { m_nCurrentPattern++; } m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF; if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE; } m_nNextPattern = m_nCurrentPattern; } // Weird stuff? if ((m_nPattern >= MAX_PATTERNS) || (!Patterns[m_nPattern]) || PatternSize[m_nPattern] == 0) return FALSE; // Should never happen if (m_nRow >= PatternSize[m_nPattern]) m_nRow = 0; m_nNextRow = m_nRow + 1; if (m_nNextRow >= PatternSize[m_nPattern]) { if (!(m_dwSongFlags & SONG_PATTERNLOOP)) m_nNextPattern = m_nCurrentPattern + 1; m_nNextRow = m_nNextStartRow; m_nNextStartRow = 0; } // Reset channel values MODCHANNEL *pChn = Chn; MODCOMMAND *m = Patterns[m_nPattern] + m_nRow * m_nChannels; for (UINT nChn=0; nChnnRowNote = m->note; pChn->nRowInstr = m->instr; pChn->nRowVolCmd = m->volcmd; pChn->nRowVolume = m->vol; pChn->nRowCommand = m->command; pChn->nRowParam = m->param; pChn->nLeftVol = pChn->nNewLeftVol; pChn->nRightVol = pChn->nNewRightVol; pChn->dwFlags &= ~(CHN_PORTAMENTO | CHN_VIBRATO | CHN_TREMOLO | CHN_PANBRELLO); pChn->nCommand = 0; } } // Should we process tick0 effects? if (!m_nMusicSpeed) m_nMusicSpeed = 1; m_dwSongFlags |= SONG_FIRSTTICK; if (m_nTickCount) { m_dwSongFlags &= ~SONG_FIRSTTICK; if ((!(m_nType & MOD_TYPE_XM)) && (m_nTickCount < m_nMusicSpeed * (1 + m_nPatternDelay))) { if (!(m_nTickCount % m_nMusicSpeed)) m_dwSongFlags |= SONG_FIRSTTICK; } } // Update Effects return ProcessEffects(); } //////////////////////////////////////////////////////////////////////////////////////////// // Handles envelopes & mixer setup BOOL CSoundFile::ReadNote() //------------------------- { if (!ProcessRow()) return FALSE; //////////////////////////////////////////////////////////////////////////////////// m_nTotalCount++; if (!m_nMusicTempo) return FALSE; m_nBufferCount = (gdwMixingFreq * 5 * m_nTempoFactor) / (m_nMusicTempo << 8); // Master Volume + Pre-Amplification / Attenuation setup DWORD nMasterVol; { int nchn32 = (m_nChannels < 32) ? m_nChannels : 31; if ((m_nType & MOD_TYPE_IT) && (m_nInstruments) && (nchn32 < 6)) nchn32 = 6; int realmastervol = m_nMasterVolume; if (realmastervol > 0x80) { realmastervol = 0x80 + ((realmastervol - 0x80) * (nchn32+4)) / 16; } UINT attenuation = (gdwSoundSetup & SNDMIX_AGC) ? PreAmpAGCTable[nchn32>>1] : PreAmpTable[nchn32>>1]; DWORD mastervol = (realmastervol * (m_nSongPreAmp + 0x10)) >> 6; if (mastervol > 0x200) mastervol = 0x200; if ((m_dwSongFlags & SONG_GLOBALFADE) && (m_nGlobalFadeMaxSamples)) { mastervol = _muldiv(mastervol, m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples); } nMasterVol = (mastervol << 7) / attenuation; if (nMasterVol > 0x180) nMasterVol = 0x180; } //////////////////////////////////////////////////////////////////////////////////// // Update channels data m_nMixChannels = 0; MODCHANNEL *pChn = Chn; for (UINT nChn=0; nChndwFlags & CHN_NOTEFADE) && (!(pChn->nFadeOutVol|pChn->nRightVol|pChn->nLeftVol))) { pChn->nLength = 0; pChn->nROfs = pChn->nLOfs = 0; } // Check for unused channel if ((pChn->dwFlags & CHN_MUTE) || ((nChn >= m_nChannels) && (!pChn->nLength))) { pChn->nVUMeter = 0; continue; } // Reset channel data pChn->nInc = 0; pChn->nRealVolume = 0; pChn->nRealPan = pChn->nPan + pChn->nPanSwing; if (pChn->nRealPan < 0) pChn->nRealPan = 0; if (pChn->nRealPan > 256) pChn->nRealPan = 256; pChn->nRampLength = 0; // Calc Frequency if ((pChn->nPeriod) && (pChn->nLength)) { int vol = pChn->nVolume + pChn->nVolSwing; if (vol < 0) vol = 0; if (vol > 256) vol = 256; // Tremolo if (pChn->dwFlags & CHN_TREMOLO) { UINT trempos = pChn->nTremoloPos & 0x3F; if (vol > 0) { int tremattn = (m_nType & MOD_TYPE_XM) ? 5 : 6; switch (pChn->nTremoloType & 0x03) { case 1: vol += (ModRampDownTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; break; case 2: vol += (ModSquareTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; break; case 3: vol += (ModRandomTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; break; default: vol += (ModSinusTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; } } if ((m_nTickCount) || ((m_nType & (MOD_TYPE_STM|MOD_TYPE_S3M|MOD_TYPE_IT)) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS)))) { pChn->nTremoloPos = (trempos + pChn->nTremoloSpeed) & 0x3F; } } // Tremor if (pChn->nCommand == CMD_TREMOR) { UINT n = (pChn->nTremorParam >> 4) + (pChn->nTremorParam & 0x0F); UINT ontime = pChn->nTremorParam >> 4; if ((!(m_nType & MOD_TYPE_IT)) || (m_dwSongFlags & SONG_ITOLDEFFECTS)) { n += 2; ontime++; } UINT tremcount = (UINT)pChn->nTremorCount; if (tremcount >= n) tremcount = 0; if ((m_nTickCount) || (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) { if (tremcount >= ontime) vol = 0; pChn->nTremorCount = (BYTE)(tremcount + 1); } pChn->dwFlags |= CHN_FASTVOLRAMP; } // Clip volume if (vol < 0) vol = 0; if (vol > 0x100) vol = 0x100; vol <<= 6; // Process Envelopes if (pChn->pHeader) { INSTRUMENTHEADER *penv = pChn->pHeader; // Volume Envelope if ((pChn->dwFlags & CHN_VOLENV) && (penv->nVolEnv)) { int envpos = pChn->nVolEnvPosition; UINT pt = penv->nVolEnv - 1; for (UINT i=0; i<(UINT)(penv->nVolEnv-1); i++) { if (envpos <= penv->VolPoints[i]) { pt = i; break; } } int x2 = penv->VolPoints[pt]; int x1, envvol; if (envpos >= x2) { envvol = penv->VolEnv[pt] << 2; x1 = x2; } else if (pt) { envvol = penv->VolEnv[pt-1] << 2; x1 = penv->VolPoints[pt-1]; } else { envvol = 0; x1 = 0; } if (envpos > x2) envpos = x2; if ((x2 > x1) && (envpos > x1)) { envvol += ((envpos - x1) * (((int)penv->VolEnv[pt]<<2) - envvol)) / (x2 - x1); } if (envvol < 0) envvol = 0; if (envvol > 256) envvol = 256; vol = (vol * envvol) >> 8; } // Panning Envelope if ((pChn->dwFlags & CHN_PANENV) && (penv->nPanEnv)) { int envpos = pChn->nPanEnvPosition; UINT pt = penv->nPanEnv - 1; for (UINT i=0; i<(UINT)(penv->nPanEnv-1); i++) { if (envpos <= penv->PanPoints[i]) { pt = i; break; } } int x2 = penv->PanPoints[pt], y2 = penv->PanEnv[pt]; int x1, envpan; if (envpos >= x2) { envpan = y2; x1 = x2; } else if (pt) { envpan = penv->PanEnv[pt-1]; x1 = penv->PanPoints[pt-1]; } else { envpan = 128; x1 = 0; } if ((x2 > x1) && (envpos > x1)) { envpan += ((envpos - x1) * (y2 - envpan)) / (x2 - x1); } if (envpan < 0) envpan = 0; if (envpan > 64) envpan = 64; int pan = pChn->nPan; if (pan >= 128) { pan += ((envpan - 32) * (256 - pan)) / 32; } else { pan += ((envpan - 32) * (pan)) / 32; } if (pan < 0) pan = 0; if (pan > 256) pan = 256; pChn->nRealPan = pan; } // FadeOut volume if (pChn->dwFlags & CHN_NOTEFADE) { UINT fadeout = penv->nFadeOut; if (fadeout) { pChn->nFadeOutVol -= fadeout << 1; if (pChn->nFadeOutVol <= 0) pChn->nFadeOutVol = 0; vol = (vol * pChn->nFadeOutVol) >> 16; } else if (!pChn->nFadeOutVol) { vol = 0; } } // Pitch/Pan separation if ((penv->nPPS) && (pChn->nRealPan) && (pChn->nNote)) { int pandelta = (int)pChn->nRealPan + (int)((int)(pChn->nNote - penv->nPPC - 1) * (int)penv->nPPS) / (int)8; if (pandelta < 0) pandelta = 0; if (pandelta > 256) pandelta = 256; pChn->nRealPan = pandelta; } } else { // No Envelope: key off => note cut if (pChn->dwFlags & CHN_NOTEFADE) // 1.41-: CHN_KEYOFF|CHN_NOTEFADE { pChn->nFadeOutVol = 0; vol = 0; } } // vol is 14-bits if (vol) { // IMPORTANT: pChn->nRealVolume is 14 bits !!! // -> _muldiv( 14+8, 6+6, 18); => RealVolume: 14-bit result (22+12-20) pChn->nRealVolume = _muldiv(vol * m_nGlobalVolume, pChn->nGlobalVol * pChn->nInsVol, 1 << 20); } if (pChn->nPeriod < m_nMinPeriod) pChn->nPeriod = m_nMinPeriod; int period = pChn->nPeriod; if ((pChn->dwFlags & (CHN_GLISSANDO|CHN_PORTAMENTO)) == (CHN_GLISSANDO|CHN_PORTAMENTO)) { period = GetPeriodFromNote(GetNoteFromPeriod(period), pChn->nFineTune, pChn->nC4Speed); } // Arpeggio ? if (pChn->nCommand == CMD_ARPEGGIO) { switch(m_nTickCount % 3) { case 1: period = GetPeriodFromNote(pChn->nNote + (pChn->nArpeggio >> 4), pChn->nFineTune, pChn->nC4Speed); break; case 2: period = GetPeriodFromNote(pChn->nNote + (pChn->nArpeggio & 0x0F), pChn->nFineTune, pChn->nC4Speed); break; } } if (m_dwSongFlags & SONG_AMIGALIMITS) { if (period < 113*4) period = 113*4; if (period > 856*4) period = 856*4; } // Pitch/Filter Envelope if ((pChn->pHeader) && (pChn->dwFlags & CHN_PITCHENV) && (pChn->pHeader->nPitchEnv)) { INSTRUMENTHEADER *penv = pChn->pHeader; int envpos = pChn->nPitchEnvPosition; UINT pt = penv->nPitchEnv - 1; for (UINT i=0; i<(UINT)(penv->nPitchEnv-1); i++) { if (envpos <= penv->PitchPoints[i]) { pt = i; break; } } int x2 = penv->PitchPoints[pt]; int x1, envpitch; if (envpos >= x2) { envpitch = (((int)penv->PitchEnv[pt]) - 32) * 8; x1 = x2; } else if (pt) { envpitch = (((int)penv->PitchEnv[pt-1]) - 32) * 8; x1 = penv->PitchPoints[pt-1]; } else { envpitch = 0; x1 = 0; } if (envpos > x2) envpos = x2; if ((x2 > x1) && (envpos > x1)) { int envpitchdest = (((int)penv->PitchEnv[pt]) - 32) * 8; envpitch += ((envpos - x1) * (envpitchdest - envpitch)) / (x2 - x1); } if (envpitch < -256) envpitch = -256; if (envpitch > 256) envpitch = 256; // Filter Envelope: controls cutoff frequency if (penv->dwFlags & ENV_FILTER) { #ifndef NO_FILTER SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE, envpitch); #endif // NO_FILTER } else // Pitch Envelope { int l = envpitch; if (l < 0) { l = -l; if (l > 255) l = 255; period = _muldiv(period, LinearSlideUpTable[l], 0x10000); } else { if (l > 255) l = 255; period = _muldiv(period, LinearSlideDownTable[l], 0x10000); } } } // Vibrato if (pChn->dwFlags & CHN_VIBRATO) { UINT vibpos = pChn->nVibratoPos; LONG vdelta; switch (pChn->nVibratoType & 0x03) { case 1: vdelta = ModRampDownTable[vibpos]; break; case 2: vdelta = ModSquareTable[vibpos]; break; case 3: vdelta = ModRandomTable[vibpos]; break; default: vdelta = ModSinusTable[vibpos]; } UINT vdepth = ((m_nType != MOD_TYPE_IT) || (m_dwSongFlags & SONG_ITOLDEFFECTS)) ? 6 : 7; vdelta = (vdelta * (int)pChn->nVibratoDepth) >> vdepth; if ((m_dwSongFlags & SONG_LINEARSLIDES) && (m_nType & MOD_TYPE_IT)) { LONG l = vdelta; if (l < 0) { l = -l; vdelta = _muldiv(period, LinearSlideDownTable[l >> 2], 0x10000) - period; if (l & 0x03) vdelta += _muldiv(period, FineLinearSlideDownTable[l & 0x03], 0x10000) - period; } else { vdelta = _muldiv(period, LinearSlideUpTable[l >> 2], 0x10000) - period; if (l & 0x03) vdelta += _muldiv(period, FineLinearSlideUpTable[l & 0x03], 0x10000) - period; } } period += vdelta; if ((m_nTickCount) || ((m_nType & MOD_TYPE_IT) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS)))) { pChn->nVibratoPos = (vibpos + pChn->nVibratoSpeed) & 0x3F; } } // Panbrello if (pChn->dwFlags & CHN_PANBRELLO) { UINT panpos = ((pChn->nPanbrelloPos+0x10) >> 2) & 0x3F; LONG pdelta; switch (pChn->nPanbrelloType & 0x03) { case 1: pdelta = ModRampDownTable[panpos]; break; case 2: pdelta = ModSquareTable[panpos]; break; case 3: pdelta = ModRandomTable[panpos]; break; default: pdelta = ModSinusTable[panpos]; } pChn->nPanbrelloPos += pChn->nPanbrelloSpeed; pdelta = ((pdelta * (int)pChn->nPanbrelloDepth) + 2) >> 3; pdelta += pChn->nRealPan; if (pdelta < 0) pdelta = 0; if (pdelta > 256) pdelta = 256; pChn->nRealPan = pdelta; } int nPeriodFrac = 0; // Instrument Auto-Vibrato if ((pChn->pInstrument) && (pChn->pInstrument->nVibDepth)) { MODINSTRUMENT *pins = pChn->pInstrument; if (pins->nVibSweep == 0) { pChn->nAutoVibDepth = pins->nVibDepth << 8; } else { if (m_nType & MOD_TYPE_IT) { pChn->nAutoVibDepth += pins->nVibSweep << 3; } else if (!(pChn->dwFlags & CHN_KEYOFF)) { pChn->nAutoVibDepth += (pins->nVibDepth << 8) / pins->nVibSweep; } if ((pChn->nAutoVibDepth >> 8) > pins->nVibDepth) pChn->nAutoVibDepth = pins->nVibDepth << 8; } pChn->nAutoVibPos += pins->nVibRate; int val; switch(pins->nVibType) { case 4: // Random val = ModRandomTable[pChn->nAutoVibPos & 0x3F]; pChn->nAutoVibPos++; break; case 3: // Ramp Down val = ((0x40 - (pChn->nAutoVibPos >> 1)) & 0x7F) - 0x40; break; case 2: // Ramp Up val = ((0x40 + (pChn->nAutoVibPos >> 1)) & 0x7f) - 0x40; break; case 1: // Square val = (pChn->nAutoVibPos & 128) ? +64 : -64; break; default: // Sine val = ft2VibratoTable[pChn->nAutoVibPos & 255]; } int n = ((val * pChn->nAutoVibDepth) >> 8); if (m_nType & MOD_TYPE_IT) { int df1, df2; if (n < 0) { n = -n; UINT n1 = n >> 8; df1 = LinearSlideUpTable[n1]; df2 = LinearSlideUpTable[n1+1]; } else { UINT n1 = n >> 8; df1 = LinearSlideDownTable[n1]; df2 = LinearSlideDownTable[n1+1]; } n >>= 2; period = _muldiv(period, df1 + ((df2-df1)*(n&0x3F)>>6), 256); nPeriodFrac = period & 0xFF; period >>= 8; } else { period += (n >> 6); } } // Final Period if (period <= m_nMinPeriod) { if (m_nType & MOD_TYPE_S3M) pChn->nLength = 0; period = m_nMinPeriod; } if (period > m_nMaxPeriod) { if ((m_nType & MOD_TYPE_IT) || (period >= 0x100000)) { pChn->nFadeOutVol = 0; pChn->dwFlags |= CHN_NOTEFADE; pChn->nRealVolume = 0; } period = m_nMaxPeriod; nPeriodFrac = 0; } UINT freq = GetFreqFromPeriod(period, pChn->nC4Speed, nPeriodFrac); if ((m_nType & MOD_TYPE_IT) && (freq < 256)) { pChn->nFadeOutVol = 0; pChn->dwFlags |= CHN_NOTEFADE; pChn->nRealVolume = 0; } UINT ninc = _muldiv(freq, 0x10000, gdwMixingFreq); if ((ninc >= 0xFFB0) && (ninc <= 0x10090)) ninc = 0x10000; if (m_nFreqFactor != 128) ninc = (ninc * m_nFreqFactor) >> 7; if (ninc > 0xFF0000) ninc = 0xFF0000; pChn->nInc = (ninc+1) & ~3; } // Increment envelope position if (pChn->pHeader) { INSTRUMENTHEADER *penv = pChn->pHeader; // Volume Envelope if (pChn->dwFlags & CHN_VOLENV) { // Increase position pChn->nVolEnvPosition++; // Volume Loop ? if (penv->dwFlags & ENV_VOLLOOP) { UINT volloopend = penv->VolPoints[penv->nVolLoopEnd]; if (m_nType != MOD_TYPE_XM) volloopend++; if (pChn->nVolEnvPosition == volloopend) { pChn->nVolEnvPosition = penv->VolPoints[penv->nVolLoopStart]; if ((penv->nVolLoopEnd == penv->nVolLoopStart) && (!penv->VolEnv[penv->nVolLoopStart]) && ((!(m_nType & MOD_TYPE_XM)) || (penv->nVolLoopEnd+1 == penv->nVolEnv))) { pChn->dwFlags |= CHN_NOTEFADE; pChn->nFadeOutVol = 0; } } } // Volume Sustain ? if ((penv->dwFlags & ENV_VOLSUSTAIN) && (!(pChn->dwFlags & CHN_KEYOFF))) { if (pChn->nVolEnvPosition == (UINT)penv->VolPoints[penv->nVolSustainEnd]+1) pChn->nVolEnvPosition = penv->VolPoints[penv->nVolSustainBegin]; } else // End of Envelope ? if (pChn->nVolEnvPosition > penv->VolPoints[penv->nVolEnv - 1]) { if ((m_nType & MOD_TYPE_IT) || (pChn->dwFlags & CHN_KEYOFF)) pChn->dwFlags |= CHN_NOTEFADE; pChn->nVolEnvPosition = penv->VolPoints[penv->nVolEnv - 1]; if ((!penv->VolEnv[penv->nVolEnv-1]) && ((nChn >= m_nChannels) || (m_nType & MOD_TYPE_IT))) { pChn->dwFlags |= CHN_NOTEFADE; pChn->nFadeOutVol = 0; pChn->nRealVolume = 0; } } } // Panning Envelope if (pChn->dwFlags & CHN_PANENV) { pChn->nPanEnvPosition++; if (penv->dwFlags & ENV_PANLOOP) { UINT panloopend = penv->PanPoints[penv->nPanLoopEnd]; if (m_nType != MOD_TYPE_XM) panloopend++; if (pChn->nPanEnvPosition == panloopend) pChn->nPanEnvPosition = penv->PanPoints[penv->nPanLoopStart]; } // Panning Sustain ? if ((penv->dwFlags & ENV_PANSUSTAIN) && (pChn->nPanEnvPosition == (UINT)penv->PanPoints[penv->nPanSustainEnd]+1) && (!(pChn->dwFlags & CHN_KEYOFF))) { // Panning sustained pChn->nPanEnvPosition = penv->PanPoints[penv->nPanSustainBegin]; } else { if (pChn->nPanEnvPosition > penv->PanPoints[penv->nPanEnv - 1]) pChn->nPanEnvPosition = penv->PanPoints[penv->nPanEnv - 1]; } } // Pitch Envelope if (pChn->dwFlags & CHN_PITCHENV) { // Increase position pChn->nPitchEnvPosition++; // Pitch Loop ? if (penv->dwFlags & ENV_PITCHLOOP) { if (pChn->nPitchEnvPosition >= penv->PitchPoints[penv->nPitchLoopEnd]) pChn->nPitchEnvPosition = penv->PitchPoints[penv->nPitchLoopStart]; } // Pitch Sustain ? if ((penv->dwFlags & ENV_PITCHSUSTAIN) && (!(pChn->dwFlags & CHN_KEYOFF))) { if (pChn->nPitchEnvPosition == (UINT)penv->PitchPoints[penv->nPitchSustainEnd]+1) pChn->nPitchEnvPosition = penv->PitchPoints[penv->nPitchSustainBegin]; } else { if (pChn->nPitchEnvPosition > penv->PitchPoints[penv->nPitchEnv - 1]) pChn->nPitchEnvPosition = penv->PitchPoints[penv->nPitchEnv - 1]; } } } // Volume ramping pChn->dwFlags &= ~CHN_VOLUMERAMP; if ((pChn->nRealVolume) || (pChn->nLeftVol) || (pChn->nRightVol)) pChn->dwFlags |= CHN_VOLUMERAMP; // Check for too big nInc if (((pChn->nInc >> 16) + 1) >= (LONG)(pChn->nLoopEnd - pChn->nLoopStart)) pChn->dwFlags &= ~CHN_LOOP; pChn->nNewRightVol = pChn->nNewLeftVol = 0; pChn->pCurrentSample = ((pChn->pSample) && (pChn->nLength) && (pChn->nInc)) ? pChn->pSample : NULL; if (pChn->pCurrentSample) { #define kChnMasterVol nMasterVol // Adjusting volumes if (gnChannels >= 2) { int pan = ((int)pChn->nRealPan) - 128; pan *= (int)m_nStereoSeparation; pan /= 128; pan += 128; if (pan < 0) pan = 0; if (pan > 256) pan = 256; if (gdwSoundSetup & SNDMIX_REVERSESTEREO) pan = 256 - pan; LONG realvol = (pChn->nRealVolume * kChnMasterVol) >> (8-1); if (gdwSoundSetup & SNDMIX_SOFTPANNING) { if (pan < 128) { pChn->nNewLeftVol = (realvol * pan) >> 8; pChn->nNewRightVol = (realvol * 128) >> 8; } else { pChn->nNewLeftVol = (realvol * 128) >> 8; pChn->nNewRightVol = (realvol * (256 - pan)) >> 8; } } else { pChn->nNewLeftVol = (realvol * pan) >> 8; pChn->nNewRightVol = (realvol * (256 - pan)) >> 8; } } else { pChn->nNewRightVol = (pChn->nRealVolume * kChnMasterVol) >> 8; pChn->nNewLeftVol = pChn->nNewRightVol; } // Clipping volumes if (pChn->nNewRightVol > 0xFFFF) pChn->nNewRightVol = 0xFFFF; if (pChn->nNewLeftVol > 0xFFFF) pChn->nNewLeftVol = 0xFFFF; // Check IDO if (gdwSoundSetup & SNDMIX_NORESAMPLING) { pChn->dwFlags |= CHN_NOIDO; } else { pChn->dwFlags &= ~(CHN_NOIDO|CHN_HQSRC); if( pChn->nInc == 0x10000 ) { pChn->dwFlags |= CHN_NOIDO; } else { if( ((gdwSoundSetup & SNDMIX_HQRESAMPLER) == 0) && ((gdwSoundSetup & SNDMIX_ULTRAHQSRCMODE) == 0) ) { if (pChn->nInc >= 0xFF00) pChn->dwFlags |= CHN_NOIDO; } } } pChn->nNewRightVol >>= MIXING_ATTENUATION; pChn->nNewLeftVol >>= MIXING_ATTENUATION; pChn->nRightRamp = pChn->nLeftRamp = 0; // Dolby Pro-Logic Surround if ((pChn->dwFlags & CHN_SURROUND) && (gnChannels <= 2)) pChn->nNewLeftVol = - pChn->nNewLeftVol; // Checking Ping-Pong Loops if (pChn->dwFlags & CHN_PINGPONGFLAG) pChn->nInc = -pChn->nInc; // Setting up volume ramp if ((pChn->dwFlags & CHN_VOLUMERAMP) && ((pChn->nRightVol != pChn->nNewRightVol) || (pChn->nLeftVol != pChn->nNewLeftVol))) { LONG nRampLength = gnVolumeRampSamples; LONG nRightDelta = ((pChn->nNewRightVol - pChn->nRightVol) << VOLUMERAMPPRECISION); LONG nLeftDelta = ((pChn->nNewLeftVol - pChn->nLeftVol) << VOLUMERAMPPRECISION); if ((gdwSoundSetup & SNDMIX_DIRECTTODISK) || ((gdwSysInfo & (SYSMIX_ENABLEMMX|SYSMIX_FASTCPU)) && (gdwSoundSetup & SNDMIX_HQRESAMPLER) && (gnCPUUsage <= 20))) { if ((pChn->nRightVol|pChn->nLeftVol) && (pChn->nNewRightVol|pChn->nNewLeftVol) && (!(pChn->dwFlags & CHN_FASTVOLRAMP))) { nRampLength = m_nBufferCount; if (nRampLength > (1 << (VOLUMERAMPPRECISION-1))) nRampLength = (1 << (VOLUMERAMPPRECISION-1)); if (nRampLength < (LONG)gnVolumeRampSamples) nRampLength = gnVolumeRampSamples; } } pChn->nRightRamp = nRightDelta / nRampLength; pChn->nLeftRamp = nLeftDelta / nRampLength; pChn->nRightVol = pChn->nNewRightVol - ((pChn->nRightRamp * nRampLength) >> VOLUMERAMPPRECISION); pChn->nLeftVol = pChn->nNewLeftVol - ((pChn->nLeftRamp * nRampLength) >> VOLUMERAMPPRECISION); if (pChn->nRightRamp|pChn->nLeftRamp) { pChn->nRampLength = nRampLength; } else { pChn->dwFlags &= ~CHN_VOLUMERAMP; pChn->nRightVol = pChn->nNewRightVol; pChn->nLeftVol = pChn->nNewLeftVol; } } else { pChn->dwFlags &= ~CHN_VOLUMERAMP; pChn->nRightVol = pChn->nNewRightVol; pChn->nLeftVol = pChn->nNewLeftVol; } pChn->nRampRightVol = pChn->nRightVol << VOLUMERAMPPRECISION; pChn->nRampLeftVol = pChn->nLeftVol << VOLUMERAMPPRECISION; // Adding the channel in the channel list ChnMix[m_nMixChannels++] = nChn; if (m_nMixChannels >= MAX_CHANNELS) break; } else { if (pChn->nVUMeter > 0xFF) pChn->nVUMeter = 0; pChn->nLeftVol = pChn->nRightVol = 0; pChn->nLength = 0; } } // Checking Max Mix Channels reached: ordering by volume if ((m_nMixChannels >= m_nMaxMixChannels) && (!(gdwSoundSetup & SNDMIX_DIRECTTODISK))) { for (UINT i=0; i m_nBufferCount) m_nGlobalFadeSamples -= m_nBufferCount; else m_nGlobalFadeSamples = 0; } return TRUE; } openjazz-20240919/ext/psmplug/stdafx.h000066400000000000000000000037301467303304200175640ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Rani Assaf , * Olivier Lapicque , * Adam Goode (endian and char fixes for PPC) */ #ifndef MP_STDAFX_H #define MP_STDAFX_H #ifdef _WIN32 #ifdef MSC_VER #pragma warning (disable:4201) #pragma warning (disable:4514) #endif #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #define srandom(_seed) srand(_seed) #define random() rand() #define sleep(_ms) Sleep(_ms) #undef strcasecmp #undef strncasecmp #define strcasecmp(a,b) _stricmp(a,b) #define strncasecmp(a,b,c) _strnicmp(a,b,c) #ifndef isblank #define isblank(c) ((c) == ' ' || (c) == '\t') #endif #else #include #include #include #include #include typedef int8_t CHAR; typedef uint8_t UCHAR; typedef uint8_t* PUCHAR; typedef uint16_t USHORT; typedef uint32_t ULONG; typedef uint32_t UINT; typedef uint32_t DWORD; typedef int32_t LONG; typedef int64_t LONGLONG; typedef int32_t* LPLONG; typedef uint32_t* LPDWORD; typedef uint16_t WORD; typedef uint8_t BYTE; typedef uint8_t* LPBYTE; typedef bool BOOL; typedef char* LPSTR; typedef void* LPVOID; typedef uint16_t* LPWORD; typedef const char* LPCSTR; typedef void* PVOID; typedef void VOID; inline LONG MulDiv (long a, long b, long c) { /*if (!c) return 0;*/ return ((uint64_t) a * (uint64_t) b ) / c; } #define LPCTSTR LPCSTR #define lstrcpyn strncpy #define lstrcpy strcpy #define lstrcmp strcmp #define wsprintf sprintf #define WAVE_FORMAT_PCM 1 #define GHND 0 #define GlobalFreePtr(p) free((void *)(p)) inline int8_t * GlobalAllocPtr(unsigned int, size_t size) { int8_t * p = (int8_t *) malloc(size); if (p != NULL) memset(p, 0, size); return p; } #ifndef FALSE #define FALSE false #endif #ifndef TRUE #define TRUE true #endif #endif /* _WIN32 */ #endif openjazz-20240919/ext/psmplug/tables.h000066400000000000000000000416601467303304200175510ustar00rootroot00000000000000/* * This source code is public domain. * * Authors: Olivier Lapicque */ #include "stdafx.h" #include "sndfile.h" #ifndef MP_TABLES_H #define MP_TABLES_H static const BYTE ImpulseTrackerPortaVolCmd[16] = { 0x00, 0x01, 0x04, 0x08, 0x10, 0x20, 0x40, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Period table for Protracker octaves 0-5: static const WORD ProTrackerPeriodTable[6*12] = { 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, 856,808,762,720,678,640,604,570,538,508,480,453, 428,404,381,360,339,320,302,285,269,254,240,226, 214,202,190,180,170,160,151,143,135,127,120,113, 107,101,95,90,85,80,75,71,67,63,60,56, 53,50,47,45,42,40,37,35,33,31,30,28 }; static const WORD ProTrackerTunedPeriods[16*12] = { 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, 1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900, 1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894, 1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888, 1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882, 1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874, 1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868, 1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862, 1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960, 1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954, 1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948, 1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940, 1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934, 1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926, 1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920, 1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914 }; // S3M C-4 periods static const WORD FreqS3MTable[16] = { 1712,1616,1524,1440,1356,1280, 1208,1140,1076,1016,960,907, 0,0,0,0 }; // S3M FineTune frequencies static const WORD S3MFineTuneTable[16] = { 7895,7941,7985,8046,8107,8169,8232,8280, 8363,8413,8463,8529,8581,8651,8723,8757, // 8363*2^((i-8)/(12*8)) }; // Sinus table static const int16_t ModSinusTable[64] = { 0,12,25,37,49,60,71,81,90,98,106,112,117,122,125,126, 127,126,125,122,117,112,106,98,90,81,71,60,49,37,25,12, 0,-12,-25,-37,-49,-60,-71,-81,-90,-98,-106,-112,-117,-122,-125,-126, -127,-126,-125,-122,-117,-112,-106,-98,-90,-81,-71,-60,-49,-37,-25,-12 }; // Triangle wave table (ramp down) static const int16_t ModRampDownTable[64] = { 0,-4,-8,-12,-16,-20,-24,-28,-32,-36,-40,-44,-48,-52,-56,-60, -64,-68,-72,-76,-80,-84,-88,-92,-96,-100,-104,-108,-112,-116,-120,-124, 127,123,119,115,111,107,103,99,95,91,87,83,79,75,71,67, 63,59,55,51,47,43,39,35,31,27,23,19,15,11,7,3 }; // Square wave table static const int16_t ModSquareTable[64] = { 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, -127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127, -127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127 }; // Random wave table static const int16_t ModRandomTable[64] = { 98,-127,-43,88,102,41,-65,-94,125,20,-71,-86,-70,-32,-16,-96, 17,72,107,-5,116,-69,-62,-40,10,-61,65,109,-18,-38,-13,-76, -23,88,21,-94,8,106,21,-112,6,109,20,-88,-30,9,-127,118, 42,-34,89,-4,-51,-72,21,-29,112,123,84,-101,-92,98,-54,-95 }; // volume fade tables for Retrig Note: static const int8_t retrigTable1[16] = { 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 0, 0, 24, 32 }; static const int8_t retrigTable2[16] = { 0, -1, -2, -4, -8, -16, 0, 0, 0, 1, 2, 4, 8, 16, 0, 0 }; static const WORD XMPeriodTable[104] = { 907,900,894,887,881,875,868,862,856,850,844,838,832,826,820,814, 808,802,796,791,785,779,774,768,762,757,752,746,741,736,730,725, 720,715,709,704,699,694,689,684,678,675,670,665,660,655,651,646, 640,636,632,628,623,619,614,610,604,601,597,592,588,584,580,575, 570,567,563,559,555,551,547,543,538,535,532,528,524,520,516,513, 508,505,502,498,494,491,487,484,480,477,474,470,467,463,460,457, 453,450,447,443,440,437,434,431 }; static const uint32_t XMLinearTable[768] = { 535232,534749,534266,533784,533303,532822,532341,531861, 531381,530902,530423,529944,529466,528988,528511,528034, 527558,527082,526607,526131,525657,525183,524709,524236, 523763,523290,522818,522346,521875,521404,520934,520464, 519994,519525,519057,518588,518121,517653,517186,516720, 516253,515788,515322,514858,514393,513929,513465,513002, 512539,512077,511615,511154,510692,510232,509771,509312, 508852,508393,507934,507476,507018,506561,506104,505647, 505191,504735,504280,503825,503371,502917,502463,502010, 501557,501104,500652,500201,499749,499298,498848,498398, 497948,497499,497050,496602,496154,495706,495259,494812, 494366,493920,493474,493029,492585,492140,491696,491253, 490809,490367,489924,489482,489041,488600,488159,487718, 487278,486839,486400,485961,485522,485084,484647,484210, 483773,483336,482900,482465,482029,481595,481160,480726, 480292,479859,479426,478994,478562,478130,477699,477268, 476837,476407,475977,475548,475119,474690,474262,473834, 473407,472979,472553,472126,471701,471275,470850,470425, 470001,469577,469153,468730,468307,467884,467462,467041, 466619,466198,465778,465358,464938,464518,464099,463681, 463262,462844,462427,462010,461593,461177,460760,460345, 459930,459515,459100,458686,458272,457859,457446,457033, 456621,456209,455797,455386,454975,454565,454155,453745, 453336,452927,452518,452110,451702,451294,450887,450481, 450074,449668,449262,448857,448452,448048,447644,447240, 446836,446433,446030,445628,445226,444824,444423,444022, 443622,443221,442821,442422,442023,441624,441226,440828, 440430,440033,439636,439239,438843,438447,438051,437656, 437261,436867,436473,436079,435686,435293,434900,434508, 434116,433724,433333,432942,432551,432161,431771,431382, 430992,430604,430215,429827,429439,429052,428665,428278, 427892,427506,427120,426735,426350,425965,425581,425197, 424813,424430,424047,423665,423283,422901,422519,422138, 421757,421377,420997,420617,420237,419858,419479,419101, 418723,418345,417968,417591,417214,416838,416462,416086, 415711,415336,414961,414586,414212,413839,413465,413092, 412720,412347,411975,411604,411232,410862,410491,410121, 409751,409381,409012,408643,408274,407906,407538,407170, 406803,406436,406069,405703,405337,404971,404606,404241, 403876,403512,403148,402784,402421,402058,401695,401333, 400970,400609,400247,399886,399525,399165,398805,398445, 398086,397727,397368,397009,396651,396293,395936,395579, 395222,394865,394509,394153,393798,393442,393087,392733, 392378,392024,391671,391317,390964,390612,390259,389907, 389556,389204,388853,388502,388152,387802,387452,387102, 386753,386404,386056,385707,385359,385012,384664,384317, 383971,383624,383278,382932,382587,382242,381897,381552, 381208,380864,380521,380177,379834,379492,379149,378807, 378466,378124,377783,377442,377102,376762,376422,376082, 375743,375404,375065,374727,374389,374051,373714,373377, 373040,372703,372367,372031,371695,371360,371025,370690, 370356,370022,369688,369355,369021,368688,368356,368023, 367691,367360,367028,366697,366366,366036,365706,365376, 365046,364717,364388,364059,363731,363403,363075,362747, 362420,362093,361766,361440,361114,360788,360463,360137, 359813,359488,359164,358840,358516,358193,357869,357547, 357224,356902,356580,356258,355937,355616,355295,354974, 354654,354334,354014,353695,353376,353057,352739,352420, 352103,351785,351468,351150,350834,350517,350201,349885, 349569,349254,348939,348624,348310,347995,347682,347368, 347055,346741,346429,346116,345804,345492,345180,344869, 344558,344247,343936,343626,343316,343006,342697,342388, 342079,341770,341462,341154,340846,340539,340231,339924, 339618,339311,339005,338700,338394,338089,337784,337479, 337175,336870,336566,336263,335959,335656,335354,335051, 334749,334447,334145,333844,333542,333242,332941,332641, 332341,332041,331741,331442,331143,330844,330546,330247, 329950,329652,329355,329057,328761,328464,328168,327872, 327576,327280,326985,326690,326395,326101,325807,325513, 325219,324926,324633,324340,324047,323755,323463,323171, 322879,322588,322297,322006,321716,321426,321136,320846, 320557,320267,319978,319690,319401,319113,318825,318538, 318250,317963,317676,317390,317103,316817,316532,316246, 315961,315676,315391,315106,314822,314538,314254,313971, 313688,313405,313122,312839,312557,312275,311994,311712, 311431,311150,310869,310589,310309,310029,309749,309470, 309190,308911,308633,308354,308076,307798,307521,307243, 306966,306689,306412,306136,305860,305584,305308,305033, 304758,304483,304208,303934,303659,303385,303112,302838, 302565,302292,302019,301747,301475,301203,300931,300660, 300388,300117,299847,299576,299306,299036,298766,298497, 298227,297958,297689,297421,297153,296884,296617,296349, 296082,295815,295548,295281,295015,294749,294483,294217, 293952,293686,293421,293157,292892,292628,292364,292100, 291837,291574,291311,291048,290785,290523,290261,289999, 289737,289476,289215,288954,288693,288433,288173,287913, 287653,287393,287134,286875,286616,286358,286099,285841, 285583,285326,285068,284811,284554,284298,284041,283785, 283529,283273,283017,282762,282507,282252,281998,281743, 281489,281235,280981,280728,280475,280222,279969,279716, 279464,279212,278960,278708,278457,278206,277955,277704, 277453,277203,276953,276703,276453,276204,275955,275706, 275457,275209,274960,274712,274465,274217,273970,273722, 273476,273229,272982,272736,272490,272244,271999,271753, 271508,271263,271018,270774,270530,270286,270042,269798, 269555,269312,269069,268826,268583,268341,268099,267857 }; static const int8_t ft2VibratoTable[256] = { 0,-2,-3,-5,-6,-8,-9,-11,-12,-14,-16,-17,-19,-20,-22,-23, -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42, -43,-44,-45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56, -56,-57,-58,-59,-59,-60,-60,-61,-61,-62,-62,-62,-63,-63, -63,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-63,-63, -63,-62,-62,-62,-61,-61,-60,-60,-59,-59,-58,-57,-56,-56, -55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42, -41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,-24,-23, -22,-20,-19,-17,-16,-14,-12,-11,-9,-8,-6,-5,-3,-2,0, 2,3,5,6,8,9,11,12,14,16,17,19,20,22,23,24,26,27,29,30, 32,33,34,36,37,38,39,41,42,43,44,45,46,47,48,49,50,51, 52,53,54,55,56,56,57,58,59,59,60,60,61,61,62,62,62,63, 63,63,64,64,64,64,64,64,64,64,64,64,64,63,63,63,62,62, 62,61,61,60,60,59,59,58,57,56,56,55,54,53,52,51,50,49, 48,47,46,45,44,43,42,41,39,38,37,36,34,33,32,30,29,27, 26,24,23,22,20,19,17,16,14,12,11,9,8,6,5,3,2 }; static const DWORD FineLinearSlideUpTable[16] = { 65536, 65595, 65654, 65714, 65773, 65832, 65892, 65951, 66011, 66071, 66130, 66190, 66250, 66309, 66369, 66429 }; static const DWORD FineLinearSlideDownTable[16] = { 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65123, 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645 }; static const DWORD LinearSlideUpTable[256] = { 65536, 65773, 66010, 66249, 66489, 66729, 66971, 67213, 67456, 67700, 67945, 68190, 68437, 68685, 68933, 69182, 69432, 69684, 69936, 70189, 70442, 70697, 70953, 71209, 71467, 71725, 71985, 72245, 72507, 72769, 73032, 73296, 73561, 73827, 74094, 74362, 74631, 74901, 75172, 75444, 75717, 75991, 76265, 76541, 76818, 77096, 77375, 77655, 77935, 78217, 78500, 78784, 79069, 79355, 79642, 79930, 80219, 80509, 80800, 81093, 81386, 81680, 81976, 82272, 82570, 82868, 83168, 83469, 83771, 84074, 84378, 84683, 84989, 85297, 85605, 85915, 86225, 86537, 86850, 87164, 87480, 87796, 88113, 88432, 88752, 89073, 89395, 89718, 90043, 90369, 90695, 91023, 91353, 91683, 92015, 92347, 92681, 93017, 93353, 93691, 94029, 94370, 94711, 95053, 95397, 95742, 96088, 96436, 96785, 97135, 97486, 97839, 98193, 98548, 98904, 99262, 99621, 99981, 100343, 100706, 101070, 101435, 101802, 102170, 102540, 102911, 103283, 103657, 104031, 104408, 104785, 105164, 105545, 105926, 106309, 106694, 107080, 107467, 107856, 108246, 108637, 109030, 109425, 109820, 110217, 110616, 111016, 111418, 111821, 112225, 112631, 113038, 113447, 113857, 114269, 114682, 115097, 115514, 115931, 116351, 116771, 117194, 117618, 118043, 118470, 118898, 119328, 119760, 120193, 120628, 121064, 121502, 121941, 122382, 122825, 123269, 123715, 124162, 124611, 125062, 125514, 125968, 126424, 126881, 127340, 127801, 128263, 128727, 129192, 129660, 130129, 130599, 131072, 131546, 132021, 132499, 132978, 133459, 133942, 134426, 134912, 135400, 135890, 136381, 136875, 137370, 137866, 138365, 138865, 139368, 139872, 140378, 140885, 141395, 141906, 142419, 142935, 143451, 143970, 144491, 145014, 145538, 146064, 146593, 147123, 147655, 148189, 148725, 149263, 149803, 150344, 150888, 151434, 151982, 152531, 153083, 153637, 154192, 154750, 155310, 155871, 156435, 157001, 157569, 158138, 158710, 159284, 159860, 160439, 161019, 161601, 162186, 162772, 163361, 163952, 164545, }; static const DWORD LinearSlideDownTable[256] = { 65536, 65299, 65064, 64830, 64596, 64363, 64131, 63900, 63670, 63440, 63212, 62984, 62757, 62531, 62305, 62081, 61857, 61634, 61412, 61191, 60970, 60751, 60532, 60314, 60096, 59880, 59664, 59449, 59235, 59021, 58809, 58597, 58385, 58175, 57965, 57757, 57548, 57341, 57134, 56928, 56723, 56519, 56315, 56112, 55910, 55709, 55508, 55308, 55108, 54910, 54712, 54515, 54318, 54123, 53928, 53733, 53540, 53347, 53154, 52963, 52772, 52582, 52392, 52204, 52015, 51828, 51641, 51455, 51270, 51085, 50901, 50717, 50535, 50353, 50171, 49990, 49810, 49631, 49452, 49274, 49096, 48919, 48743, 48567, 48392, 48218, 48044, 47871, 47698, 47526, 47355, 47185, 47014, 46845, 46676, 46508, 46340, 46173, 46007, 45841, 45676, 45511, 45347, 45184, 45021, 44859, 44697, 44536, 44376, 44216, 44056, 43898, 43740, 43582, 43425, 43268, 43112, 42957, 42802, 42648, 42494, 42341, 42189, 42037, 41885, 41734, 41584, 41434, 41285, 41136, 40988, 40840, 40693, 40546, 40400, 40254, 40109, 39965, 39821, 39677, 39534, 39392, 39250, 39108, 38967, 38827, 38687, 38548, 38409, 38270, 38132, 37995, 37858, 37722, 37586, 37450, 37315, 37181, 37047, 36913, 36780, 36648, 36516, 36384, 36253, 36122, 35992, 35862, 35733, 35604, 35476, 35348, 35221, 35094, 34968, 34842, 34716, 34591, 34466, 34342, 34218, 34095, 33972, 33850, 33728, 33606, 33485, 33364, 33244, 33124, 33005, 32886, 32768, 32649, 32532, 32415, 32298, 32181, 32065, 31950, 31835, 31720, 31606, 31492, 31378, 31265, 31152, 31040, 30928, 30817, 30706, 30595, 30485, 30375, 30266, 30157, 30048, 29940, 29832, 29724, 29617, 29510, 29404, 29298, 29192, 29087, 28982, 28878, 28774, 28670, 28567, 28464, 28361, 28259, 28157, 28056, 27955, 27854, 27754, 27654, 27554, 27455, 27356, 27257, 27159, 27061, 26964, 26866, 26770, 26673, 26577, 26481, 26386, 26291, 26196, 26102, }; static const int SpectrumSinusTable[256*2] = { 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 38, 38, 39, 39, 40, 41, 41, 42, 42, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 57, 57, 57, 56, 56, 55, 55, 55, 54, 54, 53, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 42, 42, 41, 41, 40, 39, 39, 38, 38, 37, 36, 36, 35, 34, 34, 33, 32, 32, 31, 30, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 22, 22, 21, 20, 20, 19, 18, 17, 17, 16, 15, 14, 14, 13, 12, 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 3, 3, 2, 1, 0, 0, -1, -1, -2, -3, -3, -4, -5, -6, -7, -7, -8, -9, -10, -10, -11, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -20, -20, -21, -22, -22, -23, -24, -25, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -39, -40, -41, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -53, -53, -53, -54, -54, -55, -55, -55, -56, -56, -57, -57, -57, -58, -58, -58, -59, -59, -59, -59, -60, -60, -60, -60, -61, -61, -61, -61, -61, -62, -62, -62, -62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -62, -62, -62, -62, -62, -62, -61, -61, -61, -61, -61, -60, -60, -60, -60, -59, -59, -59, -59, -58, -58, -58, -57, -57, -57, -56, -56, -55, -55, -55, -54, -54, -53, -53, -53, -52, -52, -51, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -36, -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13, -12, -11, -10, -10, -9, -8, -7, -7, -6, -5, -4, -3, -3, -2, -1, 0, }; #endif openjazz-20240919/ext/scale2x/000077500000000000000000000000001467303304200157715ustar00rootroot00000000000000openjazz-20240919/ext/scale2x/CMakeLists.txt000066400000000000000000000004721467303304200205340ustar00rootroot00000000000000# scale2x include(Find-Restrict-Keyword) add_library(scale2x STATIC scale2x.cpp scale2x.h scale3x.cpp scale3x.h scalebit.cpp scalebit.h ) target_compile_definitions(scale2x PRIVATE USE_SCALE_RANDOMWRITE restrict=${RESTRICT_KEYWORD}) target_include_directories(scale2x PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) openjazz-20240919/ext/scale2x/COPYING000066400000000000000000000432541467303304200170340ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. openjazz-20240919/ext/scale2x/scale2x.cpp000066400000000000000000001065541467303304200200510ustar00rootroot00000000000000/* * This file is part of the Scale2x project. * * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni * Copyright (C) 2015 Thomas Bernard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * This file contains a C and SSE2 implementation of the Scale2x effect. * * You can find an high level description of the effect at : * * http://www.scale2x.it/ */ #include "scale2x.h" #include /***************************************************************************/ /* Scale2x C implementation */ /** * Define the macro USE_SCALE_RANDOMWRITE to enable an optimized version that * writes memory in random order. * * This version can operate at double speed if you write in system memory. * But it's a lot slower if you write in video memory, becasue it writes * memory in two different locations at the same time, messing up with the * video memory write combining. * * Enable it only if you are sure to never write directly in video memory. */ /* #define USE_SCALE_RANDOMWRITE */ #ifdef USE_SCALE_RANDOMWRITE static inline void scale2x_8_def_whole(scale2x_uint8* restrict dst0, scale2x_uint8* restrict dst1, const scale2x_uint8* restrict src0, const scale2x_uint8* restrict src1, const scale2x_uint8* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst0[0] = src1[0] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[0] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 2; dst1 += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 2; dst1 += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[0] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[0] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale2x_8_def_border(scale2x_uint8* restrict dst, const scale2x_uint8* restrict src0, const scale2x_uint8* restrict src1, const scale2x_uint8* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[0] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } } #endif static inline void scale2x_8_def_center(scale2x_uint8* restrict dst, const scale2x_uint8* restrict src0, const scale2x_uint8* restrict src1, const scale2x_uint8* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0]; dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } } #ifdef USE_SCALE_RANDOMWRITE static inline void scale2x_16_def_whole(scale2x_uint16* restrict dst0, scale2x_uint16* restrict dst1, const scale2x_uint16* restrict src0, const scale2x_uint16* restrict src1, const scale2x_uint16* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst0[0] = src1[0] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[0] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 2; dst1 += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 2; dst1 += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[0] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[0] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale2x_16_def_border(scale2x_uint16* restrict dst, const scale2x_uint16* restrict src0, const scale2x_uint16* restrict src1, const scale2x_uint16* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[0] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } } #endif static inline void scale2x_16_def_center(scale2x_uint16* restrict dst, const scale2x_uint16* restrict src0, const scale2x_uint16* restrict src1, const scale2x_uint16* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0]; dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } } #ifdef USE_SCALE_RANDOMWRITE static inline void scale2x_32_def_whole(scale2x_uint32* restrict dst0, scale2x_uint32* restrict dst1, const scale2x_uint32* restrict src0, const scale2x_uint32* restrict src1, const scale2x_uint32* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst0[0] = src1[0] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[0] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 2; dst1 += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 2; dst1 += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst0[1] = src1[0] == src0[0] ? src0[0] : src1[0]; dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; dst1[1] = src1[0] == src2[0] ? src2[0] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale2x_32_def_border(scale2x_uint32* restrict dst, const scale2x_uint32* restrict src0, const scale2x_uint32* restrict src1, const scale2x_uint32* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; dst[1] = src1[0] == src0[0] ? src0[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } } #endif static inline void scale2x_32_def_center(scale2x_uint32* restrict dst, const scale2x_uint32* restrict src0, const scale2x_uint32* restrict src1, const scale2x_uint32* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0]; dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } ++src0; ++src1; ++src2; dst += 2; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; } } /** * Scale by a factor of 2 a row of pixels of 8 bits. * The function is implemented in C. * The pixels over the left and right borders are assumed of the same color of * the pixels on the border. * Note that the implementation is optimized to write data sequentially to * maximize the bandwidth on video memory. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. * It must be at least 2. * \param dst0 First destination row, double length in pixels. * \param dst1 Second destination row, double length in pixels. */ void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_8_def_whole(dst0, dst1, src0, src1, src2, count); #else scale2x_8_def_border(dst0, src0, src1, src2, count); scale2x_8_def_border(dst1, src2, src1, src0, count); #endif } /** * Scale by a factor of 2 a row of pixels of 16 bits. * This function operates like scale2x_8_def() but for 16 bits pixels. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. * It must be at least 2. * \param dst0 First destination row, double length in pixels. * \param dst1 Second destination row, double length in pixels. */ void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_16_def_whole(dst0, dst1, src0, src1, src2, count); #else scale2x_16_def_border(dst0, src0, src1, src2, count); scale2x_16_def_border(dst1, src2, src1, src0, count); #endif } /** * Scale by a factor of 2 a row of pixels of 32 bits. * This function operates like scale2x_8_def() but for 32 bits pixels. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. * It must be at least 2. * \param dst0 First destination row, double length in pixels. * \param dst1 Second destination row, double length in pixels. */ void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_32_def_whole(dst0, dst1, src0, src1, src2, count); #else scale2x_32_def_border(dst0, src0, src1, src2, count); scale2x_32_def_border(dst1, src2, src1, src0, count); #endif } /** * Scale by a factor of 2x3 a row of pixels of 8 bits. * \note Like scale2x_8_def(); */ void scale2x3_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_8_def_whole(dst0, dst2, src0, src1, src2, count); scale2x_8_def_center(dst1, src0, src1, src2, count); #else scale2x_8_def_border(dst0, src0, src1, src2, count); scale2x_8_def_center(dst1, src0, src1, src2, count); scale2x_8_def_border(dst2, src2, src1, src0, count); #endif } /** * Scale by a factor of 2x3 a row of pixels of 16 bits. * \note Like scale2x_16_def(); */ void scale2x3_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_16_def_whole(dst0, dst2, src0, src1, src2, count); scale2x_16_def_center(dst1, src0, src1, src2, count); #else scale2x_16_def_border(dst0, src0, src1, src2, count); scale2x_16_def_center(dst1, src0, src1, src2, count); scale2x_16_def_border(dst2, src2, src1, src0, count); #endif } /** * Scale by a factor of 2x3 a row of pixels of 32 bits. * \note Like scale2x_32_def(); */ void scale2x3_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_32_def_whole(dst0, dst2, src0, src1, src2, count); scale2x_32_def_center(dst1, src0, src1, src2, count); #else scale2x_32_def_border(dst0, src0, src1, src2, count); scale2x_32_def_center(dst1, src0, src1, src2, count); scale2x_32_def_border(dst2, src2, src1, src0, count); #endif } /** * Scale by a factor of 2x4 a row of pixels of 8 bits. * \note Like scale2x_8_def(); */ void scale2x4_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_8_def_whole(dst0, dst3, src0, src1, src2, count); scale2x_8_def_center(dst1, src0, src1, src2, count); scale2x_8_def_center(dst2, src0, src1, src2, count); #else scale2x_8_def_border(dst0, src0, src1, src2, count); scale2x_8_def_center(dst1, src0, src1, src2, count); scale2x_8_def_center(dst2, src0, src1, src2, count); scale2x_8_def_border(dst3, src2, src1, src0, count); #endif } /** * Scale by a factor of 2x4 a row of pixels of 16 bits. * \note Like scale2x_16_def(); */ void scale2x4_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_16_def_whole(dst0, dst3, src0, src1, src2, count); scale2x_16_def_center(dst1, src0, src1, src2, count); scale2x_16_def_center(dst2, src0, src1, src2, count); #else scale2x_16_def_border(dst0, src0, src1, src2, count); scale2x_16_def_center(dst1, src0, src1, src2, count); scale2x_16_def_center(dst2, src0, src1, src2, count); scale2x_16_def_border(dst3, src2, src1, src0, count); #endif } /** * Scale by a factor of 2x4 a row of pixels of 32 bits. * \note Like scale2x_32_def(); */ void scale2x4_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale2x_32_def_whole(dst0, dst3, src0, src1, src2, count); scale2x_32_def_center(dst1, src0, src1, src2, count); scale2x_32_def_center(dst2, src0, src1, src2, count); #else scale2x_32_def_border(dst0, src0, src1, src2, count); scale2x_32_def_center(dst1, src0, src1, src2, count); scale2x_32_def_center(dst2, src0, src1, src2, count); scale2x_32_def_border(dst3, src2, src1, src0, count); #endif } /***************************************************************************/ /* Scale2x SSE2 implementation */ #ifdef USE_SCALE2X_SSE2 /* * Apply the Scale2x effect at a single row. * This function must be called only by the other scale2x functions. * * Considering the pixel map : * * ABC (src0) * DEF (src1) * GHI (src2) * * this functions compute 2 new pixels in substitution of the source pixel E * like this map : * * ab (dst) */ /** * Include SSE2 intrinsics. * * A nice reference is available at: * https://software.intel.com/sites/landingpage/IntrinsicsGuide/ */ #include /** * SCALE2X_SEL(A, B, cond) = cond ? A : B; */ #define SCALE2X_SEL(A, B, cond) \ _mm_or_si128(_mm_and_si128((cond), (A)), _mm_andnot_si128((cond), (B))) static inline void scale2x_8_sse2_border(scale2x_uint8* dst, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { __m128i B, D, E, F, H, a, b; __m128i BDeq, BFeq, BHeq, DFeq; const __m128i mask_first = _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff); const __m128i mask_last = _mm_set_epi8(0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const __m128i* s0 = (const __m128i*)src0; const __m128i* s1 = (const __m128i*)src1; const __m128i* s2 = (const __m128i*)src2; __m128i* d = (__m128i*)dst; /* count must be aligned */ assert(count >= 32); assert(count % 16 == 0); /* all memory must be aligned */ assert(scale2x_align_ptr(dst) == dst); assert(scale2x_align_ptr(src0) == src0); assert(scale2x_align_ptr(src1) == src1); assert(scale2x_align_ptr(src2) == src2); /* first run */ B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_and_si128(E, mask_first), _mm_slli_si128(E, 1)); F = _mm_or_si128(_mm_srli_si128(E, 1), _mm_slli_si128(s1[1], 15)); ++s0; ++s1; ++s2; BDeq = _mm_cmpeq_epi8(B, D); BFeq = _mm_cmpeq_epi8(B, F); BHeq = _mm_cmpeq_epi8(B, H); DFeq = _mm_cmpeq_epi8(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi8(a, b); d[1] = _mm_unpackhi_epi8(a, b); d += 2; /* central run */ for (count -= 32; count > 0; count -= 16) { B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_srli_si128(s1[-1], 15), _mm_slli_si128(E, 1)); F = _mm_or_si128(_mm_srli_si128(E, 1), _mm_slli_si128(s1[1], 15)); ++s0; ++s1; ++s2; BDeq = _mm_cmpeq_epi8(B, D); BFeq = _mm_cmpeq_epi8(B, F); BHeq = _mm_cmpeq_epi8(B, H); DFeq = _mm_cmpeq_epi8(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi8(a, b); d[1] = _mm_unpackhi_epi8(a, b); d += 2; } /* last run */ B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_srli_si128(s1[-1], 15), _mm_slli_si128(E, 1)); F = _mm_or_si128(_mm_srli_si128(E, 1), _mm_and_si128(E, mask_last)); BDeq = _mm_cmpeq_epi8(B, D); BFeq = _mm_cmpeq_epi8(B, F); BHeq = _mm_cmpeq_epi8(B, H); DFeq = _mm_cmpeq_epi8(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi8(a, b); d[1] = _mm_unpackhi_epi8(a, b); } static inline void scale2x_16_sse2_border(scale2x_uint16* dst, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { __m128i B, D, E, F, H, a, b; __m128i BDeq, BFeq, BHeq, DFeq; const __m128i mask_first = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, 0xffff); const __m128i mask_last = _mm_set_epi16(0xffff, 0, 0, 0, 0, 0, 0, 0); const __m128i* s0 = (const __m128i*)src0; const __m128i* s1 = (const __m128i*)src1; const __m128i* s2 = (const __m128i*)src2; __m128i* d = (__m128i*)dst; /* count must be aligned */ assert(count >= 16); assert(count % 8 == 0); /* all memory must be aligned */ assert(scale2x_align_ptr(dst) == dst); assert(scale2x_align_ptr(src0) == src0); assert(scale2x_align_ptr(src1) == src1); assert(scale2x_align_ptr(src2) == src2); /* first run */ B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_and_si128(E, mask_first), _mm_slli_si128(E, 2)); F = _mm_or_si128(_mm_srli_si128(E, 2), _mm_slli_si128(s1[1], 14)); ++s0; ++s1; ++s2; BDeq = _mm_cmpeq_epi16(B, D); BFeq = _mm_cmpeq_epi16(B, F); BHeq = _mm_cmpeq_epi16(B, H); DFeq = _mm_cmpeq_epi16(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi16(a, b); d[1] = _mm_unpackhi_epi16(a, b); d += 2; /* central run */ for (count -= 16; count > 0; count -= 8) { B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_srli_si128(s1[-1], 14), _mm_slli_si128(E, 2)); F = _mm_or_si128(_mm_srli_si128(E, 2), _mm_slli_si128(s1[1], 14)); ++s0; ++s1; ++s2; BDeq = _mm_cmpeq_epi16(B, D); BFeq = _mm_cmpeq_epi16(B, F); BHeq = _mm_cmpeq_epi16(B, H); DFeq = _mm_cmpeq_epi16(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi16(a, b); d[1] = _mm_unpackhi_epi16(a, b); d += 2; } /* last run */ B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_srli_si128(s1[-1], 14), _mm_slli_si128(E, 2)); F = _mm_or_si128(_mm_srli_si128(E, 2), _mm_and_si128(E, mask_last)); BDeq = _mm_cmpeq_epi16(B, D); BFeq = _mm_cmpeq_epi16(B, F); BHeq = _mm_cmpeq_epi16(B, H); DFeq = _mm_cmpeq_epi16(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi16(a, b); d[1] = _mm_unpackhi_epi16(a, b); } static inline void scale2x_32_sse2_border(scale2x_uint32* dst, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { __m128i B, D, E, F, H, a, b; __m128i BDeq, BFeq, BHeq, DFeq; const __m128i mask_first = _mm_set_epi32(0, 0, 0, 0xffffffff); const __m128i mask_last = _mm_set_epi32(0xffffffff, 0, 0, 0); const __m128i* s0 = (const __m128i*)src0; const __m128i* s1 = (const __m128i*)src1; const __m128i* s2 = (const __m128i*)src2; __m128i* d = (__m128i*)dst; /* count must be aligned */ assert(count >= 8); assert(count % 4 == 0); /* memory must be aligned */ assert(scale2x_align_ptr(dst) == dst); assert(scale2x_align_ptr(src0) == src0); assert(scale2x_align_ptr(src1) == src1); assert(scale2x_align_ptr(src2) == src2); /* first run */ B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_and_si128(E, mask_first), _mm_slli_si128(E, 4)); F = _mm_or_si128(_mm_srli_si128(E, 4), _mm_slli_si128(s1[1], 12)); ++s0; ++s1; ++s2; BDeq = _mm_cmpeq_epi32(B, D); BFeq = _mm_cmpeq_epi32(B, F); BHeq = _mm_cmpeq_epi32(B, H); DFeq = _mm_cmpeq_epi32(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi32(a, b); d[1] = _mm_unpackhi_epi32(a, b); d += 2; /* central run */ for (count -= 8; count > 0; count -= 4) { B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_srli_si128(s1[-1], 12), _mm_slli_si128(E, 4)); F = _mm_or_si128(_mm_srli_si128(E, 4), _mm_slli_si128(s1[1], 12)); ++s0; ++s1; ++s2; BDeq = _mm_cmpeq_epi32(B, D); BFeq = _mm_cmpeq_epi32(B, F); BHeq = _mm_cmpeq_epi32(B, H); DFeq = _mm_cmpeq_epi32(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi32(a, b); d[1] = _mm_unpackhi_epi32(a, b); d += 2; } /* last run */ B = s0[0]; E = s1[0]; H = s2[0]; D = _mm_or_si128(_mm_srli_si128(s1[-1], 12), _mm_slli_si128(E, 4)); F = _mm_or_si128(_mm_srli_si128(E, 4), _mm_and_si128(E, mask_last)); BDeq = _mm_cmpeq_epi32(B, D); BFeq = _mm_cmpeq_epi32(B, F); BHeq = _mm_cmpeq_epi32(B, H); DFeq = _mm_cmpeq_epi32(D, F); a = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BDeq))); b = SCALE2X_SEL(B, E, _mm_andnot_si128(DFeq, _mm_andnot_si128(BHeq, BFeq))); d[0] = _mm_unpacklo_epi32(a, b); d[1] = _mm_unpackhi_epi32(a, b); } /** * Scale by a factor of 2 a row of pixels of 8 bits. * This is a very fast SSE2 implementation. * The implementation uses a combination of cmp/and/not operations to * completly remove the need of conditional jumps. This trick give the * major speed improvement. * Also, using the 16 bytes SSE2 registers more than one pixel are computed * at the same time. * Before calling this function you must ensure that the currenct CPU supports * the SSE2 instruction set. * All the memory buffer passed must be aligned at 16 bytes. * The pixels over the left and right borders are assumed of the same color of * the pixels on the border. * Note that the implementation is optimized to write data sequentially to * maximize the bandwidth on video memory. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. It must * be at least 16 and a multiple of 8. * \param dst0 First destination row, double length in pixels. * \param dst1 Second destination row, double length in pixels. */ void scale2x_8_sse2(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { if (count % 16 != 0 || count < 32) { scale2x_8_def(dst0, dst1, src0, src1, src2, count); } else { scale2x_8_sse2_border(dst0, src0, src1, src2, count); scale2x_8_sse2_border(dst1, src2, src1, src0, count); } } /** * Scale by a factor of 2 a row of pixels of 16 bits. * This function operates like scale2x_8_sse2() but for 16 bits pixels. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. It must * be at least 8 and a multiple of 4. * \param dst0 First destination row, double length in pixels. * \param dst1 Second destination row, double length in pixels. */ void scale2x_16_sse2(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { if (count % 8 != 0 || count < 16) { scale2x_16_def(dst0, dst1, src0, src1, src2, count); } else { scale2x_16_sse2_border(dst0, src0, src1, src2, count); scale2x_16_sse2_border(dst1, src2, src1, src0, count); } } /** * Scale by a factor of 2 a row of pixels of 32 bits. * This function operates like scale2x_8_sse2() but for 32 bits pixels. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. It must * be at least 4 and a multiple of 2. * \param dst0 First destination row, double length in pixels. * \param dst1 Second destination row, double length in pixels. */ void scale2x_32_sse2(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { if (count % 4 != 0 || count < 8) { scale2x_32_def(dst0, dst1, src0, src1, src2, count); } else { scale2x_32_sse2_border(dst0, src0, src1, src2, count); scale2x_32_sse2_border(dst1, src2, src1, src0, count); } } /** * Scale by a factor of 2x3 a row of pixels of 8 bits. * This function operates like scale2x_8_sse2() but with an expansion * factor of 2x3 instead of 2x2. */ void scale2x3_8_sse2(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { if (count % 16 != 0 || count < 32) { scale2x3_8_def(dst0, dst1, dst2, src0, src1, src2, count); } else { scale2x_8_sse2_border(dst0, src0, src1, src2, count); scale2x_8_def_center(dst1, src0, src1, src2, count); scale2x_8_sse2_border(dst2, src2, src1, src0, count); } } /** * Scale by a factor of 2x3 a row of pixels of 16 bits. * This function operates like scale2x_16_sse2() but with an expansion * factor of 2x3 instead of 2x2. */ void scale2x3_16_sse2(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { if (count % 8 != 0 || count < 16) { scale2x3_16_def(dst0, dst1, dst2, src0, src1, src2, count); } else { scale2x_16_sse2_border(dst0, src0, src1, src2, count); scale2x_16_def_center(dst1, src0, src1, src2, count); scale2x_16_sse2_border(dst2, src2, src1, src0, count); } } /** * Scale by a factor of 2x3 a row of pixels of 32 bits. * This function operates like scale2x_32_sse2() but with an expansion * factor of 2x3 instead of 2x2. */ void scale2x3_32_sse2(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { if (count % 4 != 0 || count < 8) { scale2x3_32_def(dst0, dst1, dst2, src0, src1, src2, count); } else { scale2x_32_sse2_border(dst0, src0, src1, src2, count); scale2x_32_def_center(dst1, src0, src1, src2, count); scale2x_32_sse2_border(dst2, src2, src1, src0, count); } } /** * Scale by a factor of 2x4 a row of pixels of 8 bits. * This function operates like scale2x_8_sse2() but with an expansion * factor of 2x4 instead of 2x2. */ void scale2x4_8_sse2(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) { if (count % 16 != 0 || count < 32) { scale2x4_8_def(dst0, dst1, dst2, dst3, src0, src1, src2, count); } else { scale2x_8_sse2_border(dst0, src0, src1, src2, count); scale2x_8_def_center(dst1, src0, src1, src2, count); scale2x_8_def_center(dst2, src0, src1, src2, count); scale2x_8_sse2_border(dst3, src2, src1, src0, count); } } /** * Scale by a factor of 2x4 a row of pixels of 16 bits. * This function operates like scale2x_16_sse2() but with an expansion * factor of 2x4 instead of 2x2. */ void scale2x4_16_sse2(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) { if (count % 8 != 0 || count < 16) { scale2x4_16_def(dst0, dst1, dst2, dst3, src0, src1, src2, count); } else { scale2x_16_sse2_border(dst0, src0, src1, src2, count); scale2x_16_def_center(dst1, src0, src1, src2, count); scale2x_16_def_center(dst2, src0, src1, src2, count); scale2x_16_sse2_border(dst3, src2, src1, src0, count); } } /** * Scale by a factor of 2x4 a row of pixels of 32 bits. * This function operates like scale2x_32_sse2() but with an expansion * factor of 2x4 instead of 2x2. */ void scale2x4_32_sse2(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) { if (count % 4 != 0 || count < 8) { scale2x4_32_def(dst0, dst1, dst2, dst3, src0, src1, src2, count); } else { scale2x_32_sse2_border(dst0, src0, src1, src2, count); scale2x_32_def_center(dst1, src0, src1, src2, count); scale2x_32_def_center(dst2, src0, src1, src2, count); scale2x_32_sse2_border(dst3, src2, src1, src0, count); } } #endif openjazz-20240919/ext/scale2x/scale2x.h000066400000000000000000000120511467303304200175020ustar00rootroot00000000000000/* * This file is part of the Scale2x project. * * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __SCALE2X_H #define __SCALE2X_H // OpenJazz modification #ifndef restrict # if (defined(_WIN32) && defined(_MSC_VER)) || defined(__GNUC__) # define restrict __restrict # else # define restrict // ignore # endif #endif typedef unsigned char scale2x_uint8; typedef unsigned short scale2x_uint16; typedef unsigned scale2x_uint32; /** * Enable the SSE2 implementation. */ #if defined(__GNUC__) && defined(__SSE2__) #define USE_SCALE2X_SSE2 1 #endif /** * Memory alignment required. */ #ifdef USE_SCALE2X_SSE2 #define SCALE2X_ALIGN_SIZE 16 #else #define SCALE2X_ALIGN_SIZE 1 #endif /** * Extra allocation to ensure alignment. */ #define SCALE2X_ALIGN_ALLOC (SCALE2X_ALIGN_SIZE - 1) /** * Align a pointer to bytes. */ static inline void* scale2x_align_ptr(const void* ptr) { #ifdef USE_SCALE2X_SSE2 __asm__( "add $15, %0\n" "and $-16, %0\n" : "+r" (ptr) : : "cc" ); #endif return (void*)ptr; } /** * Align a size in bytes. */ static inline unsigned scale2x_align_size(unsigned size) { #ifdef USE_SCALE2X_SSE2 __asm__( "add $15, %0\n" "and $-16, %0\n" : "+r" (size) : : "cc" ); #endif return size; } void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); void scale2x3_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x3_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x3_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); void scale2x4_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x4_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x4_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); #ifdef USE_SCALE2X_SSE2 void scale2x_8_sse2(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x_16_sse2(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x_32_sse2(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); void scale2x3_8_sse2(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x3_16_sse2(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x3_32_sse2(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); void scale2x4_8_sse2(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x4_16_sse2(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x4_32_sse2(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); #endif #endif openjazz-20240919/ext/scale2x/scale3x.cpp000066400000000000000000000565211467303304200200500ustar00rootroot00000000000000/* * This file is part of the Scale2x project. * * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * This file contains a C and SSE2 implementation of the Scale2x effect. * * You can find an high level description of the effect at : * * http://www.scale2x.it/ */ #include "scale3x.h" #include /***************************************************************************/ /* Scale3x C implementation */ /** * Define the macro USE_SCALE_RANDOMWRITE to enable * an optimized version which writes memory in random order. * This version is a little faster if you write in system memory. * But it's a lot slower if you write in video memory. * So, enable it only if you are sure to never write directly in video memory. */ /* #define USE_SCALE_RANDOMWRITE */ #ifdef USE_SCALE_RANDOMWRITE static inline void scale3x_8_def_whole(scale3x_uint8* restrict dst0, scale3x_uint8* restrict dst1, scale3x_uint8* restrict dst2, const scale3x_uint8* restrict src0, const scale3x_uint8* restrict src1, const scale3x_uint8* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst0[0] = src1[0]; dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; dst2[0] = src1[0]; dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 3; dst1 += 3; dst2 += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 3; dst1 += 3; dst2 += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst0[2] = src1[0]; dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; dst2[2] = src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale3x_8_def_border(scale3x_uint8* restrict dst, const scale3x_uint8* restrict src0, const scale3x_uint8* restrict src1, const scale3x_uint8* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0]; dst[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst[2] = src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale3x_8_def_center(scale3x_uint8* restrict dst, const scale3x_uint8* restrict src0, const scale3x_uint8* restrict src1, const scale3x_uint8* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } } #endif #ifdef USE_SCALE_RANDOMWRITE static inline void scale3x_16_def_whole(scale3x_uint16* restrict dst0, scale3x_uint16* restrict dst1, scale3x_uint16* restrict dst2, const scale3x_uint16* restrict src0, const scale3x_uint16* restrict src1, const scale3x_uint16* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst0[0] = src1[0]; dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; dst2[0] = src1[0]; dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 3; dst1 += 3; dst2 += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 3; dst1 += 3; dst2 += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst0[2] = src1[0]; dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; dst2[2] = src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale3x_16_def_border(scale3x_uint16* restrict dst, const scale3x_uint16* restrict src0, const scale3x_uint16* restrict src1, const scale3x_uint16* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0]; dst[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst[2] = src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale3x_16_def_center(scale3x_uint16* restrict dst, const scale3x_uint16* restrict src0, const scale3x_uint16* restrict src1, const scale3x_uint16* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } } #endif #ifdef USE_SCALE_RANDOMWRITE static inline void scale3x_32_def_whole(scale3x_uint32* restrict dst0, scale3x_uint32* restrict dst1, scale3x_uint32* restrict dst2, const scale3x_uint32* restrict src0, const scale3x_uint32* restrict src1, const scale3x_uint32* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst0[0] = src1[0]; dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; dst2[0] = src1[0]; dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 3; dst1 += 3; dst2 += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } ++src0; ++src1; ++src2; dst0 += 3; dst1 += 3; dst2 += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst0[2] = src1[0]; dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst1[1] = src1[0]; dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; dst2[2] = src1[0]; } else { dst0[0] = src1[0]; dst0[1] = src1[0]; dst0[2] = src1[0]; dst1[0] = src1[0]; dst1[1] = src1[0]; dst1[2] = src1[0]; dst2[0] = src1[0]; dst2[1] = src1[0]; dst2[2] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale3x_32_def_border(scale3x_uint32* restrict dst, const scale3x_uint32* restrict src0, const scale3x_uint32* restrict src1, const scale3x_uint32* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = src1[0]; dst[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; dst[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; dst[2] = src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } } #endif #ifndef USE_SCALE_RANDOMWRITE static inline void scale3x_32_def_center(scale3x_uint32* restrict dst, const scale3x_uint32* restrict src0, const scale3x_uint32* restrict src1, const scale3x_uint32* restrict src2, unsigned count) { assert(count >= 2); /* first pixel */ if (src0[0] != src2[0] && src1[0] != src1[1]) { dst[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; /* central pixels */ count -= 2; while (count) { if (src0[0] != src2[0] && src1[-1] != src1[1]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } ++src0; ++src1; ++src2; dst += 3; --count; } /* last pixel */ if (src0[0] != src2[0] && src1[-1] != src1[0]) { dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; dst[1] = src1[0]; dst[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; } else { dst[0] = src1[0]; dst[1] = src1[0]; dst[2] = src1[0]; } } #endif /** * Scale by a factor of 3 a row of pixels of 8 bits. * The function is implemented in C. * The pixels over the left and right borders are assumed of the same color of * the pixels on the border. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. * It must be at least 2. * \param dst0 First destination row, triple length in pixels. * \param dst1 Second destination row, triple length in pixels. * \param dst2 Third destination row, triple length in pixels. */ void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale3x_8_def_whole(dst0, dst1, dst2, src0, src1, src2, count); #else scale3x_8_def_border(dst0, src0, src1, src2, count); scale3x_8_def_center(dst1, src0, src1, src2, count); scale3x_8_def_border(dst2, src2, src1, src0, count); #endif } /** * Scale by a factor of 3 a row of pixels of 16 bits. * This function operates like scale3x_8_def() but for 16 bits pixels. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. * It must be at least 2. * \param dst0 First destination row, triple length in pixels. * \param dst1 Second destination row, triple length in pixels. * \param dst2 Third destination row, triple length in pixels. */ void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale3x_16_def_whole(dst0, dst1, dst2, src0, src1, src2, count); #else scale3x_16_def_border(dst0, src0, src1, src2, count); scale3x_16_def_center(dst1, src0, src1, src2, count); scale3x_16_def_border(dst2, src2, src1, src0, count); #endif } /** * Scale by a factor of 3 a row of pixels of 32 bits. * This function operates like scale3x_8_def() but for 32 bits pixels. * \param src0 Pointer at the first pixel of the previous row. * \param src1 Pointer at the first pixel of the current row. * \param src2 Pointer at the first pixel of the next row. * \param count Length in pixels of the src0, src1 and src2 rows. * It must be at least 2. * \param dst0 First destination row, triple length in pixels. * \param dst1 Second destination row, triple length in pixels. * \param dst2 Third destination row, triple length in pixels. */ void scale3x_32_def(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count) { #ifdef USE_SCALE_RANDOMWRITE scale3x_32_def_whole(dst0, dst1, dst2, src0, src1, src2, count); #else scale3x_32_def_border(dst0, src0, src1, src2, count); scale3x_32_def_center(dst1, src0, src1, src2, count); scale3x_32_def_border(dst2, src2, src1, src0, count); #endif } openjazz-20240919/ext/scale2x/scale3x.h000066400000000000000000000027671467303304200175200ustar00rootroot00000000000000/* * This file is part of the Scale2x project. * * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __SCALE3X_H #define __SCALE3X_H // OpenJazz modification #ifndef restrict # if (defined(_WIN32) && defined(_MSC_VER)) || defined(__GNUC__) # define restrict __restrict # else # define restrict // ignore # endif #endif typedef unsigned char scale3x_uint8; typedef unsigned short scale3x_uint16; typedef unsigned scale3x_uint32; void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count); void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count); void scale3x_32_def(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count); #endif openjazz-20240919/ext/scale2x/scalebit.cpp000066400000000000000000000436701467303304200202750ustar00rootroot00000000000000/* * This file is part of the Scale2x project. * * Copyright (C) 2003, 2004 Andrea Mazzoleni * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * This file contains an example implementation of the Scale effect * applyed to a generic bitmap. * * You can find an high level description of the effect at : * * http://www.scale2x.it/ */ #include "scale2x.h" #include "scale3x.h" #include #include #define SSDST(bits, num) (scale2x_uint ## bits *)dst ## num #define SSSRC(bits, num) (const scale2x_uint ## bits *)src ## num /** * Apply the Scale2x effect on a group of rows. Used internally. */ static inline void stage_scale2x(void* dst0, void* dst1, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) { switch (pixel) { #ifdef USE_SCALE2X_SSE2 case 1 : scale2x_8_sse2(SSDST(8, 0), SSDST(8, 1), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale2x_16_sse2(SSDST(16, 0), SSDST(16, 1), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale2x_32_sse2(SSDST(32, 0), SSDST(32, 1), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; #else case 1 : scale2x_8_def(SSDST(8, 0), SSDST(8, 1), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale2x_16_def(SSDST(16, 0), SSDST(16, 1), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale2x_32_def(SSDST(32, 0), SSDST(32, 1), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; #endif } } /** * Apply the Scale2x3 effect on a group of rows. Used internally. */ static inline void stage_scale2x3(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) { switch (pixel) { #ifdef USE_SCALE2X_SSE2 case 1 : scale2x3_8_sse2(SSDST(8, 0), SSDST(8, 1), SSDST(8, 2), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale2x3_16_sse2(SSDST(16, 0), SSDST(16, 1), SSDST(16, 2), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale2x3_32_sse2(SSDST(32, 0), SSDST(32, 1), SSDST(32, 2), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; #else case 1 : scale2x3_8_def(SSDST(8, 0), SSDST(8, 1), SSDST(8, 2), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale2x3_16_def(SSDST(16, 0), SSDST(16, 1), SSDST(16, 2), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale2x3_32_def(SSDST(32, 0), SSDST(32, 1), SSDST(32, 2), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; #endif } } /** * Apply the Scale2x4 effect on a group of rows. Used internally. */ static inline void stage_scale2x4(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) { switch (pixel) { #ifdef USE_SCALE2X_SSE2 case 1 : scale2x4_8_sse2(SSDST(8, 0), SSDST(8, 1), SSDST(8, 2), SSDST(8, 3), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale2x4_16_sse2(SSDST(16, 0), SSDST(16, 1), SSDST(16, 2), SSDST(16, 3), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale2x4_32_sse2(SSDST(32, 0), SSDST(32, 1), SSDST(32, 2), SSDST(32, 3), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; #else case 1 : scale2x4_8_def(SSDST(8, 0), SSDST(8, 1), SSDST(8, 2), SSDST(8, 3), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale2x4_16_def(SSDST(16, 0), SSDST(16, 1), SSDST(16, 2), SSDST(16, 3), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale2x4_32_def(SSDST(32, 0), SSDST(32, 1), SSDST(32, 2), SSDST(32, 3), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; #endif } } /** * Apply the Scale3x effect on a group of rows. Used internally. */ static inline void stage_scale3x(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) { switch (pixel) { case 1 : scale3x_8_def(SSDST(8, 0), SSDST(8, 1), SSDST(8, 2), SSSRC(8, 0), SSSRC(8, 1), SSSRC(8, 2), pixel_per_row); break; case 2 : scale3x_16_def(SSDST(16, 0), SSDST(16, 1), SSDST(16, 2), SSSRC(16, 0), SSSRC(16, 1), SSSRC(16, 2), pixel_per_row); break; case 4 : scale3x_32_def(SSDST(32, 0), SSDST(32, 1), SSDST(32, 2), SSSRC(32, 0), SSSRC(32, 1), SSSRC(32, 2), pixel_per_row); break; } } /** * Apply the Scale4x effect on a group of rows. Used internally. */ static inline void stage_scale4x(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, const void* src3, unsigned pixel, unsigned pixel_per_row) { stage_scale2x(dst0, dst1, src0, src1, src2, pixel, 2 * pixel_per_row); stage_scale2x(dst2, dst3, src1, src2, src3, pixel, 2 * pixel_per_row); } #define SCDST(i) (dst + (i) * dst_slice) #define SCSRC(i) (src + (i) * src_slice) #define SCMID(i) (mid[(i)]) /** * Apply the Scale2x effect on a bitmap. * The destination bitmap is filled with the scaled version of the source bitmap. * The source bitmap isn't modified. * The destination bitmap must be manually allocated before calling the function, * note that the resulting size is exactly 2x2 times the size of the source bitmap. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ static void scale2x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { unsigned char* dst = (unsigned char*)void_dst; const unsigned char* src = (const unsigned char*)void_src; unsigned count; assert(height >= 2); count = height; stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); dst = SCDST(2); count -= 2; while (count) { stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); dst = SCDST(2); src = SCSRC(1); --count; } stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); } /** * Apply the Scale2x3 effect on a bitmap. * The destination bitmap is filled with the scaled version of the source bitmap. * The source bitmap isn't modified. * The destination bitmap must be manually allocated before calling the function, * note that the resulting size is exactly 2x3 times the size of the source bitmap. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ static void scale2x3(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { unsigned char* dst = (unsigned char*)void_dst; const unsigned char* src = (const unsigned char*)void_src; unsigned count; assert(height >= 2); count = height; stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); dst = SCDST(3); count -= 2; while (count) { stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); dst = SCDST(3); src = SCSRC(1); --count; } stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); } /** * Apply the Scale2x4 effect on a bitmap. * The destination bitmap is filled with the scaled version of the source bitmap. * The source bitmap isn't modified. * The destination bitmap must be manually allocated before calling the function, * note that the resulting size is exactly 2x4 times the size of the source bitmap. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ static void scale2x4(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { unsigned char* dst = (unsigned char*)void_dst; const unsigned char* src = (const unsigned char*)void_src; unsigned count; assert(height >= 2); count = height; stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); dst = SCDST(4); count -= 2; while (count) { stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); dst = SCDST(4); src = SCSRC(1); --count; } stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); } /** * Apply the Scale3x effect on a bitmap. * The destination bitmap is filled with the scaled version of the source bitmap. * The source bitmap isn't modified. * The destination bitmap must be manually allocated before calling the function, * note that the resulting size is exactly 3x3 times the size of the source bitmap. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ static void scale3x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { unsigned char* dst = (unsigned char*)void_dst; const unsigned char* src = (const unsigned char*)void_src; unsigned count; assert(height >= 2); count = height; stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); dst = SCDST(3); count -= 2; while (count) { stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); dst = SCDST(3); src = SCSRC(1); --count; } stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); } /** * Apply the Scale4x effect on a bitmap. * The destination bitmap is filled with the scaled version of the source bitmap. * The source bitmap isn't modified. * The destination bitmap must be manually allocated before calling the function, * note that the resulting size is exactly 4x4 times the size of the source bitmap. * \note This function requires also a small buffer bitmap used internally to store * intermediate results. This bitmap must have at least an horizontal size in bytes of 2*width*pixel, * and a vertical size of 6 rows. The memory of this buffer must not be allocated * in video memory because it's also read and not only written. Generally * a heap (malloc) or a stack (alloca) buffer is the best choice. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_mid Pointer at the first pixel of the buffer bitmap. * \param mid_slice Size in bytes of a buffer bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ static void scale4x_buf(void* void_dst, unsigned dst_slice, void* void_mid, unsigned mid_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { unsigned char* dst = (unsigned char*)void_dst; const unsigned char* src = (const unsigned char*)void_src; unsigned count; unsigned char* mid[6]; assert(height >= 4); count = height; /* set the 6 buffer pointers */ mid[0] = (unsigned char*)void_mid; mid[1] = mid[0] + mid_slice; mid[2] = mid[1] + mid_slice; mid[3] = mid[2] + mid_slice; mid[4] = mid[3] + mid_slice; mid[5] = mid[4] + mid_slice; stage_scale2x(SCMID(-2 + 6), SCMID(-1 + 6), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); stage_scale2x(SCMID(0), SCMID(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); stage_scale2x(SCMID(2), SCMID(3), SCSRC(1), SCSRC(2), SCSRC(3), pixel, width); stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(-2 + 6), SCMID(-2 + 6), SCMID(-1 + 6), SCMID(0), pixel, width); dst = SCDST(4); stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(-1 + 6), SCMID(0), SCMID(1), SCMID(2), pixel, width); dst = SCDST(4); count -= 4; while (count) { unsigned char* tmp; stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(4), pixel, width); stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width); dst = SCDST(4); src = SCSRC(1); tmp = SCMID(0); /* shift by 2 position */ SCMID(0) = SCMID(2); SCMID(2) = SCMID(4); SCMID(4) = tmp; tmp = SCMID(1); SCMID(1) = SCMID(3); SCMID(3) = SCMID(5); SCMID(5) = tmp; --count; } stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(3), pixel, width); stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width); dst = SCDST(4); stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(3), SCMID(4), SCMID(5), SCMID(5), pixel, width); } /** * Apply the Scale4x effect on a bitmap. * The destination bitmap is filled with the scaled version of the source bitmap. * The source bitmap isn't modified. * The destination bitmap must be manually allocated before calling the function, * note that the resulting size is exactly 4x4 times the size of the source bitmap. * \note This function operates like ::scale4x_buf() but the intermediate buffer is * automatically allocated in the stack. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ static void scale4x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { unsigned mid_slice; void* mid_ptr; void* mid_alloc; /* required space for 1 row buffer */ mid_slice = scale2x_align_size(2 * pixel * width); mid_alloc = malloc(6 * mid_slice + SCALE2X_ALIGN_ALLOC); /* allocate space for 6 row buffers */ if (!mid_alloc) return; mid_ptr = scale2x_align_ptr(mid_alloc); scale4x_buf(void_dst, dst_slice, mid_ptr, mid_slice, void_src, src_slice, pixel, width, height); free(mid_alloc); } /** * Check if the scale implementation is applicable at the given arguments. * \param scale Scale factor. 2, 203 (fox 2x3), 204 (for 2x4), 3 or 4. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. * \return * - -1 on precondition violated. * - 0 on success. */ int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height) { if (pixel != 1 && pixel != 2 && pixel != 4) return -1; switch (scale) { case 202 : case 203 : case 204 : case 2 : case 303 : case 3 : if (height < 2) return -1; break; case 404 : case 4 : if (height < 4) return -1; break; default : return -1; } if (width < 2) return -1; return 0; } /** * Apply the Scale effect on a bitmap. * This function is simply a common interface for ::scale2x(), ::scale3x() and ::scale4x(). * \param scale Scale factor. 2, 203 (fox 2x3), 204 (for 2x4), 3 or 4. * \param void_dst Pointer at the first pixel of the destination bitmap. * \param dst_slice Size in bytes of a destination bitmap row. * \param void_src Pointer at the first pixel of the source bitmap. * \param src_slice Size in bytes of a source bitmap row. * \param pixel Bytes per pixel of the source and destination bitmap. * \param width Horizontal size in pixels of the source bitmap. * \param height Vertical size in pixels of the source bitmap. */ void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) { switch (scale) { case 202 : case 2 : scale2x(void_dst, dst_slice, void_src, src_slice, pixel, width, height); break; case 203 : scale2x3(void_dst, dst_slice, void_src, src_slice, pixel, width, height); break; case 204 : scale2x4(void_dst, dst_slice, void_src, src_slice, pixel, width, height); break; case 303 : case 3 : scale3x(void_dst, dst_slice, void_src, src_slice, pixel, width, height); break; case 404 : case 4 : scale4x(void_dst, dst_slice, void_src, src_slice, pixel, width, height); break; } } openjazz-20240919/ext/scale2x/scalebit.h000066400000000000000000000021071467303304200177300ustar00rootroot00000000000000/* * This file is part of the Scale2x project. * * Copyright (C) 2003 Andrea Mazzoleni * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * This file contains an example implementation of the Scale effect * applyed to a generic bitmap. * * You can find an high level description of the effect at : * * http://www.scale2x.it/ */ #ifndef __SCALEBIT_H #define __SCALEBIT_H int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height); void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height); #endif openjazz-20240919/licenses.txt000066400000000000000000000123161467303304200162010ustar00rootroot00000000000000================================================================================ OpenJazz ================================================================================ The OpenJazz source code is available from http://alister.eu/jazz/oj/ and the code repository lives at https://github.com/AlisterT/openjazz Copyright (c) 2005-2019 Alister Thomson Menu plasma effect Copyright (c) 2010 Alireza Nejati This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html ================================================================================ SDL ================================================================================ The Simple DirectMedia Layer (SDL for short) is a cross-platfrom library designed to make it easy to write multi-media software, such as games and emulators. The Simple DirectMedia Layer library source code is available from: https://www.libsdl.org/ Version 1.2 of this library is distributed under the terms of the GNU LGPL license: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html Version 2 uses the zlib license: https://www.libsdl.org/license.php ================================================================================ libpsmplug ================================================================================ Olivier Lapicque, author of Modplug, which is arguably the best quality MOD-playing software available, has placed his sound rendering code in the public domain. This library is based on that code. ================================================================================ Scale2x ================================================================================ Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html ================================================================================ miniz ================================================================================ Copyright 2013-2014 RAD Game Tools and Valve Software Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================================================ argparse ================================================================================ Copyright (c) 2012-2013 Yecheng Fu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. openjazz-20240919/res/000077500000000000000000000000001467303304200144215ustar00rootroot00000000000000openjazz-20240919/res/3ds/000077500000000000000000000000001467303304200151125ustar00rootroot00000000000000openjazz-20240919/res/3ds/OpenJazz.png000066400000000000000000000023561467303304200173660ustar00rootroot00000000000000PNG  IHDR00` PLTE       %%''**+334666;=??CGIIMMNPRRRUUY[^__aadlkllmmn#rrrssuuvwwy||~{tjeg``^UXVJA?974/-ݤ(&$#! DF         eeiioopp VVYY[[\\aaccdd 113366==AAEEGG ##''**Se!IDATxbh ^èމ'uc(IZ$ = 6oCPf/v>A͒ڶm۶m6kmm۶=5zQr}=5ÿJUxQ"9^o\QVT>FףL+!(\kPuLZ+[[~2<X>ˈHT:=+t[D[YUKmӸF׍H}_$vw:.Re,DQSDnd&ZniT) 3ToAZ g(GYx L$o:s:7iiX:#*/IV..si'k`& _8o[pr( ["vщw?(*}Ռ8Ɵvlw.Uvd ܸu|%#F_soc)ڗ`b"O elclez%7У2%_qZi~x}e~&⢒jAYxnR<=>61zUYn?NYIENDB`openjazz-20240919/res/emscripten/000077500000000000000000000000001467303304200165725ustar00rootroot00000000000000openjazz-20240919/res/emscripten/shell.html000066400000000000000000000130421467303304200205670ustar00rootroot00000000000000 OpenJazz
Downloading...


This is OpenJazz powered by emscripten.
{{{ SCRIPT }}} openjazz-20240919/res/psp/000077500000000000000000000000001467303304200152235ustar00rootroot00000000000000openjazz-20240919/res/psp/icon.png000066400000000000000000000044331467303304200166650ustar00rootroot00000000000000PNG  IHDRP IDATx{pT7AA "A <"B v:?:C mcmUfZ*Va / $w& lC{~wN{;W\B '݌4ǀ^g>6InTg6D!$r LpR>8g1Bv%@dd*_=@Y[^7된 ZQ@,.eI6J4p h"ZE(88^>(Z4\'S Ow!7y|?Rw 9C$ ^ $KߧK\Fw-:FmJV]H+*H heiz/8W~#GKA .@nDW&Յ5E N7814N0 jկB|}g~,KKֻ +[A#H*xt?8n@{]jT;򧫿Q>᠀29*s5K(&.^3NU{uQ~ ' QLcWF־d.0ԛ!sqCZ#HJ :30Fe4xռ9 PJex/[{XUb Юf[nWXWn yA+|ڼVl+3oQ/e U|W,4h_SVį(Y3>ENP.iacYg=ߤʴ47Y!*LkVp>FZTڶ\NZEfJ2}T@Z-N0%A ikVK9h^~³jc*gN蟱8r)hpx1~#PWw G@ɗTO@Ӳ {Ƞ37f(gpʹd-& $\ eFSS( gny^gx1d lLVfarJQJ|V 1X2P a]R[u\OTx$;aCU鰘:OIԵ<,ѢK1NEKv},bP@UNp.#cl6ҸIENDB`openjazz-20240919/res/riscos/000077500000000000000000000000001467303304200157235ustar00rootroot00000000000000openjazz-20240919/res/riscos/!Boot,feb000066400000000000000000000001001467303304200173320ustar00rootroot00000000000000Set OpenJazz$Dir IconSprites .!Sprites openjazz-20240919/res/riscos/!Run,feb000066400000000000000000000006151467303304200172060ustar00rootroot00000000000000Set OpenJazz$Dir CDir .OpenJazz If "%*0"<>"" Then Set OpenJazz$DataDir %*0 If ""="" Then Set OpenJazz$DataDir .data IfThere .PANEL/000 Then Else Error 0 Unable to find game data files. Put the data into the "data" directory inside the !OpenJazz application. Run .OpenJazz / >null: 2>null: openjazz-20240919/res/riscos/!Sprites,ff9000066400000000000000000000072601467303304200200260ustar00rootroot00000000000000!openjazz,h <<AAWWooWW11 !!'',,..<<??LLXX[[gguuww||{{kk__bbSSWWPPDDGG::;; @@  &1 <__game directory 2__> == Description OpenJazz is a free, open-source version of the classic Jazz Jackrabbit PC platform games. + To play, you will need original data files. == Options *-h*, *--help*:: Display command line flags and general help. *-v*, *--version*:: Display engine version. *-f*, *--fullscreen*:: Start in fullscreen mode. *--window*:: Start in window mode. *-m*:: Start with muted audio. *-s*, *--scale[=]* <__Factor__>:: Scale window by factor. Can be between _1_ and _4_. *-w*, *--world[=]* <__World__> *-l*, *--level[=]* <__Level__>:: Directly load specific world/level. *-q*, *--[no-]quiet*:: Enable/Disable console logging. *--verbose[=]* <__Debug Level__>:: Set logging verbosity. Can be one of _max_, _trace_, _debug_, _info_, _warn_, _error_, _fatal_. == Files _openjazz.cfg_:: The configuration file. NOTE: Command line parameters will take precedence over values in the configuration file _openjazz.log_:: The generated logfile. *Game Data*:: OpenJazz should be compatible with all released versions of Jazz Jackrabbit 1. + This includes the Shareware Episodes, "Holiday Hare" and "Holiday Hare '95", the "Jazz Jackrabbit CD" with "The Lost Episodes" and the "Complete" edition. == Ingame Controls *Fixed Controls*:: + |=== |Key(s) |Purpose |kbd:[Enter] |Choose a menu option |kbd:[Escape] |Go back to the previous menu |kbd:[F9] |View in-game statistics (e.g. FPS) |kbd:[P] |Pause the game |kbd:[Alt+Enter] |Switch between full-screen and windowed mode |kbd:[1-5] |Switch weapon to Blaster, Toaster, Missile, Bouncer or TNT respectively. If the desired weapon is not available, try next. |=== *Configurable Controls*:: These are configurable via the "setup options" menu. Default settings: + |=== |Key(s) |Purpose |kbd:[Left arrow] and kbd:[Right arrow] |Move left and right |kbd:[Space bar] |Jump/Swim upwards |kbd:[left Alt] |Shoot |kbd:[right Ctrl] |Change weapon |=== IMPORTANT: Under Windows the Jump and Shoot keys are moved to the right to avoid opening the window context menu. That means kbd:[Space bar] => Shoot and kbd:[right Alt] => Jump/Swim upwards. Other platforms may have other defaults. *Game Controllers*:: Most game controllers and joysticks that SDL recognizes can be used. + However, not all axes or buttons may be available. Needs manual settings. == Reporting Bugs Bugs can be reported at the link:https://github.com/AlisterT/OpenJazz/issues[issue tracker]. == See Also *J1E* - level editor by Newspaz, *JJ1MOD* - graphic editor by Doubble Dutch link:http://alister.eu/jazz/oj/[The OpenJazz Homepage] == Authors Alister Thomson (AlisterT):: Engine, project lead Alireza Nejati:: Menu plasma effect newspaz:: Bug fixes, enemy improvements Carsten Teibes (carstene1ns):: Maintenance, bug fixes, documentation + Integrating the ports (Wii, 3DS, PSP, PSVita, SDL2, ...) + Additional coding (logger, CLI, ...) + Modernizing + UI improvements Lars Persson (anotherguest):: Movie playback fixes CYBERDEViL:: Savegame algorithm Scott Smith (Pickle):: GP2X/WIZ, Canoo, Pandora ports Przemysław Buczkowski (przemub):: Android, Haiku ports Matthieu Milan (usineur):: PSVita port deniska:: PSP port tehpola:: Wii port pocketinsanity:: PocketPC port Ricerind:: Mac OS X port Slaanesh:: GP32 port GPF:: Dreamcast port Cameron Cawley (ccawley2011):: RISC OS, SDL2 port midzer:: Web port openjazz-20240919/res/unix/OpenJazz.desktop000066400000000000000000000004051467303304200205360ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=OpenJazz Comment=Jazz Jackrabbit™ game engine reimplementation Icon=OpenJazz Exec=OpenJazz Categories=Game;ActionGame; Keywords=jazz;jackrabbit;green;rabbit;bunny;jump;run;gun;platform; Terminal=false StartupNotify=false openjazz-20240919/res/unix/OpenJazz.png000066400000000000000000000017561467303304200176630ustar00rootroot00000000000000PNG  IHDR00WIDATxZ}g۶m۶m۶m۶m۶sR8Tw8ժꪫ*ZZt`\̂ELmØЁ,i,D[ ;&oZlI0a46piV|JO`)xbAL"<޸oFxV+X97@_ 6DN_v!Z7X- KGX!,1GiLdO^ؿ1j'5XV=\8=/jf|zVc\֦xefQ`ާ!S/Ǔ|]|k!ᔨ\ul-pWe%F-e]NW*726*Kb__#~Q~Azn`a@gG~WNUo|`saKuMFүQe',…P~7~w|HA=1q?gKEL 5beƯ#)|z6w;\ޘ>S(pִn=>Xby} l=_ KpzJ_)Rmn;@[{ms&: /iS^pVclhlU@\x5@m-O3v*!R D8 Rc/{ hlrוda.-?_!){6NJo0V8,U]uUW] ozíIENDB`openjazz-20240919/res/unix/OpenJazz.svg000066400000000000000000000015221467303304200176650ustar00rootroot00000000000000 openjazz-20240919/res/wii/000077500000000000000000000000001467303304200152115ustar00rootroot00000000000000openjazz-20240919/res/wii/READMII.txt000066400000000000000000000011371467303304200170260ustar00rootroot00000000000000OpenJazz for Wii Licensed under GPLv2. See included licenses.txt for details. OpenJazz written by Alister Thomson http://alister.eu/jazz/oj/ Originally ported to the Wii by tehpola. Thanks to the developers of SDL-Wii for making this a breeze to port. Installation: Copy the contents of the archive into the /apps directory of your SD card. Copy all of the JJ1 data files into application directory: /apps/OpenJazz Default Controls (hold the Wiimote sideways): 2: shoot 1: jump A: switch weapons Home: Esc +: Pause Network games don't work yet, but the support is there in the code. openjazz-20240919/res/wii/icon.png000077500000000000000000000040221467303304200166500ustar00rootroot00000000000000PNG  IHDR0rsRGBbKGD pHYs  tIME 62) tEXtCommentCreated with GIMPWmIDATxlU?m -haMeZ8dad#!?,!#FU?&cؒ)7%l$)J\RB ܘĮsڞmm/=997yyy8ndPT8X|6 \$ . zRp}v%^P#04ER H IT#T+xʠf NlgGq =1 N l,qj6 z l%EgGA/3dqjr*2XzHP'H%(,oYp9:-b=:  bp^>DAAM U`;x(t]> }w.UHO AרּH*q ИO 3J0;_Yr#"Q?0P=f H Cyk>|k:f_yL9.̊PDE-iP -}}p};T_X{ҝd 4%љܷl}.G? { lG ؊|u/[PCIA9yzk[ʀ۲*@:$I[k';Q{< J?2^i[w$m48ً&읏Hn'h94ޯK?Ͱi,%@¤8Nm@<3ƻ<Gvzsg ț``=15 +!7! [eP0cs Μr/[8o<5 ؈2hY8/[r4pj Mߑ`M}*!6i](- f9f8q9b!_{y)8݉9qyB N\N0B|+!̕G(r!W<i8E/ͧ:֞>h6@x-K(o-Yaop pNЭX;aP:;?By< {m,߲Vr=Mɜ2+ NL^gF臏OBym@s/VgE{ґgz>췸@؃R ۧmIΥK3We[fTpv/ >\{Yͻ0R=!DUhH۶$;O{~N Aqyf#h'=*zo%|g ѣd'ƫWuhv/jD,6y&Њ#w}Gu5됬2hY]ܹ< ZXlt`wNd{4w>4{:-۱dnSYVbK~pFmU- -hQ"-f&9i\جEt癓Ö^'I 581 wTù|1vQKP+8"vl+/= :z7 ZxT jpQTd8 , 86/L AM\ q#@v!x$p ~.ўq`c4[֙r`QX! d1"̢6~eel_s@XPhun@"lqe4V;X +^up,=ܷe.pkFUpX |Z+Pd1 qA󜐚"4h- pp}^wx7Tu"qTAIENDB`openjazz-20240919/res/wii/meta.xml000077500000000000000000000011561467303304200166670ustar00rootroot00000000000000 OpenJazz 20240919000000 AlisterT, tehpola, carstene1ns Open source Jazz Jackrabbit engine OpenJazz by Alister Thomson (http://www.alister.eu/jazz/oj/). Default Controls (hold the Wiimote sideways): Arrows: move 2: shoot 1: jump A: switch weapons Home: Esc +: Pause Special thanks to the developers of SDL-Wii for making this a breeze to port. TODO: * Network games don't work yet. Enjoy! openjazz-20240919/res/windows/000077500000000000000000000000001467303304200161135ustar00rootroot00000000000000openjazz-20240919/res/windows/OpenJazz.ico000066400000000000000000002516751467303304200203670ustar00rootroot00000000000000  HH Te@@ (Bq<< H:00 %](( h  m. ? H hUOPNG  IHDR\rfIDATxyE=$I2I&' A8!\PX]@+VDEBF8p;0#72]~A ? =—5[Ia0xx%gZFyqׁ^&}Qr0]KأzlB ^&]ۧd Y#A @OױǎL`Jl <=ۙJO$@7ӟLvZױvP{Z2 xxxXs]JK@`OG̀6=-#vQ@rDka;01VHJ ;2X :=ּN9:~pp>K`&2#`OIyrtn)6ttY :5vqQأ۩3&0 0&Mȱ<:~{8v`w) T'/p vbbu cO hF# k' 㱗nF/"j#n[D}MY;[|*AG*) DW@1W)c}=Q)T k'|{?ulU 0Js Fƥ*8{JpVs DL(@uxzRČ^)]. h`]?e\ $!t0O{+Q$;' k=܌nIϱk+J>xY}0|AIOM9npY㐒<J>X9?F?Ni_*v>]o0G[$J>zåZn}Ǧʞ2vZF37Zb6 %+kbWoS;-_PuƬ*#s7V* ^-hf}מTaWiª {a]o8{ڧB ɾ/`_%իIA %> b)x`|@h6ñI58 sO|$%I 2:ZC]"u:de|%aIz( ,Jy 0 t^%m9 d'Ju*d;#ҖKIuIijazqVm qTAux팆.Q5LffPxlړZ 0$07À50x ]W4#Y9W7lׁTRp M yC0pxsb;k;sMSŌZLW`?-'\ 7>&^J@ѿ? z6cJiqĶ! "X4xM WaKY0K~qp:=\l iD,JIg[Ia*H _J$vԒr]e,5Wcr{ɦ2:@/޲RI~z$\s ?FbɑM.R3\b"t_ߗWDꜙ$;!roQ@%󮷪%GI, _q-Cbՠ$^ W8-LYH7)?I: b@w7V!u|qg?QXlx45Jsu{vu%W ^w݁$=,o?Fy>nn4å$1<_9wp/T`v@2oI!-=c^>zR1;"xgWI/zHE22~xr3:g[4O.)u:}V`2鷹0+?_GzD,B%1BGL ţ}T(“'fY9DG|=`hoS{>+aa R!KGӽIq` .gCFbgdCc1oW(cgwH(k Fc'<~}X|^=țK#t_BmPsS'lg&__: b@{<{,l4;%e/9Gk^oL$X6܋  X.@/Buy^ܥ. OOŀ}\e@BD0G^8+h:(| /Z6Q #ޘhMpGgʦ ԗ\Ge |x"%c5 :`]#ljb %ɗ~Yړ`c/@B>YˀP_`S( %+dB=z ;*QcG(B<@0R1Y6<B>| ԕnL*Z&:{)Xʋ!lX$o %Iu0ԟo_N:|js(եٛK%\7[: uہi:4c^ p tzm>rDT~8b)G;dL&/6B~:| `!?궻O#B}(&_H+~w #&\܀Fe/l WY^5 hki^::| 8|(3p5f|/\ef ڎ]wc-"\e6<ȾL0b@{'EA: u ^EOU(VuEی!zh 4X7uD!Cpkz``7oFK4L{bP뷞9vE3Io:?9*4X,wd1~`9w&f o)` ΰxuPhˈ.WG[B /X=@lB%\.zw9,Ko!xȨ.T`tE ~WUݨRFmYwL $O(E]kn[w ; )/=S `;n-$}7+FGez!0_>Sf{Ar s ou7Ʉ%f/G7w!h>6ƿU}#`=FՍ@u8t$'O|v% 3puu% ~N[{O+ ,NqKfCU94O՘ޛ# L{m38V2d`J'w4vrj zk:_m^[NMo:fq @/Ha u4g(fOԀR~>Hbq,Kt]ϝ)HƖO/,v` A*ש:H ,qE)3!ϊH%Կ~w~:F0SB>fKPu]oQg˳ǐ2 ]> 5V7"N;[V1L/&O` AS,I=eeh7xܑ}|47 I'`af]re@߻ުR~p>ĒN}ļC-mԙַ$ԁy$VLGKVwI|ͮTe!$~DpF-"Ӽg4-)N4m*wjss)]!bʻ`F2\o])fCbʜ_C;h~{?'K35*cK& GYj+<:.3Do\ߗ}I7]wMWnF"DR0e}1?<1M:80/Sn-y]eT@\oaʦ$=93I |rs;HM~7Fb$ q3Of{H[$ЪTY{.0g2RQ@fi!5?S 1=>EJWI\#OJb]hiKG{,spV-Wa`joYfD-l8Ճ0sǼvkCS=`6ēh؄3m8tzxuIW.~y B#ߝ:I p6-`êꨇDzpeT׿vuljyb$ 0$۠fh\Wàmgծ]z;\)ïi x,VkΣ8 ' mm%OW o]wT)mNRw\ ̸kSo0̢TSgreֶS!W+} oDi[ xj ֮+0 LN) t nqni<[/ן؅}՛ XlO;w5aA41@$/8ppJPU]\^?bda-W#x)+];ɴ"<4.t .Q%?Y0nu(QVl[la)5ʵaCB1Z ܀&i39:[/´,daohW0=r7r#۩sJ=.%d$|;$*~|[XסDrvpWS?Tfm:jp?ZGa/,VUFĭ{,ws0+{>m߭=eIY!< 4UZę'8Mvu(Qom}}ed&U8GE:/^Ghule=H){Еz \wѻ~[dFX;j0˸y:jPE/z++w3˹g9u(#ibd7 uh=(  R~8u(/UVO;ׅ+6 sp%zpkܣ_9KWS$~$,fTtgZl^+ltVțF$<>ɴ=}&;Ȟ/Y7_}X<|O{{ϗ"tֺs ,sJΒ0AuM"Y` {1+z++L1w5Ҫl6pKczfǔ#ҝ$0L^AqY`lcGhYbV; 2F4uxȑ |j׭Q%eq[T0otΟswXq*UVvV-v(oe=+Teos"-TYS꿽-6`R7ǛW}ϔN4gc[,b}_7п?gm0[oZl0s0{~5\Xxo` ei=0 ȑ&&׭qk|c'\Kj`f~CS^O'G+G1|1\GmטM=Z:0`uCPJ{3/kGVD~g`qyj }uTNuQ/Ӎ=CRR{'(wǜVI`f龊^LBvԏS984`]9WKN.¾7J೨EH1`?tL ;ƾ*j%hZ88`߇1v8Z єHCd0tukfc6Y gCRK^ qa;la;D%|Jd0 Npc;/ط-f{{RMaӌ]izp@+<-D9Q$쑧3K-`6ȱmr\ b6ؕaGyk ~DYbyA`v@8`Їʜ&G?x5wicդ5dV&G`G#I!Kb{v vaj -< !T^I &@ovi]{Oaأlg_] l$ZxmPHl O/]b쩶'N۰kTn{1vo!m6QWgbGuajv쎽 Y vGۅ};tit۱;`TcASmzlI&--MvilIT 5J{e"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""^u/*x8IENDB`(H Q    !Mz]0 FzN_{./:y8( `EX"HRaw:l g|E|d?{6* b :UxD 0'/(qc,'U9`W+(Rg'! u{4a/)k x g[34ZL m'Hwhi5D R^=RC ,^=2 ?L^=%k|h^=3hW|^=;RB^=;O@ ^=3_O|^=%{sh^=D SL^=8+,^=ev^=HL?^=+-#6N^=62)A^=4#_ U+w^=H[TP ^=0 FcneI#p^=w^=9^=*b^=?z^=E~^=:l^=!H^= `^=x7^=^z._>!Ly]/ K0  ???>????????(@ @ #;MVVK8 *DSVK4OF D^#|oIorb W83%q&TQQ\T]H:S =61#s\ w \zZdc'ViB36N;4s4_3U5gv$h\Ac9X `[w&$Qv2a+Zf+}6fE8y-fY(V?fd; ? Hfd> < HfY/M?fE,v-f+fIfB ?fd Q}fX 8x>f.9p !fg&ggf'=m2Ct"fw(62 \ff5#fR=f]GfQ>f3%fqa f#|ofOFe #:LVUK7 GZXXXXXXXY#0????(<x @8 !&" "%)ai/Gf% MY `*@N0i<Jgk !/0}6$DR ]vN &AJ?#/<%R|fE  L\iW<>J0,}?]J';x&h^0#0#XU<rUi*2|8B 1Q2|Wsvs2|n AU2|z+= 2|z): 2|n 9L2|Wcys2|8V CQ2|:L *2|N= 2|]tbx2|i!Z,2||4a k+2|&Vx|vR!.2|]u2|2|$2|!02|+2|2|@M2| MX2})ah/-q !%" $''''''''?p008|~>>>>>>>>~~~~?(0` $0AG@.*?F<$ &ld Q6v%%t^Q6 1Β |x eNE4 :^iV-UZ%"P9\\< T= G&s lf\_H0tVP0TdG -O Yi{ #V 18  45 !M Jy{ d\*O 0 Vs! Ht  \x.cH 'drgzT E4 wd } v ^Q % %kd  0AF@.;IHHHHI5 088888888xxxx?((P Qf2 C{zA /U RN u, ՛d!@a0"D (8/im^cZD 3x9X: oF#a0~{Q %*s*Y7m"lXWm" k5hm" h2hm"vOWm"6i8m"}l;m":P,6m"~&c):m"DF[O(m""m"@m"Em" u,m"/U o#Pf2 I? >~?( @  &55% (*7-5/ }-Ga$!aQ.7WP,/\D.|  sl]17eB F0:_ GC'3/GC)0/G02V Gv P G[{G7s5@^-G&#  GG$GG4/D&55$ 09896@(0 1lM9<{W%V @5j8 d SdYs'$_!W? y?"OjQF otvp{1 tvpx/ tvQNetv h0+ltv hMCu'!tvdtv5|tv%Wvx1lMGH@@(( @?ZU3  ;ZO! fB6s kyp &;3"4I tPK+ta9~#?I1; K/;a0t""@9c^p4+; _ fC?ZU3 @`_5 (  )&55U%G FKsD8hpmz i8rg"$3[!0[mzsYL Y|QxGB52)%, openjazz-20240919/res/windows/OpenJazz.rc000066400000000000000000000011221467303304200201750ustar00rootroot000000000000000 ICON "OpenJazz.ico" 1 VERSIONINFO FILEVERSION 0,0,0,0 FILETYPE 0x00000001L { BLOCK "StringFileInfo" { BLOCK "040904b0" { VALUE "CompanyName", "" VALUE "ProductName", "OpenJazz" VALUE "ProductVersion", "2024.09.19.0" VALUE "FileDescription", "Jazz Jackrabbit™ engine reimplementation" VALUE "LegalCopyright", "Alister Thomson et al." VALUE "OriginalFilename", "OpenJazz.exe" VALUE "InternalName", "OpenJazz" VALUE "Comments", "http://www.alister.eu/jazz/oj/" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x409, 0x4b0 } } openjazz-20240919/src/000077500000000000000000000000001467303304200144175ustar00rootroot00000000000000openjazz-20240919/src/OpenJazz.h000066400000000000000000000036601467303304200163350ustar00rootroot00000000000000 /** * * @file OpenJazz.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 31st January 2006: Created player.h from parts of OpenJazz.h * - 3rd February 2009: Created menu.h from parts of OpenJazz.h * - 3rd February 2009: Created file.h from parts of OpenJazz.h * - 3rd February 2009: Created font.h from parts of OpenJazz.h * - 4th February 2009: Created palette.h from parts of OpenJazz.h * - 2nd March 2009: Created network.h from parts of OpenJazz.h * - 2nd June 2009: Created sound.h from parts of OpenJazz.h * - 3rd June 2009: Created network.h from parts of OpenJazz.h * - 13th July 2009: Created controls.h from parts of OpenJazz.h * - 13th July 2009: Created graphics.h from parts of OpenJazz.h * - 30th April 2010: Created util.h from parts of OpenJazz.h * - 30th April 2010: Created loop.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _OPENJAZZ_H #define _OPENJAZZ_H #ifndef EXTERN #define EXTERN extern #endif #include "types.h" // Constants // Standard string length #define STRING_LENGTH 32 // Return values #define E_N_OTHER -(0x26) #define E_N_DISCONNECT -(0x25) #define E_N_CONNECT -(0x24) #define E_N_ADDRESS -(0x23) #define E_N_LISTEN -(0x22) #define E_N_BIND -(0x21) #define E_N_SOCKET -(0x20) #define E_DATA -(0x15) #define E_VERSION -(0x14) #define E_TIMEOUT -(0x13) #define E_DEMOTYPE -(0x12) #define E_FILE -(0x11) #define E_VIDEO -(0x10) #define E_LOAD0 -(0x0F) #define E_LOAD1 -(0x0E) #define E_LOAD2 -(0x0D) #define E_LOAD3 -(0x0C) #define E_RETURN -(0x02) #define E_QUIT -(0x01) #define E_NONE 0 #define MAX_PALETTE_COLORS 256 #endif openjazz-20240919/src/game/000077500000000000000000000000001467303304200153305ustar00rootroot00000000000000openjazz-20240919/src/game/clientgame.cpp000066400000000000000000000232151467303304200201470ustar00rootroot00000000000000 /** * * @file clientgame.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c and menu.c * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 18th July 2009: Created clientgame.cpp from parts of game.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "game.h" #include "gamemode.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/network.h" #include "player/player.h" #include "loop.h" #include "setup.h" #include "util.h" #include /** * Create game client * * @param address Address of the server to which to connect */ ClientGame::ClientGame (char* address) { unsigned char buffer[BUFFER_LENGTH]; unsigned int timeout; int count, ret; GameModeType modeType; sock = net->join(address); if (sock < 0) throw sock; // Tee hee hee hee hee. // Receive initialisation message count = 0; timeout = globalTicks + T_SCHECK + T_TIMEOUT; // Wait for whole message to arrive while (count < MTL_G_PROPS) { if (loop(NORMAL_LOOP) == E_QUIT) { net->close(sock); throw E_QUIT; } if (controls.release(C_ESCAPE)) { net->close(sock); throw E_RETURN; } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); fontmn2->showString("WAITING FOR REPLY", canvasW >> 2, (canvasH >> 1) - 16); ret = net->recv(sock, buffer + count, MTL_G_PROPS - count); if (ret > 0) count += ret; if (globalTicks > timeout) { net->close(sock); throw E_TIMEOUT; } } // Make sure message is valid if (buffer[1] != MT_G_PROPS) { net->close(sock); throw E_DATA; } else if (buffer[2] != 1) { net->close(sock); throw E_VERSION; } printf("Connected to server (version %d).\n", buffer[2]); // Copy game parameters modeType = GameModeType(buffer[3]); difficulty = buffer[4]; maxPlayers = buffer[5]; nPlayers = buffer[6]; clientID = buffer[7]; printf("Game mode %d, difficulty %d, %d of %d players.\n", modeType, difficulty, nPlayers, maxPlayers); if (nPlayers > maxPlayers) { net->close(sock); throw E_DATA; } mode = createMode(modeType); if (!mode) { net->close(sock); throw E_DATA; } // Create players nPlayers = 0; players = new Player[maxPlayers]; // Download the level from the server levelFile = createString(LEVEL_FILE); file = NULL; ret = setLevel(NULL); if (ret < 0) { net->close(sock); if (file) delete file; delete mode; throw ret; } // Add a new player to the game buffer[0] = MTL_G_PJOIN + strlen(setup.characterName); buffer[1] = MT_G_PJOIN; buffer[2] = clientID; buffer[3] = 0; // Player's number, assigned by the server buffer[4] = 0; // Player's team, assigned by the server memcpy(buffer + 5, setup.characterCols, 4); memcpy(buffer + 9, setup.characterName, strlen(setup.characterName) + 1); send(buffer); // Wait for acknowledgement localPlayer = NULL; while (!localPlayer) { if (loop(NORMAL_LOOP) == E_QUIT) { net->close(sock); if (file) delete file; delete mode; throw E_QUIT; } if (controls.release(C_ESCAPE)) { net->close(sock); if (file) delete file; delete mode; throw E_RETURN; } video.clearScreen(0); fontmn2->showString("JOINING GAME", canvasW >> 2, (canvasH >> 1) - 16); ret = step(0); if (ret < 0) { net->close(sock); if (file) delete file; delete mode; throw ret; } } } /** * Disconnect and destroy client */ ClientGame::~ClientGame () { net->close(sock); if (file) delete file; delete mode; } /** * Set the next level and receive level data from server * * @param fileName The file name of the next level * * @return Error code */ int ClientGame::setLevel (char* fileName) { (void)fileName; int ret; video.setPalette(menuPalette); // Wait for level data to start arriving while (!file && levelFile) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_RETURN; SDL_Delay(T_MENU_FRAME); video.clearScreen(0); fontmn2->showString("WAITING FOR SERVER", canvasW >> 2, (canvasH >> 1) - 16); ret = step(0); if (ret < 0) return ret; } // Wait for level data to finish arriving while (file && levelFile) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_RETURN; SDL_Delay(T_MENU_FRAME); video.clearScreen(0); fontmn2->showString("downloaded", canvasW >> 2, (canvasH >> 1) - 16); fontmn2->showNumber(file->tell(), (canvasW >> 2) + 56, canvasH >> 1); fontmn2->showString("bytes", (canvasW >> 2) + 64, canvasH >> 1); ret = step(0); if (ret < 0) return ret; } return E_NONE; } /** * Send data to server * * @param buffer Data to send. First byte indicates length. */ void ClientGame::send (unsigned char* buffer) { net->send(sock, buffer); } /** * Game iteration * * @param ticks Current time * * @return Error code */ int ClientGame::step (unsigned int ticks) { int length; // Receive data from server if (received == 0) { // Not currently receiving a message // See if there is a new message to receive length = net->recv(sock, recvBuffer, 1); if (length > 0) received++; } if (received > 0) { // Currently receiving a message // See if there is any more data length = net->recv(sock, recvBuffer + received, recvBuffer[0] - received); if (length > 0) received += length; // See if the whole message has arrived if (received >= recvBuffer[0]) { switch (recvBuffer[1] & MCMASK) { case MC_GAME: if (recvBuffer[1] == MT_G_LEVEL) { bool firstMessage; if (!file) { // Not already storing level data, so open the file try { file = new File(levelFile, PATH_TYPE_TEMP, true); } catch (int e) { return e; } firstMessage = true; } else firstMessage = false; if (file) { file->seek((recvBuffer[2] << 8) + recvBuffer[3], true); for (int i = 4; i < recvBuffer[0]; i++) file->storeChar(recvBuffer[i]); } // If a zero-length block has been sent, it is the last if (recvBuffer[0] == MTL_G_LEVEL) { if (firstMessage) { // If the last message was also the first, // then the run of levels has ended delete[] levelFile; levelFile = NULL; } delete file; file = NULL; } break; } if ((recvBuffer[1] == MT_G_PJOIN) && (recvBuffer[3] < maxPlayers)) { printf("Player %d joined the game.\n", recvBuffer[3]); // Add the new player, and any that have been missed int player_num; for (player_num = nPlayers; player_num <= recvBuffer[3]; player_num++) { players[player_num].init(this, reinterpret_cast(recvBuffer + 9), recvBuffer + 5, recvBuffer[4]); addLevelPlayer(players + player_num); printf("Player %d joined team %d.\n", player_num, recvBuffer[4]); } nPlayers = player_num; if (recvBuffer[2] == clientID) localPlayer = players + recvBuffer[3]; } if ((recvBuffer[1] == MT_G_PQUIT) && (recvBuffer[2] < nPlayers)) { printf("Player %d left the game.\n", recvBuffer[2]); // Remove the player players[recvBuffer[2]].deinit(); // If necessary, move more recent players for (int i = recvBuffer[2]; i < nPlayers; i++) memcpy(static_cast(players + i), players + i + 1, sizeof(Player)); // Clear duplicate pointers memset(static_cast(players + nPlayers), 0, sizeof(Player)); } if (recvBuffer[1] == MT_G_CHECK) { checkX = recvBuffer[2]; checkY = recvBuffer[3]; if (recvBuffer[0] > 4) { checkX += recvBuffer[4] << 8; checkY += recvBuffer[5] << 8; } } if (recvBuffer[1] == MT_G_SCORE) { for (int i = 0; i < nPlayers; i++) { if (players[i].getTeam() == recvBuffer[2]) players[i].teamScore++; } } if (recvBuffer[1] == MT_G_LTYPE) { levelType = (LevelType)recvBuffer[2]; } break; case MC_LEVEL: if (baseLevel) baseLevel->receive(recvBuffer); break; case MC_PLAYER: if (recvBuffer[2] < maxPlayers) players[recvBuffer[2]].receive(recvBuffer); break; } received = 0; } } if (ticks >= checkTime) { // Check for disconnection if (!(net->isConnected(sock))) { if (file) delete file; file = NULL; return E_N_DISCONNECT; } checkTime = ticks + T_CCHECK; } if (localPlayer && (ticks >= sendTime)) { // Update server unsigned char sendBuffer[BUFFER_LENGTH]; sendBuffer[0] = MTL_P_TEMP; sendBuffer[1] = MT_P_TEMP; sendBuffer[2] = 0; localPlayer->send(sendBuffer); send(sendBuffer); sendTime = ticks + T_CSEND; } return E_NONE; } /** * Ask server to award team a point * * @param team Team to receive point */ void ClientGame::score (unsigned char team) { unsigned char buffer[MTL_G_SCORE]; // Inform server buffer[0] = MTL_G_SCORE; buffer[1] = MT_G_SCORE; buffer[2] = team; send(buffer); } /** * Ask server to approve new checkpoint * * @param gridX X-coordinate (in tiles) of the checkpoint * @param gridY Y-coordinate (in tiles) of the checkpoint */ void ClientGame::setCheckpoint (int gridX, int gridY) { unsigned char buffer[MTL_G_CHECK]; buffer[0] = MTL_G_CHECK; buffer[1] = MT_G_CHECK; buffer[2] = gridX & 0xFF; buffer[3] = gridY & 0xFF; buffer[4] = (gridX >> 8) & 0xFF; buffer[5] = (gridY >> 8) & 0xFF; send(buffer); } openjazz-20240919/src/game/game.cpp000066400000000000000000000163321467303304200167520ustar00rootroot00000000000000 /** * * @file game.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c and menu.c * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 3rd June 2009: Created network.cpp from parts of game.cpp * - 18th July 2009: Created servergame.cpp from parts of game.cpp * - 18th July 2009: Created clientgame.cpp from parts of game.cpp * - 3rd October 2010: Created localgame.cpp from parts of game.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "game.h" #include "gamemode.h" #include "io/gfx/video.h" #include "io/sound.h" #include "jj1/bonuslevel/jj1bonuslevel.h" #include "jj1/level/jj1level.h" #include "jj1/planet/jj1planet.h" #ifdef ENABLE_JJ2 #include "jj2/level/jj2level.h" #endif #include "player/player.h" #include "util.h" #include "io/log.h" #include #include /** * Create base game */ Game::Game () { levelFile = NULL; levelType = LT_JJ1; players = NULL; baseLevel = NULL; planetId = -1; mode = NULL; // Default difficulty setting difficulty = 1; sendTime = checkTime = 0; checkX = checkY = 0; } /** * Destroy game */ Game::~Game () { if (levelFile) delete[] levelFile; if (players) delete[] players; localPlayer = NULL; } /** * Check the if a file name corresponds to a type * * @param fileName The name of the file * @param type The type identifier in lower case characters * @param typeLength The length of the type identifier string * * @return Whether or not the file name corresponds to the type */ bool Game::isFileType (const char *fileName, const char *type, int typeLength) { int i; for (i = 0; i < typeLength; i++) { if ((fileName[i] != type[i]) && (fileName[i] != type[i] - 32)) return false; } return true; } /** * Create a new game mode * * @param modeType The mode to create * * @return The new game mode (NULL on failure) */ GameMode* Game::createMode (GameModeType modeType) { switch (modeType) { case M_SINGLE: return new SingleGameMode(); case M_COOP: return new CoopGameMode(); case M_BATTLE: return new BattleGameMode(); case M_TEAMBATTLE: return new TeamBattleGameMode(); case M_RACE: return new RaceGameMode(); } return NULL; } /** * Get the game's mode * * @return The game's mode */ GameMode* Game::getMode () { return mode; } /** * Get the game's difficulty * * @return The game's difficulty */ int Game::getDifficulty () { return difficulty; } /** * Set the game's difficulty */ void Game::setDifficulty (int diff) { difficulty = diff; } /** * Play a level. * * @return Error code */ int Game::playLevel (char* fileName, bool intro, bool checkpoint) { bool multiplayer; int ret; multiplayer = (mode->getMode() != M_SINGLE); if (isFileType(fileName, "macro", 5)) { // Load and play the level try { baseLevel = level = new JJ1DemoLevel(this, fileName); } catch (int e) { return e; } ret = level->play(); delete level; baseLevel = level = NULL; } else if (levelType == LT_JJ1BONUS) { JJ1BonusLevel *bonus; try { baseLevel = bonus = new JJ1BonusLevel(this, fileName, multiplayer); } catch (int e) { return e; } ret = bonus->play(); delete bonus; baseLevel = NULL; #ifdef ENABLE_JJ2 } else if (levelType == LT_JJ2) { try { baseLevel = jj2Level = new JJ2Level(this, fileName, checkpoint, multiplayer); } catch (int e) { return e; } ret = jj2Level->play(); delete jj2Level; baseLevel = jj2Level = NULL; #endif } else { try { baseLevel = level = new JJ1Level(this, fileName, checkpoint, multiplayer); } catch (int e) { return e; } if (intro) { JJ1Planet *planet; char *planetFileName = NULL; planetFileName = createFileName("PLANET", level->getWorld()); try { planet = new JJ1Planet(planetFileName, planetId); } catch (int e) { planet = NULL; } delete[] planetFileName; if (planet) { if (planet->play() == E_QUIT) { delete planet; delete level; return E_QUIT; } planetId = planet->getId(); delete planet; } } ret = level->play(); delete level; baseLevel = level = NULL; } return ret; } /** * Determine the type of the specified level file. * * @return Level type */ LevelType Game::getLevelType (const char* fileName) { #ifdef ENABLE_JJ2 int length = strlen(fileName); if ((length > 4) && isFileType(fileName + length - 4, ".j2l", 4)) return LT_JJ2; #endif if (isFileType(fileName, "bonusmap", 8)) return LT_JJ1BONUS; return LT_JJ1; } /** * Play a level. * * @return Error code */ int Game::playLevel (char* fileName) { levelType = getLevelType(fileName); return playLevel(fileName, false, false); } /** * Play the game * * @return Error code */ int Game::play () { bool multiplayer = (mode->getMode() != M_SINGLE); bool checkpoint = false; // Play the level(s) while (true) { if (!levelFile) return E_NONE; sendTime = checkTime = 0; // Load and play the level int ret = playLevel(levelFile, !multiplayer, checkpoint); if (ret <= 0) return ret; if (levelFile && isFileType(levelFile, "bonusmap", 8)) { if (ret == WON) { char *fileName; // Go to next level fileName = createFileName("BONUSMAP", (levelFile[10] * 10) + levelFile[11] - 527); setLevel(fileName); delete[] fileName; } } else { if (ret == WON) { // Won the level // Do not use old level's checkpoint coordinates checkpoint = false; } else { // Lost the level if (!localPlayer->getLives()) return E_NONE; // Use checkpoint coordinates checkpoint = true; } } } return E_NONE; } /** * Move the viewport towards the exit sign * * @param change Distance to move */ void Game::view (int change) { if (TTOF(checkX) > viewX + (canvasW << 9) + change) viewX += change; else if (TTOF(checkX) < viewX + (canvasW << 9) - change) viewX -= change; if (TTOF(checkY) > viewY + (canvasH << 9) + change) viewY += change; else if (TTOF(checkY) < viewY + (canvasH << 9) - change) viewY -= change; } /** * Make a player restart the level from the beginning/last checkpoint * * @param player Player to reset */ void Game::resetPlayer (Player *player) { player->reset(checkX, checkY); } /** * Create a level player * * @param player Player for which to create the level player */ void Game::addLevelPlayer (Player *player) { int count; if (level) { Anim* pAnims[JJ1PANIMS]; for (count = 0; count < JJ1PANIMS; count++) pAnims[count] = level->getPlayerAnim(count); player->createLevelPlayer(levelType, pAnims, NULL, checkX, checkY); #ifdef ENABLE_JJ2 } else if (jj2Level) { Anim* pAnims[JJ2PANIMS]; Anim* pFlippedAnims[JJ2PANIMS]; for (count = 0; count < JJ2PANIMS; count++) { pAnims[count] = jj2Level->getPlayerAnim(0, count, false); pFlippedAnims[count] = jj2Level->getPlayerAnim(0, count, true); } player->createLevelPlayer(levelType, pAnims, pFlippedAnims, checkX, checkY); #endif } else { LOG_WARN("Cannot create level player!"); assert(false); } } openjazz-20240919/src/game/game.h000066400000000000000000000134131467303304200164140ustar00rootroot00000000000000 /** * * @file game.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 2nd March 2009: Created network.h from parts of OpenJazz.h * - 9th February 2009: Renamed network.h to game.h * - 2nd August 2009: Created gamemode.h from parts of game.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _GAME_H #define _GAME_H #include "gamemode.h" #include "io/network.h" #include "level/level.h" // Constants // Time intervals #define T_SSEND 20 #define T_SCHECK 1000 #define T_CSEND 10 #define T_CCHECK 1000 // Message categories and types #define MCMASK 0xF0 #define MC_GAME 0x00 #define MC_LEVEL 0x10 #define MC_PLAYER 0x20 #define MT_G_PROPS 0x00 /* Game properties */ #define MT_G_PJOIN 0x01 /* New player joined */ #define MT_G_PQUIT 0x02 /* Player left */ #define MT_G_LEVEL 0x03 /* Level data */ #define MT_G_CHECK 0x04 #define MT_G_SCORE 0x05 /* Team scored a roast/lap/etc. */ #define MT_G_LTYPE 0x06 /* Level type */ #define MT_L_PROP 0x10 /* Level property */ #define MT_L_GRID 0x11 /* Change to gridElement */ #define MT_L_STAGE 0x12 /* Change in level stage */ #define MT_P_ANIMS 0x20 /* Player animations */ #define MT_P_TEMP 0x21 /* Temporary player properties, e.g. position */ // Minimum message lengths, including header #define MTL_G_PROPS 8 #define MTL_G_PJOIN 10 #define MTL_G_PQUIT 3 #define MTL_G_LEVEL 4 /* + amount of level data */ #define MTL_G_CHECK 6 #define MTL_G_SCORE 3 #define MTL_G_LTYPE 3 #define MTL_L_PROP 5 #define MTL_L_GRID 8 #define MTL_L_STAGE 3 #define MTL_P_ANIMS 3 /* + PANIMS, BPANIMS, or 1 (for JJ2) */ #define MTL_P_TEMP 46 #define BUFFER_LENGTH 255 /* Should always be big enough to hold any message */ // Classes class Anim; class File; /// Base class for game handling classes class Game { private: int planetId; ///< ID of last planet approach sequence bool isFileType (const char *fileName, const char *type, int typeLength); protected: GameMode* mode; ///< Mode-specific management Level* baseLevel; ///< Current level char* levelFile; ///< Current level's file name LevelType levelType; ///< Current level's type int difficulty; ///< Difficulty setting (0 = easy, 1 = medium, 2 = hard, 3 = turbo (hard in JJ2 levels)) unsigned int sendTime; ///< The next time data will be sent unsigned int checkTime; ///< The next time a connection/disconnection will be dealt with short int checkX; ///< X-coordinate of the level checkpoint short int checkY; ///< Y-coordinate of the level checkpoint Game (); GameMode* createMode (GameModeType modeType); LevelType getLevelType (const char* fileName); int playLevel (char *fileName, bool intro, bool checkpoint); void addLevelPlayer (Player *player); public: virtual ~Game (); GameMode* getMode (); int getDifficulty (); void setDifficulty (int diff); int playLevel (char *fileName); virtual int setLevel (char *fileName) = 0; int play (); void view (int change); virtual void send (unsigned char *buffer) = 0; virtual int step (unsigned int ticks) = 0; virtual void score (unsigned char team) = 0; virtual void setCheckpoint (int gridX, int gridY) = 0; void resetPlayer (Player *player); }; /// Game handling for single-player local play class LocalGame : public Game { public: LocalGame (const char *firstLevel, int gameDifficulty); ~LocalGame (); int setLevel (char *fileName); void send (unsigned char *buffer); int step (unsigned int ticks); void score (unsigned char team); void setCheckpoint (int gridX, int gridY); }; /// Game handling for multiplayer servers class ServerGame : public Game { private: int clientStatus[MAX_CLIENTS]; /**< Array of client statuses -2: Connected and operational -1: Not connected >=0: Number of bytes of the level that have been sent */ int clientPlayer[MAX_CLIENTS]; ///< Array of client player indexes int clientSock[MAX_CLIENTS]; ///< Array of client sockets unsigned char recvBuffers[MAX_CLIENTS][BUFFER_LENGTH]; ///< Array of buffers containing data received from clients int received[MAX_CLIENTS]; ///< Array containing the amount of data received from each client unsigned char *levelData; ///< Contents of the current level file int levelSize; ///< Size of the current level file int sock; ///< Server socket public: ServerGame (GameModeType mode, char *firstLevel, int gameDifficulty); ~ServerGame (); int setLevel (char *fileName); void send (unsigned char *buffer); int step (unsigned int ticks); void score (unsigned char team); void setCheckpoint (int gridX, int gridY); }; /// Game handling for multiplayer clients class ClientGame : public Game { private: File *file; ///< File to which the incoming level will be written unsigned char recvBuffer[BUFFER_LENGTH]; ///< Buffer containing data received from server int received; ///< Amount of data received from server int clientID; ///< Client's index on the server int maxPlayers; ///< The maximum number of players in the game int sock; ///< Client socket public: explicit ClientGame(char *address); ~ClientGame(); int setLevel (char *fileName); void send (unsigned char *buffer); int step (unsigned int ticks); void score (unsigned char team); void setCheckpoint (int gridX, int gridY); }; #endif openjazz-20240919/src/game/gamemode.cpp000066400000000000000000000125011467303304200176110ustar00rootroot00000000000000 /** * * @file gamemode.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c and menu.c * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 18th July 2009: Created servergame.cpp from parts of game.cpp * - 2nd August 2009: Created gamemode.cpp from parts of servergame.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "game.h" #include "gamemode.h" #include "io/gfx/font.h" #include "player/player.h" /** * Outcome of player being hit * * @param source Player responsible for the hit * @param victim Player victim of the hit * * @return Whether or not the hit should result in energy loss (true) */ bool GameMode::hit (Player *source, Player *victim) { (void)source; (void)victim; return true; } /** * Outcome of player being killed * * @param game The current game * @param source Player responsible for the kill * @param victim Player victim of the kill * * @return Whether or not the player should be be killed (true) */ bool GameMode::kill (Game* game, Player *source, Player *victim) { if (source && (victim == localPlayer)) game->score(source->getTeam()); return true; } /** * Outcome of level being completed * * @param game The current game * @param player Player that has completed level * @param gridX X-coordinate (in tiles) of finishing position * @param gridY Y-coordinate (in tiles) of finishing position * * @return Whether or not the level should end (true) */ bool GameMode::endOfLevel (Game* game, Player *player, int gridX, int gridY) { (void)player; game->setCheckpoint(gridX, gridY); return true; } /** * Outcome of time running out */ void GameMode::outOfTime () { } /** * Get the game mode type * * @return Game mode type (M_SINGLE) */ GameModeType SingleGameMode::getMode () { return M_SINGLE; } /** * Choose a team for a new player * * @return New player's team (0) */ unsigned char SingleGameMode::chooseTeam () { return 0; } /** * Draw the player's team's score (not in single-player mode) * * @param font Font to use to draw score */ void SingleGameMode::drawScore (Font* font) { (void)font; } /** * Choose a team for a new player * * @return New player's team (0) */ unsigned char CooperativeGameMode::chooseTeam () { // All players are on the same team return 0; } /** * Draw the player's team's score (not in cooperative mode) * * @param font Font to use to draw score */ void CooperativeGameMode::drawScore (Font* font) { (void)font; // Do nothing } /** * Choose a team for a new player * * @return New player's team (unique) */ unsigned char FreeForAllGameMode::chooseTeam () { // Every player is on a separate team int count; unsigned char team; team = 1; // Find a team number higher than any other for (count = nPlayers - 1; count >= 0; count--) { if (players[count].getTeam() > team) team = players[count].getTeam() + 1; } return team; } /** * Draw the player's team's score * * @param font Font to use to draw score */ void FreeForAllGameMode::drawScore (Font* font) { font->showNumber(localPlayer->teamScore, 64, 4); } /** * Choose a team for a new player * * @return New player's team (0 or 1) */ unsigned char TeamGameMode::chooseTeam () { // Players are split between two teams int count, difference; // Calculate team imbalance difference = 0; for (count = 0; count < nPlayers; count++) { if (players[count].getTeam()) difference++; else difference--; } // Assign to the team with the least players if (difference >= 0) return 0; return 1; } /** * Draw the player's team's score * * @param font Font to use to draw score */ void TeamGameMode::drawScore (Font* font) { font->showNumber(localPlayer->teamScore, 64, 4); } /** * Get the game mode type * * @return Game mode type (M_COOP) */ GameModeType CoopGameMode::getMode () { return M_COOP; } /** * Get the game mode type * * @return Game mode type (M_BATTLE) */ GameModeType BattleGameMode::getMode () { return M_BATTLE; } /** * Get the game mode type * * @return Game mode type (M_TEAMBATTLE) */ GameModeType TeamBattleGameMode::getMode () { return M_TEAMBATTLE; } /** * Get the game mode type * * @return Game mode type (M_RACE) */ GameModeType RaceGameMode::getMode () { return M_RACE; } /** * Outcome of player being hit * * @param source Player responsible for the hit * @param victim Player victim of the hit * * @return Whether or not the hit should result in energy loss (false) */ bool RaceGameMode::hit (Player *source, Player *victim) { (void)source; (void)victim; return false; } /** * Outcome of level being completed * * @param game The current game * @param player Player that has completed level * @param gridX X-coordinate (in tiles) of finishing position * @param gridY Y-coordinate (in tiles) of finishing position * * @return Whether or not the level should end (false) */ bool RaceGameMode::endOfLevel (Game* game, Player *player, int gridX, int gridY) { (void)gridX; (void)gridY; if (player == localPlayer) game->score(localPlayer->getTeam()); game->resetPlayer(player); return false; } openjazz-20240919/src/game/gamemode.h000066400000000000000000000055251467303304200172660ustar00rootroot00000000000000 /** * * @file gamemode.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 2nd March 2009: Created network.h from parts of OpenJazz.h * - 9th February 2009: Renamed network.h to game.h * - 2nd August 2009: Created gamemode.h from parts of game.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _GAMEMODE_H #define _GAMEMODE_H // Constants #define MAX_PLAYERS (MAX_CLIENTS + 1) // Enum /// Game mode identifier enum GameModeType { M_SINGLE = 0, ///< Single-player mode M_COOP = 1, ///< Co-operative mode M_BATTLE = 2, ///< Battle mode M_TEAMBATTLE = 3, ///< Team battle mode M_RACE = 4 ///< Race mode }; // Classes class Font; class Game; class Player; /// Game mode base class class GameMode { public: virtual ~GameMode () {}; virtual GameModeType getMode () = 0; virtual unsigned char chooseTeam () = 0; virtual void drawScore (Font* font) = 0; virtual bool hit (Player *source, Player *victim); virtual bool kill (Game* game, Player *source, Player *victim); virtual bool endOfLevel (Game* game, Player *player, int gridX, int gridY); virtual void outOfTime (); }; /// Single-player game mode class SingleGameMode : public GameMode { public: GameModeType getMode (); unsigned char chooseTeam (); void drawScore (Font* font); }; /// Co-operative game mode base class class CooperativeGameMode : public GameMode { public: unsigned char chooseTeam (); virtual void drawScore (Font* font); }; /// Free-for-all game mode base class class FreeForAllGameMode : public GameMode { public: unsigned char chooseTeam (); virtual void drawScore (Font* font); }; /// Team-based game mode base class class TeamGameMode : public GameMode { public: unsigned char chooseTeam (); virtual void drawScore (Font* font); }; /// Co-operative game mode class CoopGameMode : public CooperativeGameMode { public: GameModeType getMode (); }; /// Battle game mode class BattleGameMode : public FreeForAllGameMode { private: //int targetKills; ///< Number of kills required for a player to win public: GameModeType getMode (); }; /// Team battle game mode class TeamBattleGameMode : public TeamGameMode { private: //int targetKills; ///< Number of kills required for a team to win public: GameModeType getMode (); }; /// Race game mode class RaceGameMode : public FreeForAllGameMode { private: //int targetLaps; ///< Number of laps required for a player to win public: GameModeType getMode (); bool hit (Player *source, Player *victim); bool endOfLevel (Game* game, Player *player, int gridX, int gridY); }; #endif openjazz-20240919/src/game/localgame.cpp000066400000000000000000000050201467303304200177550ustar00rootroot00000000000000 /** * * @file localgame.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c and menu.c * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 3rd June 2009: Created network.cpp from parts of game.cpp * - 18th July 2009: Created servergame.cpp from parts of game.cpp * - 18th July 2009: Created clientgame.cpp from parts of game.cpp * - 3rd October 2010: Created localgame.cpp from parts of game.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "game.h" #include "gamemode.h" #include "player/player.h" #include "setup.h" #include "util.h" /** * Create a single-player local game * * @param firstLevel File name of the first level to play * @param gameDifficulty Difficulty setting */ LocalGame::LocalGame (const char *firstLevel, int gameDifficulty) { levelFile = createString(firstLevel); levelType = getLevelType(firstLevel); difficulty = gameDifficulty; mode = new SingleGameMode(); // Create the player nPlayers = 1; localPlayer = players = new Player[1]; localPlayer->init(this, setup.characterName, NULL, 0); } /** * Destroy local game */ LocalGame::~LocalGame () { delete mode; } /** * Set the next level * * @param fileName The file name of the next level * * @return Error code */ int LocalGame::setLevel (char *fileName) { if (levelFile) delete[] levelFile; if (fileName) { levelFile = createString(fileName); levelType = getLevelType(fileName); } else levelFile = NULL; return E_NONE; } /** * No data is sent in local games * * @param buffer Data that will not be sent. First byte indicates length. */ void LocalGame::send (unsigned char *buffer) { (void)buffer; // Do nothing } /** * Game iteration - nothing to be done in local games * * @param ticks Current time * * @return Error code */ int LocalGame::step (unsigned int ticks) { (void)ticks; // Do nothing return E_NONE; } /** * No points are assigned to teams in local games * * @param team Team to receive point */ void LocalGame::score (unsigned char team) { (void)team; // Do nothing } /** * Set the checkpoint * * @param gridX X-coordinate (in tiles) of the checkpoint * @param gridY Y-coordinate (in tiles) of the checkpoint */ void LocalGame::setCheckpoint (int gridX, int gridY) { checkX = gridX; checkY = gridY; } openjazz-20240919/src/game/servergame.cpp000066400000000000000000000260101467303304200201730ustar00rootroot00000000000000 /** * * @file servergame.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c and menu.c * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 18th July 2009: Created servergame.cpp from parts of game.cpp * - 2nd August 2009: Created gamemode.cpp from parts of servergame.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "game.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/network.h" #include "player/player.h" #include "setup.h" #include "util.h" #include /** * Create game server * * @param modeType Game mode * @param firstLevel File name of the first level to play * @param gameDifficulty Difficulty setting */ ServerGame::ServerGame (GameModeType modeType, char* firstLevel, int gameDifficulty) { int count; // Create the server sock = net->host(); if (sock < 0) throw sock; // Tee hee. Throw sock. // Create the players nPlayers = 1; localPlayer = players = new Player[MAX_PLAYERS]; localPlayer->init(this, setup.characterName, setup.characterCols, 0); for (count = 0; count < MAX_CLIENTS; count++) clientPlayer[count] = clientStatus[count] = -1; // Copy the first level into memory levelFile = NULL; levelData = NULL; count = setLevel(firstLevel); if (count < 0) { net->close(sock); if (levelData) delete[] levelData; throw count; } difficulty = gameDifficulty; mode = createMode(modeType); } /** * Disconnect clients and destroy server */ ServerGame::~ServerGame () { int count; for (count = 0; count < MAX_CLIENTS; count++) { if (clientStatus[count] != -1) net->close(clientSock[count]); } net->close(sock); if (levelData) delete[] levelData; delete mode; } /** * Set the next level and load it into memory * * @param fileName The file name of the next level * * @return Error code */ int ServerGame::setLevel (char* fileName) { File* file; int count; if (levelFile) delete[] levelFile; if (levelData) delete[] levelData; // The new level will be sent to all clients for (count = 0; count < MAX_CLIENTS; count++) { if (clientStatus[count] != -1) clientStatus[count] = 0; } if (!fileName) { levelFile = NULL; levelData = NULL; return E_NONE; } try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { levelFile = NULL; levelData = NULL; return e; } levelFile = createString(fileName); // Load the entire file into memory levelSize = file->getSize(); levelData = file->loadBlock(levelSize); delete file; levelType = getLevelType(fileName); if (levelType != LT_JJ1) return E_NONE; // Modify the extension section to match the actual extension count = levelSize - 5; while (levelData[count - 1] != 3) count--; levelData[count] = fileName[strlen(fileName) - 3]; levelData[count + 1] = fileName[strlen(fileName) - 2]; levelData[count + 2] = fileName[strlen(fileName) - 1]; return E_NONE; } /** * Send data to clients * * @param buffer Data to send. First byte indicates length. */ void ServerGame::send (unsigned char* buffer) { int count; for (count = 0; count < MAX_CLIENTS; count++) { // Send data to client, unless the data concerns the client's player // Each client is solely responsible for its player's state if ((clientStatus[count] != -1) && (((buffer[1] & MCMASK) != MC_PLAYER) || (buffer[2] != clientPlayer[count]))) net->send(clientSock[count], buffer); } } /** * Game iteration * * @param ticks Current time * * @return Error code */ int ServerGame::step (unsigned int ticks) { unsigned char sendBuffer[BUFFER_LENGTH]; int count, pcount, length; for (count = 0; count < MAX_CLIENTS; count++) { if (clientStatus[count] >= 0) { if (clientStatus[count] == 0) { // Send level type sendBuffer[0] = MTL_G_LTYPE; sendBuffer[1] = MT_G_LTYPE; sendBuffer[2] = levelType; net->send(clientSock[count], sendBuffer); } // Client is connected, but not operational // Send a chunk of the level length = levelSize - clientStatus[count]; if (length > 251) length = 251; sendBuffer[0] = MTL_G_LEVEL + length; sendBuffer[1] = MT_G_LEVEL; sendBuffer[2] = clientStatus[count] >> 8; sendBuffer[3] = clientStatus[count] & 255; memcpy(sendBuffer + 4, levelData + clientStatus[count], length); length = net->send(clientSock[count], sendBuffer); // Client is operational if the whole level has been sent // Otherwise, keep sending data if (length == MTL_G_LEVEL) clientStatus[count] = -2; else if (length > 0) clientStatus[count] += length - MTL_G_LEVEL; } if ((clientStatus[count] == -2) && (received[count] == 0)) { // Client is operational, but not currently receiving a message // See if there is a new message to receive length = net->recv(clientSock[count], recvBuffers[count], 1); if (length > 0) received[count]++; } if ((clientStatus[count] == -2) && (received[count] > 0)) { // Currently receiving a message // See if there is any more data length = net->recv(clientSock[count], recvBuffers[count] + received[count], recvBuffers[count][0] - received[count]); if (length > 0) received[count] += length; // See if the whole message has arrived if (received[count] >= recvBuffers[count][0]) { switch (recvBuffers[count][1] & MCMASK) { case MC_GAME: if ((recvBuffers[count][1] == MT_G_PJOIN) && (clientPlayer[count] == -1)) { printf("Player %d (client %d) joined the game.\n", nPlayers, count); // Set up the new player recvBuffers[count][4] = mode->chooseTeam(); players[nPlayers].init(this, reinterpret_cast(recvBuffers[count] + 9), recvBuffers[count] + 5, recvBuffers[count][4]); addLevelPlayer(players + nPlayers); printf("Player %d joined team %d.\n", nPlayers, recvBuffers[count][4]); recvBuffers[count][3] = clientPlayer[count] = nPlayers; nPlayers++; } if (recvBuffers[count][1] == MT_G_CHECK) { checkX = recvBuffers[count][2]; checkY = recvBuffers[count][3]; if (recvBuffers[count][0] > 4) { checkX += recvBuffers[count][4] << 8; checkY += recvBuffers[count][5] << 8; } } if (recvBuffers[count][1] == MT_G_SCORE) { for (pcount = 0; pcount < nPlayers; pcount++) { if (players[pcount].getTeam() == recvBuffers[count][2]) players[pcount].teamScore++; } } break; case MC_LEVEL: baseLevel->receive(recvBuffers[count]); break; case MC_PLAYER: if (clientPlayer[count] != -1) { // Assign player byte based on sender recvBuffers[count][2] = clientPlayer[count]; players[clientPlayer[count]].receive(recvBuffers[count]); } break; } // Update clients send(recvBuffers[count]); received[count] = 0; } } if (ticks >= checkTime) { if ((clientStatus[count] == -1) && levelData) { // Client is not connected // Check for new connection clientSock[count] = net->accept(sock); if (clientSock[count] != -1) { printf("Client %d connected.\n", count); clientPlayer[count] = -1; received[count] = 0; // Incorporate the new client // Send data sendBuffer[0] = MTL_G_PROPS; sendBuffer[1] = MT_G_PROPS; sendBuffer[2] = 1; // Server version sendBuffer[3] = mode->getMode(); sendBuffer[4] = difficulty; sendBuffer[5] = MAX_PLAYERS; sendBuffer[6] = nPlayers; // Number of players sendBuffer[7] = count; // Client's clientID net->send(clientSock[count], sendBuffer); // Initiate sending of level data clientStatus[count] = 0; // Inform the new client of the checkpoint sendBuffer[0] = MTL_G_CHECK; sendBuffer[1] = MT_G_CHECK; sendBuffer[2] = checkX & 0xFF; sendBuffer[3] = checkY & 0xFF; sendBuffer[4] = (checkX >> 8) & 0xFF; sendBuffer[5] = (checkY >> 8) & 0xFF; net->send(clientSock[count], sendBuffer); // Inform the new client of the existing players sendBuffer[1] = MT_G_PJOIN; for (pcount = 0; pcount < nPlayers; pcount++) { sendBuffer[0] = MTL_G_PJOIN + strlen(players[pcount].getName()); sendBuffer[2] = count; sendBuffer[3] = pcount; sendBuffer[4] = players[pcount].getTeam(); memcpy(sendBuffer + 5, players[pcount].getCols(), PCOLOURS); memcpy(sendBuffer + 9, players[pcount].getName(), strlen(players[pcount].getName()) + 1); net->send(clientSock[count], sendBuffer); } } } else { // Client is connected // Check for disconnection if (!(net->isConnected(clientSock[count]))) { printf("Client %d disconnected (code: %d).\n", count, net->getError()); // Disconnect client net->close(clientSock[count]); clientStatus[count] = -1; if (clientPlayer[count] != -1) { // Remove the client's player printf("Player %d (client %d) left the game.\n", clientPlayer[count], count); nPlayers--; players[clientPlayer[count]].deinit(); // If necessary, move more recent players for (pcount = clientPlayer[count]; pcount < nPlayers; pcount++) memcpy(static_cast(players + pcount), players + pcount + 1, sizeof(Player)); // Clear duplicate pointers memset(static_cast(players + nPlayers), 0, sizeof(Player)); // Inform remaining clients that the player has left sendBuffer[0] = MTL_G_PQUIT; sendBuffer[1] = MT_G_PQUIT; sendBuffer[2] = clientPlayer[count]; send(sendBuffer); clientPlayer[count] = -1; } } } } } if (ticks >= checkTime) checkTime = ticks + T_SCHECK; if (ticks >= sendTime) { // Update clients sendBuffer[0] = MTL_P_TEMP; sendBuffer[1] = MT_P_TEMP; for (count = 0; count < nPlayers; count++) { sendBuffer[2] = count; players[count].send(sendBuffer); send(sendBuffer); } sendTime = ticks + T_SSEND; } return E_NONE; } /** * Assign point to team and inform clients * * @param team Team to receive point */ void ServerGame::score (unsigned char team) { unsigned char buffer[MTL_G_SCORE]; int count; // Inform clients buffer[0] = MTL_G_SCORE; buffer[1] = MT_G_SCORE; buffer[2] = team; send(buffer); // Update self for (count = 0; count < nPlayers; count++) { if (players[count].getTeam() == team) players[count].teamScore++; } } /** * Set the checkpoint and inform clients * * @param gridX X-coordinate (in tiles) of the checkpoint * @param gridY Y-coordinate (in tiles) of the checkpoint */ void ServerGame::setCheckpoint (int gridX, int gridY) { unsigned char buffer[MTL_G_CHECK]; buffer[0] = MTL_G_CHECK; buffer[1] = MT_G_CHECK; buffer[2] = gridX & 0xFF; buffer[3] = gridY & 0xFF; buffer[4] = (gridX >> 8) & 0xFF; buffer[5] = (gridY >> 8) & 0xFF; send(buffer); checkX = gridX; checkY = gridY; } openjazz-20240919/src/io/000077500000000000000000000000001467303304200150265ustar00rootroot00000000000000openjazz-20240919/src/io/controls.cpp000066400000000000000000000463111467303304200174020ustar00rootroot00000000000000 /** * * @file controls.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created main.c * - 22nd July 2008: Created util.c from parts of main.c * - 3rd February 2009: Renamed main.c to main.cpp * - 13th July 2009: Created controls.cpp from parts of main.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with input. * */ #include "controls.h" #include "gfx/video.h" #include "loop.h" #define DEFAULT_KEY_UP (SDLK_UP) #define DEFAULT_KEY_DOWN (SDLK_DOWN) #define DEFAULT_KEY_LEFT (SDLK_LEFT) #define DEFAULT_KEY_RIGHT (SDLK_RIGHT) #if defined (_WIN32) #define DEFAULT_KEY_JUMP (SDLK_RALT) #define DEFAULT_KEY_SWIM (SDLK_RALT) #define DEFAULT_KEY_FIRE (SDLK_SPACE) #define DEFAULT_KEY_CHANGE (SDLK_RCTRL) #define DEFAULT_KEY_ENTER (SDLK_RETURN) #define DEFAULT_KEY_ESCAPE (SDLK_ESCAPE) #define DEFAULT_KEY_STATS (SDLK_F9) #define DEFAULT_KEY_PAUSE (SDLK_p) #define DEFAULT_KEY_YES (SDLK_y) #define DEFAULT_KEY_NO (SDLK_n) #elif defined(DINGOO) #define DEFAULT_KEY_JUMP (SDLK_LCTRL) #define DEFAULT_KEY_SWIM (SDLK_LCTRL) #define DEFAULT_KEY_FIRE (SDLK_LALT) #define DEFAULT_KEY_CHANGE (SDLK_LSHIFT) #define DEFAULT_KEY_ENTER (SDLK_LCTRL) #define DEFAULT_KEY_ESCAPE (SDLK_ESCAPE) #define DEFAULT_KEY_STATS (SDLK_TAB) #define DEFAULT_KEY_PAUSE (SDLK_RETURN) #define DEFAULT_KEY_YES (SDLK_LCTRL) #define DEFAULT_KEY_NO (SDLK_LALT) #elif defined (ANDROID) #define DEFAULT_KEY_JUMP (SDLK_SPACE) #define DEFAULT_KEY_SWIM (SDLK_SPACE) #define DEFAULT_KEY_FIRE (SDLK_LALT) #define DEFAULT_KEY_CHANGE (SDLK_RETURN) #define DEFAULT_KEY_ENTER (SDLK_SPACE) #define DEFAULT_KEY_ESCAPE (SDLK_ESCAPE) #define DEFAULT_KEY_STATS (SDLK_F9) #define DEFAULT_KEY_PAUSE (SDLK_p) #define DEFAULT_KEY_YES (SDLK_y) #define DEFAULT_KEY_NO (SDLK_n) #elif defined(GAMESHELL) #define DEFAULT_KEY_JUMP (SDLK_j) /* A button */ #define DEFAULT_KEY_SWIM (SDLK_j) /* A button */ #define DEFAULT_KEY_FIRE (SDLK_k) /* B button */ #define DEFAULT_KEY_CHANGE (SDLK_o) /* Y button */ #define DEFAULT_KEY_ENTER (SDLK_RETURN) /* START button */ #define DEFAULT_KEY_ESCAPE (SDLK_ESCAPE) /* MENU button */ #define DEFAULT_KEY_STATS (SDLK_SPACE) /* SELECT button */ #define DEFAULT_KEY_PAUSE (SDLK_p) #define DEFAULT_KEY_YES (SDLK_y) #define DEFAULT_KEY_NO (SDLK_n) #else #define DEFAULT_KEY_JUMP (SDLK_SPACE) #define DEFAULT_KEY_SWIM (SDLK_SPACE) #define DEFAULT_KEY_FIRE (SDLK_LALT) #define DEFAULT_KEY_CHANGE (SDLK_RCTRL) #define DEFAULT_KEY_ENTER (SDLK_RETURN) #define DEFAULT_KEY_ESCAPE (SDLK_ESCAPE) #define DEFAULT_KEY_STATS (SDLK_F9) #define DEFAULT_KEY_PAUSE (SDLK_p) #define DEFAULT_KEY_YES (SDLK_y) #define DEFAULT_KEY_NO (SDLK_n) #endif /* These are optional */ #define DEFAULT_KEY_BLASTER (SDLK_1) #define DEFAULT_KEY_TOASTER (SDLK_2) #define DEFAULT_KEY_MISSILE (SDLK_3) #define DEFAULT_KEY_BOUNCER (SDLK_4) #define DEFAULT_KEY_TNT (SDLK_5) #if defined(GP2X) || defined(WIZ) #define DEFAULT_BUTTON_UP (0) #define DEFAULT_BUTTON_DOWN (4) #define DEFAULT_BUTTON_LEFT (2) #define DEFAULT_BUTTON_RIGHT (6) #define DEFAULT_BUTTON_JUMP (12) /* A */ #define DEFAULT_BUTTON_SWIM (12) /* A */ #define DEFAULT_BUTTON_FIRE (14) /* X */ #define DEFAULT_BUTTON_CHANGE (15) /* Y */ #define DEFAULT_BUTTON_ESCAPE (10) /* L */ #define DEFAULT_BUTTON_ENTER (11) /* R */ #define DEFAULT_BUTTON_PAUSE (8) /* Start */ #define DEFAULT_BUTTON_STATS (9) /* Select */ #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #elif defined(CAANOO) #define DEFAULT_BUTTON_UP (-1) #define DEFAULT_BUTTON_DOWN (-1) #define DEFAULT_BUTTON_LEFT (-1) #define DEFAULT_BUTTON_RIGHT (-1) #define DEFAULT_BUTTON_JUMP (0) /* A? */ #define DEFAULT_BUTTON_SWIM (0) /* A? */ #define DEFAULT_BUTTON_FIRE (1) /* X? */ #define DEFAULT_BUTTON_CHANGE (3) /* Y? */ #define DEFAULT_BUTTON_ESCAPE (6) /* Home */ #define DEFAULT_BUTTON_ENTER (5) /* R? */ #define DEFAULT_BUTTON_PAUSE (9) /* Help 2 */ #define DEFAULT_BUTTON_STATS (8) /* Help 1 */ #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #elif defined(PSP) #define DEFAULT_BUTTON_UP (8) #define DEFAULT_BUTTON_DOWN (6) #define DEFAULT_BUTTON_LEFT (7) #define DEFAULT_BUTTON_RIGHT (9) #define DEFAULT_BUTTON_JUMP (2) #define DEFAULT_BUTTON_SWIM (2) #define DEFAULT_BUTTON_FIRE (3) #define DEFAULT_BUTTON_CHANGE (0) #define DEFAULT_BUTTON_ENTER (5) #define DEFAULT_BUTTON_ESCAPE (4) #define DEFAULT_BUTTON_STATS (10) #define DEFAULT_BUTTON_PAUSE (11) #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #elif defined(__wii__) #define DEFAULT_BUTTON_UP (-1) #define DEFAULT_BUTTON_DOWN (-1) #define DEFAULT_BUTTON_LEFT (-1) #define DEFAULT_BUTTON_RIGHT (-1) #define DEFAULT_BUTTON_JUMP (2) #define DEFAULT_BUTTON_SWIM (2) #define DEFAULT_BUTTON_FIRE (3) #define DEFAULT_BUTTON_CHANGE (0) #define DEFAULT_BUTTON_ENTER (3) #define DEFAULT_BUTTON_ESCAPE (6) #define DEFAULT_BUTTON_STATS (4) #define DEFAULT_BUTTON_PAUSE (5) #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #elif defined (_3DS) #define DEFAULT_BUTTON_UP (-1) #define DEFAULT_BUTTON_DOWN (-1) #define DEFAULT_BUTTON_LEFT (-1) #define DEFAULT_BUTTON_RIGHT (-1) #define DEFAULT_BUTTON_JUMP (2) /* B */ #define DEFAULT_BUTTON_SWIM (2) /* B */ #define DEFAULT_BUTTON_FIRE (4) /* Y */ #define DEFAULT_BUTTON_CHANGE (3) /* X */ #define DEFAULT_BUTTON_ENTER (1) /* A */ #define DEFAULT_BUTTON_ESCAPE (7) /* Select */ #define DEFAULT_BUTTON_STATS (5) /* L */ #define DEFAULT_BUTTON_PAUSE (8) /* Start */ #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #elif defined(__vita__) #define DEFAULT_BUTTON_UP (8) #define DEFAULT_BUTTON_DOWN (6) #define DEFAULT_BUTTON_LEFT (7) #define DEFAULT_BUTTON_RIGHT (9) #define DEFAULT_BUTTON_JUMP (2) #define DEFAULT_BUTTON_SWIM (1) #define DEFAULT_BUTTON_FIRE (3) #define DEFAULT_BUTTON_CHANGE (0) #define DEFAULT_BUTTON_ENTER (5) #define DEFAULT_BUTTON_ESCAPE (4) #define DEFAULT_BUTTON_STATS (10) #define DEFAULT_BUTTON_PAUSE (11) #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #else #define DEFAULT_BUTTON_UP (-1) #define DEFAULT_BUTTON_DOWN (-1) #define DEFAULT_BUTTON_LEFT (-1) #define DEFAULT_BUTTON_RIGHT (-1) #define DEFAULT_BUTTON_JUMP (1) #define DEFAULT_BUTTON_SWIM (1) #define DEFAULT_BUTTON_FIRE (0) #define DEFAULT_BUTTON_CHANGE (3) #define DEFAULT_BUTTON_ENTER (0) #define DEFAULT_BUTTON_ESCAPE (5) #define DEFAULT_BUTTON_STATS (-1) #define DEFAULT_BUTTON_PAUSE (4) #define DEFAULT_BUTTON_YES (-1) #define DEFAULT_BUTTON_NO (-1) #endif /** * Set up the default controls. */ Controls::Controls () { int count; keys[C_UP].key = DEFAULT_KEY_UP; keys[C_DOWN].key = DEFAULT_KEY_DOWN; keys[C_LEFT].key = DEFAULT_KEY_LEFT; keys[C_RIGHT].key = DEFAULT_KEY_RIGHT; keys[C_JUMP].key = DEFAULT_KEY_JUMP; keys[C_SWIM].key = DEFAULT_KEY_SWIM; keys[C_FIRE].key = DEFAULT_KEY_FIRE; keys[C_CHANGE].key = DEFAULT_KEY_CHANGE; keys[C_ENTER].key = DEFAULT_KEY_ENTER; keys[C_ESCAPE].key = DEFAULT_KEY_ESCAPE; keys[C_BLASTER].key = DEFAULT_KEY_BLASTER; keys[C_TOASTER].key = DEFAULT_KEY_TOASTER; keys[C_MISSILE].key = DEFAULT_KEY_MISSILE; keys[C_BOUNCER].key = DEFAULT_KEY_BOUNCER; keys[C_TNT].key = DEFAULT_KEY_TNT; keys[C_STATS].key = DEFAULT_KEY_STATS; keys[C_PAUSE].key = DEFAULT_KEY_PAUSE; keys[C_YES].key = DEFAULT_KEY_YES; keys[C_NO].key = DEFAULT_KEY_NO; buttons[C_UP].button = DEFAULT_BUTTON_UP; buttons[C_DOWN].button = DEFAULT_BUTTON_DOWN; buttons[C_LEFT].button = DEFAULT_BUTTON_LEFT; buttons[C_RIGHT].button = DEFAULT_BUTTON_RIGHT; buttons[C_JUMP].button = DEFAULT_BUTTON_JUMP; buttons[C_SWIM].button = DEFAULT_BUTTON_SWIM; buttons[C_FIRE].button = DEFAULT_BUTTON_FIRE; buttons[C_CHANGE].button = DEFAULT_BUTTON_CHANGE; buttons[C_ENTER].button = DEFAULT_BUTTON_ENTER; buttons[C_ESCAPE].button = DEFAULT_BUTTON_ESCAPE; buttons[C_BLASTER].button = -1; buttons[C_TOASTER].button = -1; buttons[C_MISSILE].button = -1; buttons[C_BOUNCER].button = -1; buttons[C_TNT].button = -1; buttons[C_STATS].button = DEFAULT_BUTTON_STATS; buttons[C_PAUSE].button = DEFAULT_BUTTON_PAUSE; buttons[C_YES].button = DEFAULT_BUTTON_YES; buttons[C_NO].button = DEFAULT_BUTTON_NO; axes[C_UP].axis = 1; axes[C_UP].direction = false; axes[C_DOWN].axis = 1; axes[C_DOWN].direction = true; axes[C_LEFT].axis = 0; axes[C_LEFT].direction = false; axes[C_RIGHT].axis = 0; axes[C_RIGHT].direction = true; axes[C_JUMP].axis = -1; axes[C_SWIM].axis = -1; axes[C_FIRE].axis = -1; axes[C_CHANGE].axis = -1; axes[C_ENTER].axis = -1; axes[C_ESCAPE].axis = -1; axes[C_BLASTER].axis = -1; axes[C_TOASTER].axis = -1; axes[C_MISSILE].axis = -1; axes[C_BOUNCER].axis = -1; axes[C_TNT].axis = -1; axes[C_STATS].axis = -1; axes[C_PAUSE].axis = -1; axes[C_YES].axis = -1; axes[C_NO].axis = -1; hats[C_UP].hat = 0; hats[C_UP].direction = SDL_HAT_UP; hats[C_DOWN].hat = 0; hats[C_DOWN].direction = SDL_HAT_DOWN; hats[C_LEFT].hat = 0; hats[C_LEFT].direction = SDL_HAT_LEFT; hats[C_RIGHT].hat = 0; hats[C_RIGHT].direction = SDL_HAT_RIGHT; hats[C_JUMP].hat = -1; hats[C_SWIM].hat = -1; hats[C_FIRE].hat = -1; hats[C_CHANGE].hat = -1; hats[C_ENTER].hat = -1; hats[C_ESCAPE].hat = -1; hats[C_BLASTER].hat = -1; hats[C_TOASTER].hat = -1; hats[C_MISSILE].hat = -1; hats[C_BOUNCER].hat = -1; hats[C_TNT].hat = -1; hats[C_STATS].hat = -1; hats[C_PAUSE].hat = -1; hats[C_YES].hat = -1; hats[C_NO].hat = -1; for (count = 0; count < CONTROLS; count++) { keys[count].pressed = false; buttons[count].pressed = false; axes[count].pressed = false; hats[count].pressed = false; controlstates[count].time = 0; controlstates[count].state = false; } cursorX = cursorY = 0; cursorPressed = cursorReleased = false; wheelUp = wheelDown = 0; } /** * Set the key to use for the specified control. * * @param control The control * @param key The key to use */ void Controls::setKey (int control, int key) { keys[control].key = key; keys[control].pressed = false; } /** * Set the button to use for the specified control. * * @param control The control * @param button The button to use */ void Controls::setButton (int control, int button) { buttons[control].button = button; buttons[control].pressed = false; } /** * Set the axis and direction to use for the specified control. * * @param control The control * @param axis The axis to use * @param direction Whether or not to use positive axis values */ void Controls::setAxis (int control, int axis, bool direction) { axes[control].axis = axis; axes[control].direction = direction; axes[control].pressed = false; } /** * Set the hat and direction to use for the specified control. * * @param control The control * @param hat The hat to use * @param direction The direction to use */ void Controls::setHat (int control, int hat, int direction) { hats[control].hat = hat; hats[control].direction = direction; hats[control].pressed = false; } /** * Get the key being used for the specified control. * * @param control The control * * @return The key being used */ int Controls::getKey (int control) { return keys[control].key; } /** * Get the button being used for the specified control. * * @param control The control * * @return The button being used */ int Controls::getButton (int control) { return buttons[control].button; } /** * Get the axis being used for the specified control. * * @param control The control * * @return The axis being used */ int Controls::getAxis (int control) { return axes[control].axis; } /** * Get the direction of the axis being used for the specified control. * * @param control The control * * @return True if positive values of the axis are being used */ int Controls::getAxisDirection (int control) { return axes[control].direction; } /** * Get the hat being used for the specified control. * * @param control The control * * @return The hat being used */ int Controls::getHat (int control) { return hats[control].hat; } /** * Get the direction of the hat being used for the specified control. * * @param control The control * * @return hat direction */ int Controls::getHatDirection (int control) { return hats[control].direction; } /** * Set the position and state of the cursor. * * @param x The x-coordinate of the cursor * @param y The y-coordinate of the cursor * @param pressed The state of the cursor */ void Controls::setCursor(int x, int y, bool pressed) { cursorX = x; cursorY = y; cursorPressed = pressed; cursorReleased = !pressed; } /** * Update controls based on a system event. * * @param event The system event. Non-input events will be ignored * @param type Type of loop. Normal, typing, or input configuration * * @return Error code */ int Controls::update (SDL_Event *event, LoopType type) { int count; count = CONTROLS; switch (event->type) { case SDL_KEYDOWN: if (type == SET_KEY_LOOP) return event->key.keysym.sym; for (count = 0; count < CONTROLS; count++) if (event->key.keysym.sym == keys[count].key) keys[count].pressed = true; if (type == TYPING_LOOP) return event->key.keysym.sym; break; case SDL_KEYUP: for (count = 0; count < CONTROLS; count++) if (event->key.keysym.sym == keys[count].key) keys[count].pressed = false; break; case SDL_JOYBUTTONDOWN: if (type == SET_JOYSTICK_LOOP) return JOYSTICKB | event->jbutton.button; for (count = 0; count < CONTROLS; count++) if (event->jbutton.button == buttons[count].button) buttons[count].pressed = true; break; case SDL_JOYBUTTONUP: for (count = 0; count < CONTROLS; count++) if (event->jbutton.button == buttons[count].button) buttons[count].pressed = false; break; case SDL_JOYAXISMOTION: if (type == SET_JOYSTICK_LOOP) { if (event->jaxis.value < -16384) return JOYSTICKANEG | event->jaxis.axis; else if (event->jaxis.value > 16384) return JOYSTICKAPOS | event->jaxis.axis; } for (count = 0; count < CONTROLS; count++) if (event->jaxis.axis == axes[count].axis) { if (!axes[count].direction && (event->jaxis.value < -16384)) axes[count].pressed = true; else if (axes[count].direction && (event->jaxis.value > 16384)) axes[count].pressed = true; else axes[count].pressed = false; } break; case SDL_JOYHATMOTION: if (type == SET_JOYSTICK_LOOP) { switch(event->jhat.value) { case SDL_HAT_UP: return JOYSTICKHUP | event->jhat.hat; case SDL_HAT_LEFT: return JOYSTICKHLFT | event->jhat.hat; case SDL_HAT_RIGHT: return JOYSTICKHRHT | event->jhat.hat; case SDL_HAT_DOWN: return JOYSTICKHDWN | event->jhat.hat; } } for(count = 0; count < CONTROLS; count++) if (event->jhat.hat == hats[count].hat) { if (hats[count].direction & event->jhat.value) hats[count].pressed = true; else hats[count].pressed = false; } break; case SDL_MOUSEMOTION: if (event->motion.state & SDL_BUTTON(1)) { setCursor(event->motion.x, event->motion.y, true); } break; case SDL_MOUSEBUTTONDOWN: if (event->button.button == SDL_BUTTON_LEFT) { setCursor(event->button.x, event->button.y, true); } else if (event->button.button == 4) { wheelUp++; } else if (event->button.button == 5) { wheelDown++; } break; case SDL_MOUSEBUTTONUP: if (event->button.button == SDL_BUTTON_LEFT) { setCursor(event->button.x, event->button.y, false); } break; } return E_NONE; } /** * Process input iteration. * * Called once per game iteration. Updates input. */ void Controls::loop () { int count; // Apply controls to universal control tracking for (count = 0; count < CONTROLS; count++) controlstates[count].state = (controlstates[count].time < globalTicks) && (keys[count].pressed || buttons[count].pressed || axes[count].pressed || hats[count].pressed); if (wheelUp) { controlstates[C_UP].state = true; wheelUp--; } if (wheelDown) { controlstates[C_DOWN].state = true; wheelDown--; } } /** * Determine whether or not the specified control is being used. * * @param control The control * * @return True if the control is being used */ bool Controls::getState (int control) { return controlstates[control].state; } /** * If it's being used, release the specified control. * * @param control The control * * @return True if the control was being used */ bool Controls::release (int control) { if (!controlstates[control].state) return false; controlstates[control].time = globalTicks + T_KEY; controlstates[control].state = false; return true; } /** * Get the position of the cursor, and determine whether or not it's being used. * * @param x Is set to the x-coordinate of the cursor * @param y Is set to the y-coordinate of the cursor * * @return True if the cursor was being used */ bool Controls::getCursor (int& x, int& y) { #ifdef SCALE int scaleFactor = video.getScaleFactor(); x = cursorX / scaleFactor; y = cursorY / scaleFactor; #else x = cursorX; y = cursorY; #endif return cursorPressed || cursorReleased; } /** * Determine whether or not the cursor has been released. * * @return True if the cursor has been released */ bool Controls::wasCursorReleased () { if (cursorReleased) { cursorReleased = false; return true; } return false; } openjazz-20240919/src/io/controls.h000066400000000000000000000063101467303304200170420ustar00rootroot00000000000000 /** * * @file controls.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 13th July 2009: Created controls.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _INPUT_H #define _INPUT_H #include "loop.h" #include "OpenJazz.h" #include // Constants // Indexes for the keys / buttons / axes player controls arrays #define C_UP 0 #define C_DOWN 1 #define C_LEFT 2 #define C_RIGHT 3 #define C_JUMP 4 #define C_SWIM 5 #define C_FIRE 6 #define C_CHANGE 7 /* Change weapon */ #define C_ENTER 8 #define C_ESCAPE 9 #define C_BLASTER 10 #define C_TOASTER 11 #define C_MISSILE 12 #define C_BOUNCER 13 #define C_TNT 14 #define C_STATS 15 #define C_PAUSE 16 #define C_YES 17 #define C_NO 18 // Size of those arrays #define CONTROLS 19 // Time interval #define T_KEY 200 // Class /// Keeps track of all control input class Controls { private: typedef struct { int key; ///< Keyboard key bool pressed; ///< Whether or not the key is pressed } Keys; typedef struct { int button; ///< Joystick button bool pressed; ///< Whether or not the button is pressed } Buttons; typedef struct { int axis; ///< Joystick axis bool direction; ///< Axis direction bool pressed; ///< Whether or not the axis is pressed in the given direction } Axes; typedef struct { int hat; ///< Joystick hat int direction; ///< Hat direction bool pressed; ///< Whether or not the hat is pressed in the given direction } Hats; typedef struct { unsigned int time; ///< The time from which the control will respond to being pressed bool state; ///< Whether or not the control is being used } ControlState; Keys keys[CONTROLS]; Buttons buttons[CONTROLS]; Axes axes[CONTROLS]; Hats hats[CONTROLS]; ControlState controlstates[CONTROLS]; int cursorX; ///< X-coordinate of the cursor int cursorY; ///< Y-coordinate of the cursor bool cursorPressed; ///< Whether or not the cursor is being pressed bool cursorReleased; ///< Whether or not the cursor has been released int wheelUp; ///< How many times the wheel has been scrolled upwards int wheelDown; ///< How many times the wheel has been scrolled downwards void setCursor (int x, int y, bool pressed); public: Controls (); void setKey (int control, int key); void setButton (int control, int button); void setAxis (int control, int axis, bool direction); void setHat (int control, int hat, int direction); int getKey (int control); int getButton (int control); int getAxis (int control); int getAxisDirection (int control); int getHat (int control); int getHatDirection (int control); int update (SDL_Event *event, LoopType type); void loop (); bool getState (int control); bool release (int control); bool getCursor (int& x, int& y); bool wasCursorReleased (); }; // Variable EXTERN Controls controls; #endif openjazz-20240919/src/io/file.cpp000066400000000000000000000316321467303304200164560ustar00rootroot00000000000000 /** * * @file file.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created main.c * - 22nd July 2008: Created util.c from parts of main.c * - 3rd February 2009: Renamed util.c to util.cpp * - 3rd February 2009: Created file.cpp from parts of util.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with files. * */ #include "file.h" #include "io/gfx/video.h" #include "util.h" #include "io/log.h" #include #include #include #if !(defined(_WIN32) || defined(WII) || defined(PSP) || defined(_3DS)) #define UPPERCASE_FILENAMES #define LOWERCASE_FILENAMES #endif /** * Try opening a file from the available paths. * * @param name File name * @param pathType Kind of directory * @param write Whether or not the file can be written to */ File::File (const char* name, int pathType, bool write) { Path* path = gamePaths.paths; while (path) { // skip other paths if (pathType != PATH_TYPE_ANY && (path->pathType & pathType) != pathType) { path = path->next; continue; } // only allow certain write paths if (!write || (pathType & (PATH_TYPE_CONFIG|PATH_TYPE_TEMP)) > 0) { if (open(path->path, name, write)) return; } else LOG_FATAL("Not allowed to write to %s", name); path = path->next; } LOG_WARN("Could not open file: %s", name); throw E_FILE; } /** * Delete the file object. */ File::~File () { fclose(file); LOG_TRACE("Closed file: %s", filePath); delete[] filePath; } /** * Try opening a file from the given path * * @param path Directory path * @param name File name * @param write Whether or not the file can be written to */ bool File::open (const char* path, const char* name, bool write) { // Create the file path for the given directory filePath = createString(path, name); // Open the file from the path file = fopen(filePath, write ? "wb": "rb"); #ifdef UPPERCASE_FILENAMES if (!file) { uppercaseString(filePath + strlen(path)); file = fopen(filePath, write ? "wb": "rb"); } #endif #ifdef LOWERCASE_FILENAMES if (!file) { lowercaseString(filePath + strlen(path)); file = fopen(filePath, write ? "wb": "rb"); } #endif if (file) { LOG_DEBUG("Opened file: %s", filePath); return true; } delete[] filePath; return false; } /** * Get the size of the file. * * @return The size of the file */ int File::getSize () { int pos, size; pos = ftell(file); fseek(file, 0, SEEK_END); size = ftell(file); fseek(file, pos, SEEK_SET); return size; } /** * Get the current read/write location within the file. * * @return The current location */ int File::tell () { return ftell(file); } /** * Set the read/write location within the file. * * @param offset The new offset * @param reset Whether to offset from the current location or the start of the file */ void File::seek (int offset, bool reset) { fseek(file, offset, reset ? SEEK_SET: SEEK_CUR); } /** * Load an unsigned char from the file. * * @return The value read */ unsigned char File::loadChar () { return fgetc(file); } void File::storeChar (unsigned char val) { fputc(val, file); } /** * Load an unsigned short int from the file. * * @return The value read */ unsigned short int File::loadShort () { unsigned short int val; val = fgetc(file); val += fgetc(file) << 8; return val; } /** * Load an unsigned short int with an upper limit from the file. * * @return The value read */ unsigned short int File::loadShort (unsigned short int max) { unsigned short int val; val = loadShort(); if (val > max) { LOG_ERROR("Oversized value %d>%d in file %s", val, max, filePath); return max; } return val; } void File::storeShort (unsigned short int val) { fputc(val & 255, file); fputc(val >> 8, file); } /** * Load a signed int from the file. * * @return The value read */ signed int File::loadInt () { unsigned int val; val = fgetc(file); val += fgetc(file) << 8; val += fgetc(file) << 16; val += fgetc(file) << 24; return *((signed int *)&val); } void File::storeInt (signed int val) { unsigned int uval; uval = *((unsigned int *)&val); fputc(uval & 255, file); fputc((uval >> 8) & 255, file); fputc((uval >> 16) & 255, file); fputc(uval >> 24, file); } /** * Load a block of uncompressed data from the file. * * @param length The length of the block * * @return Buffer containing the block of data */ unsigned char * File::loadBlock (int length) { unsigned char *buffer; buffer = new unsigned char[length]; int res = fread(buffer, 1, length, file); if (res != length) LOG_ERROR("Could not read whole block (%d of %d bytes read)", res, length); return buffer; } /** * Load a block of RLE compressed data from the file. * * @param length The length of the uncompressed block * * @return Buffer containing the uncompressed data */ unsigned char* File::loadRLE (int length) { // Determine the offset that follows the block int next = fgetc(file); next += fgetc(file) << 8; next += ftell(file); unsigned char* buffer = new unsigned char[length]; int pos = 0; while (pos < length) { int rle = fgetc(file); if (rle & 128) { int byte = fgetc(file); for (int i = 0; i < (rle & 127); i++) { buffer[pos++] = byte; if (pos >= length) break; } } else if (rle) { for (int i = 0; i < rle; i++) { buffer[pos++] = fgetc(file); if (pos >= length) break; } } else buffer[pos++] = fgetc(file); } fseek(file, next, SEEK_SET); return buffer; } /** * Skip past a block of RLE compressed data in the file. */ void File::skipRLE () { int next; next = fgetc(file); next += fgetc(file) << 8; fseek(file, next, SEEK_CUR); } /** * Load a block of LZ compressed data from the file. * * @param compressedLength The length of the compressed block * @param length The length of the uncompressed block * * @return Buffer containing the uncompressed data */ unsigned char* File::loadLZ (int compressedLength, int length) { unsigned char* compressedBuffer; unsigned char* buffer; compressedBuffer = loadBlock(compressedLength); buffer = new unsigned char[length]; uncompress(buffer, (unsigned long int *)&length, compressedBuffer, compressedLength); delete[] compressedBuffer; return buffer; } /** * Load a string from the file. * * @return The new string */ char * File::loadString () { char *string; int length = fgetc(file); if (length) { string = new char[length + 1]; int res = fread(string, 1, length, file); if (res != length) LOG_ERROR("Could not read whole string (%d of %d bytes read)", res, length); } else { // If the length is not given, assume it is an 8.3 file name string = new char[13]; LOG_TRACE("Assuming reading a filename from %s", filePath); int count; for (count = 0; count < 9; count++) { string[count] = fgetc(file); if (string[count] == '.') { string[++count] = fgetc(file); string[++count] = fgetc(file); string[++count] = fgetc(file); count++; break; } } length = count; } string[length] = 0; return string; } /** * Load a string with given length from the file. * * @return The new string */ char * File::loadString (int length) { char *string = new char[length + 1]; int res = fread(string, 1, length, file); if (res != length) LOG_ERROR("Could not read whole string (%d of %d bytes read)", res, length); string[length] = '\0'; return string; } /** * Load RLE compressed graphical data from the file. * * @param width The width of the image to load * @param height The height of the image to load * * @return SDL surface containing the loaded image */ SDL_Surface* File::loadSurface (int width, int height) { SDL_Surface* surface; unsigned char* pixels; pixels = loadRLE(width * height); surface = createSurface(pixels, width, height); delete[] pixels; return surface; } /** * Load a block of scrambled pixel data from the file. * * @param length The length of the block * * @return Buffer containing the de-scrambled data */ unsigned char* File::loadPixels (int length) { unsigned char* pixels; unsigned char* sorted; int count; sorted = new unsigned char[length]; pixels = loadBlock(length); // Rearrange pixels in correct order for (count = 0; count < length; count++) { sorted[count] = pixels[(count >> 2) + ((count & 3) * (length >> 2))]; } delete[] pixels; return sorted; } /** * Load a block of scrambled and masked pixel data from the file. * * @param length The length of the block * @param key The transparent pixel value * * @return Buffer containing the de-scrambled data */ unsigned char* File::loadPixels (int length, int key) { unsigned char* pixels; unsigned char* sorted; unsigned char mask = 0; int count; sorted = new unsigned char[length]; pixels = new unsigned char[length]; // Read the mask // Each mask pixel is either 0 or 1 // Four pixels are packed into the lower end of each byte for (count = 0; count < length; count++) { if (!(count & 3)) mask = fgetc(file); pixels[count] = (mask >> (count & 3)) & 1; } // Pixels are loaded if the corresponding mask pixel is 1, otherwise // the transparent index is used. Pixels are scrambled, so the mask // has to be scrambled the same way. for (count = 0; count < length; count++) { sorted[(count >> 2) + ((count & 3) * (length >> 2))] = pixels[count]; } // Read pixels according to the scrambled mask for (count = 0; count < length; count++) { // Use the transparent pixel pixels[count] = key; if (sorted[count] == 1) { // The unmasked portions are transparent, so no masked // portion should be transparent. while (pixels[count] == key) pixels[count] = fgetc(file); } } // Rearrange pixels in correct order for (count = 0; count < length; count++) { sorted[count] = pixels[(count >> 2) + ((count & 3) * (length >> 2))]; } delete[] pixels; return sorted; } /** * Load a palette from the file. * * @param palette The palette to be filled with loaded colours * @param rle Whether or not the palette data is RLE-encoded */ void File::loadPalette (SDL_Color* palette, bool rle) { unsigned char* buffer; int count; if (rle) buffer = loadRLE(MAX_PALETTE_COLORS * 3); else buffer = loadBlock(MAX_PALETTE_COLORS * 3); for (count = 0; count < MAX_PALETTE_COLORS; count++) { // Palette entries are 6-bit // Shift them upwards to 8-bit, and fill in the lower 2 bits palette[count].r = (buffer[count * 3] << 2) + (buffer[count * 3] >> 4); palette[count].g = (buffer[(count * 3) + 1] << 2) + (buffer[(count * 3) + 1] >> 4); palette[count].b = (buffer[(count * 3) + 2] << 2) + (buffer[(count * 3) + 2] >> 4); } delete[] buffer; } PathMgr::PathMgr(): paths(NULL), has_config(false), has_temp(false) { }; PathMgr::~PathMgr() { delete paths; }; bool PathMgr::add(char* newPath, int newPathType) { // Check for CWD if(!strlen(newPath)) { delete[] newPath; char cwd[1024]; if (getcwd(cwd, sizeof(cwd)) != NULL) { newPath = createString(cwd); } else { LOG_WARN("Could not get current working directory!"); return false; } } // Append a directory separator if necessary if (newPath[strlen(newPath) - 1] != OJ_DIR_SEP) { char* tmp = createString(newPath, OJ_DIR_SEP_STR); delete[] newPath; newPath = tmp; } // all paths need to be readable if (access(newPath, R_OK) != 0) { LOG_TRACE("Path '%s' is not readable, ignoring!", newPath); delete[] newPath; return false; } // ignore, if already present if(has_config) newPathType &= ~PATH_TYPE_CONFIG; if(has_temp) newPathType &= ~PATH_TYPE_TEMP; // config and temp dir need to be writeable if ((newPathType & (PATH_TYPE_CONFIG|PATH_TYPE_TEMP)) > 0) { if (access(newPath, W_OK) != 0) { LOG_WARN("Path '%s' is not writeable, disabling!", newPath); newPathType &= ~PATH_TYPE_CONFIG; newPathType &= ~PATH_TYPE_TEMP; } } if(newPathType == PATH_TYPE_INVALID) { delete[] newPath; return false; } // we only need one directory for these if (newPathType & PATH_TYPE_CONFIG) has_config = true; if (newPathType & PATH_TYPE_TEMP) has_temp = true; // Finally add paths = new Path(paths, newPath, newPathType); return true; } /** * Create a new directory path object. * * @param newNext Next path * @param newPath The new path */ Path::Path (Path* newNext, char* newPath, int newPathType) { char pathInfo[10] = {}; if(newPathType & PATH_TYPE_SYSTEM) strcat(pathInfo, "S"); if(newPathType & PATH_TYPE_CONFIG) strcat(pathInfo, "C"); if(newPathType & PATH_TYPE_GAME) strcat(pathInfo, "G"); if(newPathType & PATH_TYPE_TEMP) strcat(pathInfo, "T"); if(newPathType & PATH_TYPE_ANY) strcat(pathInfo, "A"); LOG_DEBUG("Adding '%s' to the path list [%s]", newPath, pathInfo); next = newNext; path = newPath; pathType = newPathType; } /** * Delete the directory path object. */ Path::~Path () { if (next) delete next; delete[] path; } openjazz-20240919/src/io/file.h000066400000000000000000000053231467303304200161210ustar00rootroot00000000000000 /** * * @file file.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 3rd February 2009: Created file.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2010 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _FILE_H #define _FILE_H #include "OpenJazz.h" #include #include // Classes /// File i/o class File { private: FILE* file; char* filePath; bool open (const char* path, const char* name, bool write); public: File (const char* name, int pathType, bool write = false); ~File (); int getSize (); void seek (int offset, bool reset = false); int tell (); unsigned char loadChar (); void storeChar (unsigned char val); unsigned short int loadShort (); unsigned short int loadShort (unsigned short int max); void storeShort (unsigned short int val); signed int loadInt (); void storeInt (signed int val); unsigned char* loadBlock (int length); unsigned char* loadRLE (int length); void skipRLE (); unsigned char* loadLZ (int compressedLength, int length); char* loadString (); char* loadString (int length); SDL_Surface* loadSurface (int width, int height); unsigned char* loadPixels (int length); unsigned char* loadPixels (int length, int key); void loadPalette (SDL_Color* palette, bool rle = true); }; /// Directory path enum path_type { PATH_TYPE_INVALID = 0, ///< Invalid directory, do not use PATH_TYPE_SYSTEM = 1 << 0, ///< Read-only system directory PATH_TYPE_CONFIG = 1 << 1, ///< User writable configuration directory PATH_TYPE_GAME = 1 << 2, ///< Directory containing game data PATH_TYPE_TEMP = 1 << 3, ///< User writable temporary directory PATH_TYPE_ANY = 1 << 4 ///< Special case: any type }; class Path { public: Path* next; ///< Next path to check char* path; ///< Path int pathType; ///< One or more of path_type enum Path (Path* newNext, char* newPath, int newPathType); ~Path (); }; class PathMgr { public: PathMgr(); ~PathMgr(); bool add(char* newPath, int newPathType = PATH_TYPE_ANY); Path* paths; bool has_config; bool has_temp; }; // Directory Seperator #ifdef _WIN32 #define OJ_DIR_SEP '\\' #define OJ_DIR_SEP_STR "\\" #else #define OJ_DIR_SEP '/' #define OJ_DIR_SEP_STR "/" #endif // Variable EXTERN PathMgr gamePaths; ///< Paths to files #endif openjazz-20240919/src/io/gfx/000077500000000000000000000000001467303304200156125ustar00rootroot00000000000000openjazz-20240919/src/io/gfx/anim.cpp000066400000000000000000000134311467303304200172440ustar00rootroot00000000000000 /** * * @file anim.cpp * * Part of the OpenJazz project * * @par History * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp * - 26th July 2009: Created anim.cpp from parts of sprite.cpp * * @par Licence * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "anim.h" #include "sprite.h" #include "jj1/level/jj1level.h" /** * Create empty animation. */ Anim::Anim () { sprites = new Sprite *[19]; xOffsets = new signed char[19]; yOffsets = new signed char[19]; shootX = shootY = 0; accessoryX = accessoryY = 0; yOffset = 0; frames = 19; frame = 0; accessory = 0; } /** * Delete animation. */ Anim::~Anim () { delete[] sprites; delete[] xOffsets; delete[] yOffsets; } /** * Set overall animation data. * * @param length Number of frames * @param sX Bullet generation x-coordinate * @param sY Bullet generation y-coordinate * @param aX Accessory animation x-coordinate * @param aY Accessory animation y-coordinate * @param a Accessory animation index * @param y Vertical offset */ void Anim::setData (int length, signed char sX, signed char sY, signed char aX, signed char aY, unsigned char a, signed char y) { if (length > 19) { delete[] sprites; delete[] xOffsets; delete[] yOffsets; sprites = new Sprite *[length]; xOffsets = new signed char[length]; yOffsets = new signed char[length]; } frames = length; shootX = sX; shootY = sY; accessoryX = aX; accessoryY = aY; accessory = a; yOffset = y; } /** * Set current frame. * * @param nextFrame The frame to use * @param looping Whether the animation should stop at the end or loop */ void Anim::setFrame (int nextFrame, bool looping) { if (looping) frame = nextFrame % frames; else frame = (nextFrame >= frames)? frames - 1: nextFrame; } /** * Set the data for the current frame. * * @param sprite Sprite to use * @param x Horizontal offset * @param y Vertical offset */ void Anim::setFrameData (Sprite *sprite, signed char x, signed char y) { sprites[frame] = sprite; xOffsets[frame] = x; yOffsets[frame] = y; } /** * Determine the width of the current frame. * * @return The width of the current frame */ int Anim::getWidth () { return sprites[frame]->getWidth(); } /** * Determine the height of the current frame. * * @return The height of the current frame */ int Anim::getHeight () { return sprites[frame]->getHeight(); } /** * Determine the length of the animation. * * @return The length of the animation */ int Anim::getLength () { return frames; } /** * Determine the bullet generation x-coordinate of the current frame. * * @return The bullet generation x-coordinate */ fixed Anim::getShootX () { return ITOF(shootX + xOffsets[frame]) << 2; } /** * Determine the bullet generation y-coordinate of the current frame. * * @return The bullet generation y-coordinate */ fixed Anim::getShootY () { return ITOF(shootY + yOffsets[frame]); } /** * Determine the accessory bullet generation x-coordinate of the current frame. * * @return The accessory bullet generation x-coordinate */ fixed Anim::getAccessoryShootX () { return ITOF(shootX + accessoryX + xOffsets[frame]) << 2; } /** * Determine the accessory bullet generation y-coordinate of the current frame. * * @return The accessory bullet generation y-coordinate */ fixed Anim::getAccessoryShootY () { return ITOF(shootY + accessoryY + yOffsets[frame]); } /** * Determine the vertical offset. * * @return The vertical offset */ fixed Anim::getOffset () { return -ITOF(yOffset); } /** * Determine the horizontal offset of the current frame. * * @return The horizontal offset */ fixed Anim::getXOffset () { return ITOF(sprites[frame]->getXOffset() + (xOffsets[frame] << 2) + 1); } /** * Determine the vertical offset of the current frame. * * @return The vertical offset */ fixed Anim::getYOffset () { return ITOF(sprites[frame]->getYOffset() + yOffsets[frame] + 1); } /** * Draw current frame. * * @param x X-coordinate at which to draw * @param y Y-coordinate at which to draw * @param accessories Number of accessory animations to draw */ void Anim::draw (fixed x, fixed y, int accessories) { sprites[frame]->draw( FTOI(x) + (xOffsets[frame] << 2), FTOI(y) + yOffsets[frame] - yOffset); if (accessories && accessory) { Anim* anim = level->getAnim(accessory); anim->setFrame(frame, true); anim->draw( x + ITOF(accessoryX << 2), y + ITOF(accessoryY - yOffset) - anim->getOffset(), accessories - 1); } } /** * Draw current frame scaled. * * @param x X-coordinate at which to draw * @param y Y-coordinate at which to draw * @param scale Scaling factor */ void Anim::drawScaled (fixed x, fixed y, fixed scale) { // Used to draw bonus level player, so no offset sprites[frame]->drawScaled(FTOI(x), FTOI(y), scale); } /** * Set the current frame's palette. * * @param palette The new palette to use * @param start The first entry to use * @param amount The number of entries to use */ void Anim::setPalette (SDL_Color *palette, int start, int amount) { sprites[frame]->setPalette(palette, start, amount); } /** * Turn the whole of the current frame a single colour. * * @param index The index of the colour to use */ void Anim::flashPalette (int index) { sprites[frame]->flashPalette(index); } /** * Restore the current frame's original palette. */ void Anim::restorePalette () { sprites[frame]->restorePalette(); } openjazz-20240919/src/io/gfx/anim.h000066400000000000000000000046701467303304200167160ustar00rootroot00000000000000 /** * * @file anim.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 19th March 2009: Created sprite.h from parts of level.h * - 26th July 2009: Created anim.h from parts of sprite.h * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _ANIM_H #define _ANIM_H #include "OpenJazz.h" #include // Classes class Sprite; /// Animation class Anim { private: Sprite** sprites; ///< Sprite images signed char* xOffsets; ///< Horizontal offsets for each frame signed char* yOffsets; ///< Vertical offsets for each frame signed char shootX; ///< Bullet generation x-coordinate signed char shootY; ///< Bullet generation y-coordinate signed char accessoryX; ///< Accessory animation x-coordinate signed char accessoryY; ///< Accessory animation y-coordinate signed char yOffset; ///< Vertical offset unsigned char frames; ///< Number of frames unsigned char frame; ///< Current frame unsigned char accessory; ///< Number of an animation that is an accessory to this animation ///< Most of the time accessories are used with guardians. Anim(const Anim&); // non construction-copyable Anim& operator=(const Anim&); // non copyable public: Anim (); ~Anim (); void setData (int length, signed char sX, signed char sY, signed char aX, signed char aY, unsigned char a, signed char y); void setFrame (int nextFrame, bool looping); void setFrameData (Sprite *frameSprite, signed char x, signed char y); int getWidth (); int getHeight (); int getLength (); fixed getShootX (); fixed getShootY (); fixed getAccessoryShootX (); fixed getAccessoryShootY (); fixed getOffset (); fixed getXOffset (); fixed getYOffset (); void draw (fixed x, fixed y, int accessories = 7); void drawScaled (fixed x, fixed y, fixed scale); void setPalette (SDL_Color *palette, int start, int amount); void flashPalette (int index); void restorePalette (); }; #endif openjazz-20240919/src/io/gfx/font.cpp000066400000000000000000000276241467303304200172770ustar00rootroot00000000000000 /** * * @file font.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created font.c * - 3rd February 2009: Renamed font.c to font.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading, displaying and freeing of screen fonts. * */ #include "../file.h" #include "font.h" #include "video.h" #include /** * Load a font from the given .0FN file. * * @param fileName Name of an .0FN file */ Font::Font (const char* fileName) { File* file; unsigned char* pixels; unsigned char* blank; int fileSize; int count, width, height; // Load font from a font file try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { throw; } fileSize = file->getSize(); nCharacters = 128; file->seek(20, true); lineHeight = file->loadChar() << 1; // Create blank character data blank = new unsigned char[3]; memset(blank, 0, 3); // Load characters for (count = 0; count < 128; count++) { if (file->tell() >= fileSize) { nCharacters = count; break; } int size = file->loadShort(); if (size > 4) { pixels = file->loadRLE(size); width = pixels[0]; width += pixels[1] << 8; height = pixels[2]; height += pixels[3] << 8; if (size - 4 >= width * height) characters[count] = createSurface(pixels + 4, width, height); else characters[count] = createSurface(blank, 3, 1); delete[] pixels; } else characters[count] = createSurface(blank, 3, 1); enableColorKey(characters[count], 0); } delete[] blank; delete file; // Create ASCII->font map for (count = 0; count < 33; count++) map[count] = 0; map[33] = 107; // ! map[34] = 116; // " map[35] = 0; // # map[36] = 63; // $ map[37] = 0; // % map[38] = 0; // & map[39] = 115; // ' map[40] = 111; // ( map[41] = 112; // ) map[42] = 0; // * map[43] = 105; // + map[44] = 101; // , map[45] = 104; // - map[46] = 102; // . map[47] = 108; // / for (count = 48; count < 58; count++) map[count] = count + 5; // Numbers map[58] = 114; // : map[59] = 113; // ; map[60] = 0; // < map[61] = 106; // = map[62] = 0; // > map[63] = 103; // ? map[64] = 0; // @ for (count = 65; count < 91; count++) map[count] = count - 38; // Upper-case letters for (; count < 97; count++) map[count] = 0; for (; count < 123; count++) map[count] = count - 96; // Lower-case letters for (; count < 128; count++) map[count] = 0; for (count = 0; count < 128; count++) { if (map[count] >= nCharacters) map[count] = 0; } } /** * Create a font from the panel pixel data. * * @param pixels Panel pixel data * @param big Whether to use the small or the big font */ Font::Font (unsigned char* pixels, bool big) { unsigned char* chrPixels; int count, y; if (big) lineHeight = 8; else lineHeight = 7; chrPixels = new unsigned char[8 * lineHeight]; for (count = 0; count < 40; count++) { for (y = 0; y < lineHeight; y++) memcpy(chrPixels + (y * 8), pixels + (count * 8) + (y * SW), 8); characters[count] = createSurface(chrPixels, 8, lineHeight); if (big) enableColorKey(characters[count], 31); } nCharacters= 40; delete[] chrPixels; // Create ASCII->font map if (big) { // Goes " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:." for (count = 0; count < 45; count++) map[count] = 0; map[count++] = 37; map[count++] = 39; for (; count < 48; count++) map[count] = 0; for (; count < 58; count++) map[count] = count - 47; // Numbers map[count++] = 38; for (; count < 65; count++) map[count] = 0; for (; count < 91; count++) map[count] = count - 54; // Upper-case letters for (; count < 97; count++) map[count] = 0; for (; count < 123; count++) map[count] = count - 86; // Lower-case letters for (; count < 128; count++) map[count] = 0; } else { // Goes " 0123456789oo" (where oo = infinity) // Use :; to represent the infinity symbol for (count = 0; count < 48; count++) map[count] = 0; for (; count < 60; count++) map[count] = count - 47; // Numbers and :; for (; count < 128; count++) map[count] = 0; } } /** * Load a font from a .000 file. * * @param bonus whether to use FONTS.000 or BONUS.000 */ Font::Font (bool bonus) { File* file; unsigned char* pixels; int fileSize; int count; // Load font from FONTS.000 or BONUS.000 try { file = new File(bonus? "BONUS.000": "FONTS.000", PATH_TYPE_GAME); } catch (int e) { throw; } fileSize = file->getSize(); nCharacters = file->loadShort(256); if (bonus) { count = file->loadShort(); nCharacters -= count; // Skip sprites for (; count > 0; count--) { file->seek(4, false); int width = file->loadShort(); if (width == 0xFFFF) width = 0; file->seek((width << 2) + file->loadShort(), false); } } // Load characters for (count = 0; count < nCharacters; count++) { if (file->tell() >= fileSize) { nCharacters = count; break; } int width = file->loadShort(SW); int height = file->loadShort(SH); if (bonus) width = (width + 3) & ~3; else width <<= 2; file->seek(4, false); pixels = file->loadPixels(width * height); characters[count] = createSurface(pixels, width, height); enableColorKey(characters[count], 254); delete[] pixels; } delete file; lineHeight = characters[0]->h; // Create blank character data pixels = new unsigned char[3]; memset(pixels, 254, 3); characters[nCharacters] = createSurface(pixels, 3, 1); enableColorKey(characters[nCharacters], 254); delete[] pixels; // Create ASCII->font map count = 0; if (bonus) { for (; count < 42; count++) map[count] = nCharacters; map[count++] = 37; // * for (; count < 46; count++) map[count] = nCharacters; map[count++] = 39; // . map[count++] = 38; // / for (; count < 59; count++) map[count] = count - 22; // Numbers and : } else { for (; count < 37; count++) map[count] = nCharacters; map[count++] = 36; // % for (; count < 48; count++) map[count] = nCharacters; for (; count < 58; count++) map[count] = count - 22; // Numbers } for (; count < 65; count++) map[count] = nCharacters; for (; count < 91; count++) map[count] = count - 65; // Upper-case letters for (; count < 97; count++) map[count] = nCharacters; for (; count < 123; count++) map[count] = count - 97; // Lower-case letters for (; count < 128; count++) map[count] = nCharacters; nCharacters++; for (count = 0; count < 128; count++) { if (map[count] >= nCharacters) map[count] = 0; } } /** * Delete the font. */ Font::~Font () { for (int i = 0; i < nCharacters; i++) SDL_FreeSurface(characters[i]); } /** * Draw a string using the font. * * @param string The string to draw * @param x The x-coordinate at which to draw the string * @param y The y-coordinate at which to draw the string * * @return The x-coordinate of the end of the string */ int Font::showString (const char* string, int x, int y) { SDL_Surface* surface; SDL_Rect dst; unsigned int count; int xOffset, yOffset; // Determine the position at which to draw the first character xOffset = x; yOffset = y; // Go through each character of the string for (count = 0; string[count]; count++) { if (string[count] == '\n') { xOffset = x; yOffset += lineHeight; } else { // Determine the character's position on the screen dst.y = yOffset; dst.x = xOffset; // Determine the character's surface surface = characters[int(map[int(string[count])])]; // Draw the character to the screen SDL_BlitSurface(surface, NULL, canvas, &dst); xOffset += surface->w + 2; } } return xOffset; } /** * Draw a JJ1 cutscene string using the font. * * @param string The JJ1 cutstring to draw * @param x The x-coordinate at which to draw the string * @param y The y-coordinate at which to draw the string * * @return The x-coordinate of the end of the string */ int Font::showSceneString (const unsigned char* string, int x, int y) { SDL_Surface* surface; SDL_Rect dst; unsigned int count; int offset; // Determine the position at which to draw the first character offset = x; // Go through each character of the string for (count = 0; string[count]; count++) { // Determine the character's position on the screen dst.y = y; dst.x = offset; // Determine the character's surface if (string[count] < nCharacters) surface = characters[int(string[count])]; else surface = characters[0]; // Draw the character to the screen SDL_BlitSurface(surface, NULL, canvas, &dst); offset += surface->w + 1; } return offset; } /** * Draw a number using the font. * * @param n The number to draw * @param x The x-coordinate at which to draw the number * @param y The y-coordinate at which to draw the number * * @return The x-coordinate of the end of the number */ void Font::showNumber (int n, int x, int y) { SDL_Surface *surface; SDL_Rect dst; int count, offset; // n being 0 is a special case. It must not be considered to be a trailing // zero, as these are not displayed. if (!n) { // Determine 0's surface surface = characters[int(map[int('0')])]; // Determine 0's position on the screen dst.y = y; dst.x = x - surface->w; // Draw 0 to the screen SDL_BlitSurface(surface, NULL, canvas, &dst); return; } // Determine the length of the number to be drawn if (n > 0) count = n; else count = -n; // Determine the position at which to draw the lowest digit offset = x; while (count) { // Determine the digit's surface surface = characters[int(map['0' + (count % 10)])]; offset -= surface->w; // Determine the digit's position on the screen dst.y = y; dst.x = offset; // Draw the digit to the screen SDL_BlitSurface(surface, NULL, canvas, &dst); count /= 10; } // If needed, draw the negative sign if (n < 0) { // Determine the negative sign's surface surface = characters[int(map[int('-')])]; // Determine the negative sign's position on the screen dst.y = y; dst.x = offset - surface->w; // Draw the negative sign to the screen SDL_BlitSurface(surface, NULL, canvas, &dst); } } /** * Map a range of palette indices to another range * * @param start Start of original range * @param length Span of original range * @param newStart Start of new range * @param newLength Span of new range */ void Font::mapPalette (int start, int length, int newStart, int newLength) { SDL_Color palette[MAX_PALETTE_COLORS]; int count; for (count = 0; count < length; count++) palette[count].r = palette[count].g = palette[count].b = (count * newLength / length) + newStart; for (count = 0; count < nCharacters; count++) setLogicalPalette(characters[count], palette, start, length); } /** * Restore a palette to its original state. */ void Font::restorePalette () { int count; for (count = 0; count < nCharacters; count++) video.restoreSurfacePalette(characters[count]); } /** * Get the height of a single line of any text. * * @return The height */ int Font::getHeight () { return lineHeight; } /** * Get the width of a single line of a given string. * * @param string The string to measure * * @return The width */ int Font::getStringWidth (const char *string) { int count; int stringWidth = 0; // Go through each character of the string for (count = 0; string[count]; count++) { // Only get the width of the first line if (string[count] == '\n') return stringWidth; stringWidth += characters[int(map[int(string[count])])]->w + 2; } return stringWidth; } /** * Get the width of a single line of a given JJ1 cutscene string. * * @param string The string to measure * * @return The width */ int Font::getSceneStringWidth (const unsigned char *string) { int count; int stringWidth = 0; // Go through each character of the string for (count = 0; string[count]; count++) { if (string[count] < nCharacters) stringWidth += characters[int(string[count])]->w + 1; else stringWidth += characters[0]->w + 1; } return stringWidth; } openjazz-20240919/src/io/gfx/font.h000066400000000000000000000033151467303304200167330ustar00rootroot00000000000000 /** * * @file font.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 3rd February 2009: Created font.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2010 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _FONT_H #define _FONT_H #include "OpenJazz.h" #include // Classes class File; /// Font class Font { private: SDL_Surface *characters[128]; ///< Symbol images int nCharacters; ///< Number of symbols unsigned char lineHeight; ///< Vertical spacing of displayed characters char map[128]; ///< Maps ASCII values to symbol indices public: explicit Font(const char *fileName); Font(unsigned char *pixels, bool big); explicit Font(bool bonus); ~Font(); int showString (const char *s, int x, int y); int showSceneString (const unsigned char *s, int x, int y); void showNumber (int n, int x, int y); void mapPalette (int start, int length, int newStart, int newLength); void restorePalette (); int getHeight (); int getStringWidth (const char *string); int getSceneStringWidth (const unsigned char *string); }; // Variables EXTERN Font *font2; /** Taken from .0FN file name */ EXTERN Font *fontbig; /** Taken from .0FN file name */ EXTERN Font *fontiny; /** Taken from .0FN file name */ EXTERN Font *fontmn1; /** Taken from .0FN file name */ EXTERN Font *fontmn2; /** Taken from .0FN file name */ EXTERN Font *panelBigFont; /** Found in PANEL.000 */ EXTERN Font *panelSmallFont; /** Found in PANEL.000 */ #endif openjazz-20240919/src/io/gfx/paletteeffects.cpp000066400000000000000000000404221467303304200213160ustar00rootroot00000000000000 /** * * @file paletteeffects.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created main.c * - 22nd July 2008: Created util.c from parts of main.c * - 3rd February 2009: Renamed main.c to main.cpp and util.c to util.cpp * - 4th February 2009: Created palette.cpp from parts of main.cpp and util.cpp * - 1st August 2009: Renamed palette.cpp to paletteeffects.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "paletteeffects.h" #include "video.h" #include "jj1/level/jj1level.h" #ifdef ENABLE_JJ2 #include "jj2/level/jj2level.h" #endif #include "level/levelplayer.h" #include "player/player.h" #include /** * Create a new palette effect. * * @param nextPE The next palette effect */ PaletteEffect::PaletteEffect (PaletteEffect* nextPE) { next = nextPE; } /** * Delete the palette effect. */ PaletteEffect::~PaletteEffect () { if (next) delete next; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void PaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); } /** * Create a new white-in palette effect. * * @param newDuration The length of time the effect will last * @param nextPE The next palette effect */ WhiteInPaletteEffect::WhiteInPaletteEffect (int newDuration, PaletteEffect* nextPE) : PaletteEffect (nextPE) { duration = newDuration; whiteness = F1 + FH; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void WhiteInPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { int count; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); if (whiteness > F1) { memset(shownPalette, 255, sizeof(SDL_Color) * MAX_PALETTE_COLORS); if (!isStatic) whiteness -= ITOF(mspf) / duration; } else if (whiteness > 0) { for (count = 0; count < MAX_PALETTE_COLORS; count++) { shownPalette[count].r = 255 - FTOI((255 - shownPalette[count].r) * (F1 - whiteness)); shownPalette[count].g = 255 - FTOI((255 - shownPalette[count].g) * (F1 - whiteness)); shownPalette[count].b = 255 - FTOI((255 - shownPalette[count].b) * (F1 - whiteness)); } if (!isStatic) whiteness -= ITOF(mspf) / duration; } if (direct) video.changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } /** * Create a new fade-in palette effect. * * @param newDuration The length of time the effect will last * @param nextPE The next palette effect */ FadeInPaletteEffect::FadeInPaletteEffect (int newDuration, PaletteEffect* nextPE) : PaletteEffect (nextPE) { duration = newDuration; blackness = F1 + FH; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void FadeInPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { int count; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); if (blackness > F1) { memset(shownPalette, 0, sizeof(SDL_Color) * MAX_PALETTE_COLORS); if (!isStatic) blackness -= ITOF(mspf) / duration; } else if (blackness > 0) { for (count = 0; count < MAX_PALETTE_COLORS; count++) { shownPalette[count].r = FTOI(shownPalette[count].r * (F1 - blackness)); shownPalette[count].g = FTOI(shownPalette[count].g * (F1 - blackness)); shownPalette[count].b = FTOI(shownPalette[count].b * (F1 - blackness)); } if (!isStatic) blackness -= ITOF(mspf) / duration; } if (direct) video.changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } /** * Create a new white-out palette effect. * * @param newDuration The length of time the effect will last * @param nextPE The next palette effect */ WhiteOutPaletteEffect::WhiteOutPaletteEffect (int newDuration, PaletteEffect* nextPE) : PaletteEffect (nextPE) { duration = newDuration; whiteness = 0; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void WhiteOutPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); if (whiteness > F1) { memset(shownPalette, 255, sizeof(SDL_Color) * MAX_PALETTE_COLORS); } else { if (whiteness > 0) { for (int i = 0; i < MAX_PALETTE_COLORS; i++) { shownPalette[i].r = 255 - FTOI((255 - shownPalette[i].r) * (F1 - whiteness)); shownPalette[i].g = 255 - FTOI((255 - shownPalette[i].g) * (F1 - whiteness)); shownPalette[i].b = 255 - FTOI((255 - shownPalette[i].b) * (F1 - whiteness)); } } if (!isStatic) whiteness += ITOF(mspf) / duration; } if (direct) video.changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } /** * Create a new fade-out palette effect. * * @param newDuration The length of time the effect will last * @param nextPE The next palette effect */ FadeOutPaletteEffect::FadeOutPaletteEffect (int newDuration, PaletteEffect* nextPE) : PaletteEffect (nextPE) { duration = newDuration; blackness = -(F2 + F1); } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void FadeOutPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); if (blackness > F1) { memset(shownPalette, 0, sizeof(SDL_Color) * MAX_PALETTE_COLORS); } else { if (blackness > 0) { for (int i = 0; i < MAX_PALETTE_COLORS; i++) { shownPalette[i].r = FTOI(shownPalette[i].r * (F1 - blackness)); shownPalette[i].g = FTOI(shownPalette[i].g * (F1 - blackness)); shownPalette[i].b = FTOI(shownPalette[i].b * (F1 - blackness)); } } if (!isStatic) blackness += ITOF(mspf) / duration; } if (direct) video.changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } /** * Create a new flash-to-colour palette effect. * * @param newRed The red component of the colour * @param newGreen The green component of the colour * @param newBlue The blue component of the colour * @param newDuration The length of time the effect will last * @param nextPE The next palette effect */ FlashPaletteEffect::FlashPaletteEffect (unsigned char newRed, unsigned char newGreen, unsigned char newBlue, int newDuration, PaletteEffect* nextPE) : PaletteEffect (nextPE) { duration = newDuration; progress = -F1; red = newRed; green = newGreen; blue = newBlue; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void FlashPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { int count; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); if (progress < 0) { for (count = 0; count < MAX_PALETTE_COLORS; count++) { shownPalette[count].r = FTOI((shownPalette[count].r * -progress) + (red * (progress + F1))); shownPalette[count].g = FTOI((shownPalette[count].g * -progress) + (green * (progress + F1))); shownPalette[count].b = FTOI((shownPalette[count].b * -progress) + (blue * (progress + F1))); } if (!isStatic) progress += ITOF(mspf) / duration; } else if (progress < F1) { for (count = 0; count < MAX_PALETTE_COLORS; count++) { shownPalette[count].r = FTOI((shownPalette[count].r * progress) + (red * (F1 - progress))); shownPalette[count].g = FTOI((shownPalette[count].g * progress) + (green * (F1 - progress))); shownPalette[count].b = FTOI((shownPalette[count].b * progress) + (blue * (F1 - progress))); } if (!isStatic) progress += ITOF(mspf) / duration; } if (direct) video.changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } /** * Create a new colour rotation palette effect. * * @param newFirst The first palette index to be affected * @param newAmount The number of palette indices to be affected * @param newSpeed The speed of he rotation * @param nextPE The next palette effect */ RotatePaletteEffect::RotatePaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, PaletteEffect * nextPE) : PaletteEffect (nextPE) { first = newFirst; amount = newAmount; speed = newSpeed; position = 0; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void RotatePaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { SDL_Color* currentPalette; int count; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); currentPalette = video.getPalette(); for (count = 0; count < amount; count++) { memcpy(shownPalette + first + count, currentPalette + first + ((count + FTOI(position)) % amount), sizeof(SDL_Color)); } if (!isStatic) { position -= (mspf * speed) >> 10; while (position < 0) position += ITOF(amount); } if (direct) video.changePalette(shownPalette + first, first, amount); } /** * Create a new parallaxing sky background palette effect. * * @param newFirst The first palette index to be affected * @param newAmount The number of palette indices to be affected * @param newSpeed The relative speed of the background * @param newSkyPalette Palette containing the sky's colours * @param nextPE The next palette effect */ SkyPaletteEffect::SkyPaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, SDL_Color* newSkyPalette, PaletteEffect* nextPE) : PaletteEffect (nextPE) { skyPalette = newSkyPalette; first = newFirst; amount = newAmount; speed = newSpeed; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void SkyPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { int position, count, y; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); position = viewY + ((canvasH - 33) << 9) - F4; y = ((canvasH - 34) / 100) + 1; count = (((position * speed) / y) >> 20) % 255; if (direct) { if (count > 255 - amount) { video.changePalette(skyPalette + count, first, 255 - count); video.changePalette(skyPalette, first + (255 - count), count + amount - 255); } else { video.changePalette(skyPalette + count, first, amount); } } else { if (count > 255 - amount) { memcpy(shownPalette + first, skyPalette + count, sizeof(SDL_Color) * (255 - count)); memcpy(shownPalette + first + (255 - count), skyPalette, sizeof(SDL_Color) * (count + amount - 255)); } else { memcpy(shownPalette + first, skyPalette + count, sizeof(SDL_Color) * amount); } } } /** * Create a new 2D parallaxing background palette effect. * * @param newFirst The first palette index to be affected * @param newAmount The number of palette indices to be affected * @param newSpeed The relative speed of the background * @param nextPE The next palette effect */ P2DPaletteEffect::P2DPaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, PaletteEffect* nextPE) : PaletteEffect (nextPE) { first = newFirst; amount = newAmount; speed = newSpeed; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void P2DPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { SDL_Color* currentPalette; int count, x, y, j; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); currentPalette = video.getPalette(); x = FTOI(((256 * 32) - FTOI(viewX)) * speed); y = FTOI(((64 * 32) - FTOI(viewY)) * speed); for (count = 0; count < amount >> 3; count++) { for (j = 0; j < 8; j++) { memcpy(shownPalette + first + (count << 3) + j, currentPalette + first + (((count + y) % 8) << 3) + ((j + x) % 8), sizeof(SDL_Color)); } } if (direct) video.changePalette(shownPalette + first, first, amount); } /** * Create a new 1D parallaxing background palette effect. * * @param newFirst The first palette index to be affected * @param newAmount The number of palette indices to be affected * @param newSpeed The relative speed of the background * @param nextPE The next palette effect */ P1DPaletteEffect::P1DPaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, PaletteEffect* nextPE) : PaletteEffect (nextPE) { first = newFirst; amount = newAmount; speed = newSpeed; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void P1DPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { SDL_Color* currentPalette; fixed position; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); currentPalette = video.getPalette(); position = viewX + viewY; for (int i = 0; i < amount; i++) { memcpy(shownPalette + first + i, currentPalette + first + ((i + (amount - 1 - (FTOI(MUL(position, speed)) % amount))) % amount), sizeof(SDL_Color)); } if (direct) video.changePalette(shownPalette + first, first, amount); } /** * Create a new water palette effect. * * @param newDepth Water depth * @param nextPE The next palette effect */ WaterPaletteEffect::WaterPaletteEffect (fixed newDepth, PaletteEffect* nextPE) : PaletteEffect (nextPE) { depth = newDepth; } /** * Apply the palette effect. * * @param shownPalette The palette the effect will be applied to * @param direct Whether or not to apply the effect directly * @param mspf Ticks per frame * @param isStatic Whether the effect should advance after applying */ void WaterPaletteEffect::apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic) { SDL_Color* currentPalette; int position; // Apply the next palette effect if (next) next->apply(shownPalette, direct, mspf, isStatic); currentPalette = video.getPalette(); if (level) position = localPlayer->getLevelPlayer()->getY() - level->getWaterLevel(); #ifdef ENABLE_JJ2 else if (jj2Level) position = localPlayer->getLevelPlayer()->getY() - jj2Level->getWaterLevel(); #endif else return; if (position <= 0) return; if (position < depth) { for (int i = 0; i < MAX_PALETTE_COLORS; i++) { shownPalette[i].r = FTOI(currentPalette[i].r * (1023 - DIV(position, depth))); shownPalette[i].g = FTOI(currentPalette[i].g * (1023 - DIV(position, depth))); shownPalette[i].b = FTOI(currentPalette[i].b * (1023 - DIV(position, depth))); } } else memset(shownPalette, 0, sizeof(SDL_Color) * MAX_PALETTE_COLORS); if (direct) video.changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } openjazz-20240919/src/io/gfx/paletteeffects.h000066400000000000000000000127421467303304200207670ustar00rootroot00000000000000 /** * * @file paletteeffects.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 4th February 2009: Created palette.h from parts of OpenJazz.h * - 1st August 2009: Renamed palette.h to paletteeffects.h * * @par Licence: * Copyright (c) 2005-2010 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _PALETTE_H #define _PALETTE_H #include "OpenJazz.h" #include // Constants // Types of palette effect #define PE_FADE 0 /* Fades to black, then remains black */ #define PE_ROTATE 1 /* Cyclical colour animation */ // Level background palette effects #define PE_SKY 2 /* Transfers the appropriate section of the background palette to the main palette */ #define PE_2D 8 /* Real parallaxing background */ #define PE_1D 9 /* Diagonal lines parallaxing background */ #define PE_WATER 11 /* The deeper below water, the darker it gets */ // Class /// Palette effect base class class PaletteEffect { protected: PaletteEffect* next; ///< Next effect to use public: explicit PaletteEffect(PaletteEffect* nextPE); virtual ~PaletteEffect(); virtual void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Dissolve from white palette effect class WhiteInPaletteEffect : public PaletteEffect { private: int duration; ///< Number of milliseconds the effect lasts fixed whiteness; public: WhiteInPaletteEffect (int newDuration, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Fade in palette effect class FadeInPaletteEffect : public PaletteEffect { private: int duration; ///< Number of milliseconds the effect lasts fixed blackness; public: FadeInPaletteEffect (int newDuration, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Dissolve to white palette effect class WhiteOutPaletteEffect : public PaletteEffect { private: int duration; ///< Number of milliseconds the effect lasts fixed whiteness; public: WhiteOutPaletteEffect (int newDuration, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Fade out palette effect class FadeOutPaletteEffect : public PaletteEffect { private: int duration; ///< Number of milliseconds the effect lasts fixed blackness; public: FadeOutPaletteEffect (int newDuration, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Flash colour (dissolve to it and back again) palette effect class FlashPaletteEffect : public PaletteEffect { private: int duration; ///< Number of milliseconds the effect lasts fixed progress; unsigned char red, green, blue; ///< Flash colour public: FlashPaletteEffect (unsigned char newRed, unsigned char newGreen, unsigned char newBlue, int newDuration, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Entry rotation palette effect class RotatePaletteEffect : public PaletteEffect { private: unsigned char first; ///< The first palette index affected int amount; ///< The number of (consecutive) palette indices affected fixed speed; ///< Rotations per second fixed position; public: RotatePaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Sky palette palette effect class SkyPaletteEffect : public PaletteEffect { private: SDL_Color* skyPalette; unsigned char first; ///< The first palette index affected int amount; ///< The number of (consecutive) palette indices affected fixed speed; ///< Relative Y speed - as in Jazz 2 public: SkyPaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, SDL_Color* newSkyPalette, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// 2D parallaxing background palette effect class P2DPaletteEffect : public PaletteEffect { private: unsigned char first; ///< The first palette index affected int amount; ///< The number of (consecutive) palette indices affected fixed speed; ///< Relative X & Y speed - as in Jazz 2 public: P2DPaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// 1D parallaxing background palette effect class P1DPaletteEffect : public PaletteEffect { private: unsigned char first; ///< The first palette index affected int amount; ///< The number of (consecutive) palette indices affected fixed speed; ///< Relative X & Y speed - as in Jazz 2 public: P1DPaletteEffect (unsigned char newFirst, int newAmount, fixed newSpeed, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; /// Underwater darkening palette effect class WaterPaletteEffect : public PaletteEffect { private: fixed depth; ///< Number of pixels between water surface and total darkness public: WaterPaletteEffect (fixed newDepth, PaletteEffect* nextPE); void apply (SDL_Color* shownPalette, bool direct, int mspf, bool isStatic); }; #endif openjazz-20240919/src/io/gfx/sprite.cpp000066400000000000000000000115311467303304200176250ustar00rootroot00000000000000 /** * * @file sprite.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp * - 26th July 2009: Created anim.cpp from parts of sprite.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "video.h" #include "sprite.h" /** * Create a sprite. */ Sprite::Sprite () { pixels = NULL; xOffset = 0; yOffset = 0; } /** * Delete the sprite. */ Sprite::~Sprite () { if (pixels) SDL_FreeSurface(pixels); } /** * Make the sprite blank. */ void Sprite::clearPixels () { unsigned char data; if (pixels) SDL_FreeSurface(pixels); data = 0; pixels = createSurface(&data, 1, 1); enableColorKey(pixels, 0); } void Sprite::setOffset (short int x, short int y) { xOffset = x; yOffset = y; } /** * Set new pixel data for the sprite. * * @param data The new pixel data * @param width The width of the sprite image * @param height The height of the sprite image * @param key The transparent pixel value */ void Sprite::setPixels (unsigned char *data, int width, int height, unsigned char key) { if (pixels) SDL_FreeSurface(pixels); pixels = createSurface(data, width, height); enableColorKey(pixels, key); } /** * Get the width of the sprite. * * @return The width */ int Sprite::getWidth () { return pixels->w; } /** * Get the height of the sprite. * * @return The height */ int Sprite::getHeight() { return pixels->h; } /** * Get the horizontal offset of the sprite. * * @return The horizontal offset */ int Sprite::getXOffset () { return xOffset; } /** * Get the vertical offset of the sprite. * * @return The vertical offset */ int Sprite::getYOffset () { return yOffset; } /** * Set the sprite's palette, or a portion thereof. * * @param palette New palette * @param start First colour to change * @param amount Number of colours to change */ void Sprite::setPalette (SDL_Color *palette, int start, int amount) { setLogicalPalette(pixels, palette + start, start, amount); } /** * Map the whole of the sprite's palette to one index. * * @param index The index to use */ void Sprite::flashPalette (int index) { SDL_Color palette[MAX_PALETTE_COLORS]; for (int i = 0; i < MAX_PALETTE_COLORS; i++) palette[i].r = palette[i].g = palette[i].b = index; setLogicalPalette(pixels, palette, 0, MAX_PALETTE_COLORS); } /** * Restore the sprite's palette to its original state. */ void Sprite::restorePalette () { video.restoreSurfacePalette(pixels); } /** * Draw the sprite * * @param x The x-coordinate at which to draw the sprite * @param y The y-coordinate at which to draw the sprite * @param includeOffsets Whether or not to include the sprite's offsets */ void Sprite::draw (int x, int y, bool includeOffsets) { SDL_Rect dst; dst.x = x; dst.y = y; if (includeOffsets) { dst.x += xOffset; dst.y += yOffset; } SDL_BlitSurface(pixels, NULL, canvas, &dst); } /** * Draw the sprite scaled * * @param x The x-coordinate at which to draw the sprite * @param y The y-coordinate at which to draw the sprite * @param scale The amount by which to scale the sprite */ void Sprite::drawScaled (int x, int y, fixed scale) { int width, height, fullWidth, fullHeight; int dstX, dstY; int srcX, srcY; unsigned char key = getColorKey(pixels); fullWidth = FTOI(pixels->w * scale); if (x < -(fullWidth >> 1)) return; // Off-screen if (x + (fullWidth >> 1) > canvasW) width = canvasW + (fullWidth >> 1) - x; else width = fullWidth; fullHeight = FTOI(pixels->h * scale); if (y < -(fullHeight >> 1)) return; // Off-screen if (y + (fullHeight >> 1) > canvasH) height = canvasH + (fullHeight >> 1) - y; else height = fullHeight; if (SDL_MUSTLOCK(canvas)) SDL_LockSurface(canvas); if (y < (fullHeight >> 1)) { srcY = (fullHeight >> 1) - y; dstY = 0; } else { srcY = 0; dstY = y - (fullHeight >> 1); } while (srcY < height) { unsigned char* srcRow = static_cast(pixels->pixels) + (pixels->pitch * DIV(srcY, scale)); unsigned char* dstRow = static_cast(canvas->pixels) + (canvas->pitch * dstY); if (x < (fullWidth >> 1)) { srcX = (fullWidth >> 1) - x; dstX = 0; } else { srcX = 0; dstX = x - (fullWidth >> 1); } while (srcX < width) { unsigned char pixel = srcRow[DIV(srcX, scale)]; if (pixel != key) dstRow[dstX] = pixel; srcX++; dstX++; } srcY++; dstY++; } if (SDL_MUSTLOCK(canvas)) SDL_UnlockSurface(canvas); } openjazz-20240919/src/io/gfx/sprite.h000066400000000000000000000024541467303304200172760ustar00rootroot00000000000000 /** * * @file sprite.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 19th March 2009: Created sprite.h from parts of level.h * - 26th July 2009: Created anim.h from parts of sprite.h * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _SPRITE_H #define _SPRITE_H #include "OpenJazz.h" #include // Class /// Sprite class Sprite { private: SDL_Surface* pixels; ///< Sprite image short int xOffset; ///< Horizontal offset short int yOffset; ///< Vertical offset public: Sprite (); ~Sprite (); void clearPixels (); void setOffset (short int x, short int y); void setPixels (unsigned char* data, int width, int height, unsigned char key); int getWidth (); int getHeight (); int getXOffset (); int getYOffset (); void draw (int x, int y, bool includeOffsets = true); void drawScaled (int x, int y, fixed scale); void setPalette (SDL_Color* palette, int start, int amount); void flashPalette (int index); void restorePalette (); }; #endif openjazz-20240919/src/io/gfx/video.cpp000066400000000000000000000365141467303304200174350ustar00rootroot00000000000000 /** * * @file video.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created main.c * - 22nd July 2008: Created util.c from parts of main.c * - 3rd February 2009: Renamed util.c to util.cpp * - 13th July 2009: Created graphics.cpp from parts of util.cpp * - 26th July 2009: Renamed graphics.cpp to video.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Contains graphics utility functions. * */ #include "paletteeffects.h" #include "video.h" #ifdef SCALE #include #endif #include "setup.h" #include "util.h" #include "io/log.h" #include /** * Creates a surface. * * @param pixels Pixel data to copy into the surface. Can be NULL. * @param width Width of the pixel data and of the surface to be created * @param height Height of the pixel data and of the surface to be created * * @return The completed surface */ SDL_Surface* createSurface (unsigned char * pixels, int width, int height) { #if OJ_SDL2 int surfaceFlag = 0; // flags are unused #else int surfaceFlag = SDL_HWSURFACE; #endif // Create the surface SDL_Surface *ret = SDL_CreateRGBSurface(surfaceFlag, width, height, 8, 0, 0, 0, 0); // Set the surface's palette video.restoreSurfacePalette(ret); if (pixels) { // Upload pixel data to the surface if (SDL_MUSTLOCK(ret)) SDL_LockSurface(ret); for (int y = 0; y < height; y++) memcpy(static_cast(ret->pixels) + (ret->pitch * y), pixels + (width * y), width); if (SDL_MUSTLOCK(ret)) SDL_UnlockSurface(ret); } return ret; } /** * Create the video output object. */ Video::Video () { screen = NULL; minW = maxW = screenW = DEFAULT_SCREEN_WIDTH; minH = maxH = screenH = DEFAULT_SCREEN_HEIGHT; #ifdef SCALE scaleFactor = 1; #endif fullscreen = false; // Generate the logical palette for (int i = 0; i < MAX_PALETTE_COLORS; i++) logicalPalette[i].r = logicalPalette[i].g = logicalPalette[i].b = i; currentPalette = logicalPalette; } /** * Find the minimum and maximum horizontal and vertical resolutions. */ void Video::findResolutions () { #ifdef NO_RESIZE minW = maxW = DEFAULT_SCREEN_WIDTH; minH = maxH = DEFAULT_SCREEN_HEIGHT; // no need to sanitize return; #elif OJ_SDL2 SDL_DisplayMode mode; minW = SW; minH = SH; if (SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(window), &mode) >= 0) { maxW = mode.w; maxH = mode.h; } else { LOG_WARN("Could not query display mode, using defaults."); maxW = DEFAULT_SCREEN_WIDTH; maxH = DEFAULT_SCREEN_HEIGHT; } #else SDL_Rect **resolutions = SDL_ListModes(NULL, fullscreen? FULLSCREEN_FLAGS: WINDOWED_FLAGS); // All resolutions available, set to arbitrary limit if (resolutions == reinterpret_cast(-1)) { LOG_DEBUG("No display mode limit found."); minW = SW; minH = SH; maxW = MAX_SCREEN_WIDTH; maxH = MAX_SCREEN_HEIGHT; } else { for (int i = 0; resolutions[i] != NULL; i++) { // Save largest resolution if (i == 0) { maxW = resolutions[i]->w; maxH = resolutions[i]->h; } // Save smallest resolution if (resolutions[i + 1] == NULL) { minW = resolutions[i]->w; minH = resolutions[i]->h; } } } #endif // Sanitize if (minW < SW) minW = SW; if (minH < SH) minH = SH; if (maxW > MAX_SCREEN_WIDTH) maxW = MAX_SCREEN_WIDTH; if (maxH > MAX_SCREEN_HEIGHT) maxH = MAX_SCREEN_HEIGHT; LOG_TRACE("Allowing resolutions between %dx%d and %dx%d.", minW, minH, maxW, maxH); } /** * Initialise video output. * * @param cfg Video Options * * @return Success */ bool Video::init (SetupOptions cfg) { fullscreen = cfg.fullScreen; #if OJ_SDL2 window = SDL_CreateWindow("OpenJazz", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SW, SH, fullscreen? SDL_WINDOW_FULLSCREEN_DESKTOP: SDL_WINDOW_RESIZABLE); if (!window) { LOG_FATAL("Could not create window: %s", SDL_GetError()); return false; } renderer = SDL_CreateRenderer(window, -1, 0); if (!renderer) { LOG_FATAL("Could not create renderer: %s", SDL_GetError()); return false; } #endif if (fullscreen) SDL_ShowCursor(SDL_DISABLE); findResolutions(); #ifdef SCALE if ((SW * cfg.videoScale <= cfg.videoWidth) && (SH * cfg.videoScale <= cfg.videoHeight)) scaleFactor = cfg.videoScale; #endif if (!reset(cfg.videoWidth, cfg.videoHeight)) { LOG_FATAL("Could not set video mode: %s", SDL_GetError()); return false; } setTitle(NULL); return true; } /** * Shared Deinitialisation code for reset() and deinit() * */ void Video::commonDeinit () { // canvas is used when scaling or built with SDL2 if (canvas != screen && canvas) { SDL_FreeSurface(canvas); canvas = NULL; } #if OJ_SDL2 if(screen) { SDL_FreeSurface(screen); screen = NULL; } if(textureSurface) { SDL_FreeSurface(textureSurface); textureSurface = NULL; } if(texture) { SDL_DestroyTexture(texture); texture = NULL; } #endif } /** * Deinitialise video output. * */ void Video::deinit () { commonDeinit(); #if OJ_SDL2 if(renderer) { SDL_DestroyRenderer(renderer); renderer = NULL; } if(window) { SDL_DestroyWindow(window); window = NULL; } #endif } /** * Sets the size of the video window or the resolution of the screen. * * @param width New width of the window or screen * @param height New height of the window or screen * * @return Success */ bool Video::reset (int width, int height) { #ifdef NO_RESIZE screenW = DEFAULT_SCREEN_WIDTH; screenH = DEFAULT_SCREEN_HEIGHT; fullscreen = true; #else screenW = width; screenH = height; #endif commonDeinit(); // If video mode is not valid reset to low default if (screenW < minW || screenW > maxW || screenH< minH || screenH > maxH) { LOG_WARN("Video mode invalid, resetting."); screenW = minW; screenH = minH; } #if OJ_SDL2 SDL_SetWindowSize(window, screenW, screenH); SDL_SetWindowFullscreen(window, fullscreen? SDL_WINDOW_FULLSCREEN_DESKTOP: 0); #else screen = SDL_SetVideoMode(screenW, screenH, 8, fullscreen? FULLSCREEN_FLAGS: WINDOWED_FLAGS); if (!screen) return false; #endif #ifdef SCALE // sanitize if (scaleFactor < 1) scaleFactor = 1; if (scaleFactor > 4) scaleFactor = 4; // Check that the scale will fit in the current resolution while ( ((screenW/SW < scaleFactor) || (screenH/SH < scaleFactor)) && (scaleFactor > 1) ) { scaleFactor--; } if (scaleFactor > 1) { canvasW = screenW / scaleFactor; canvasH = screenH / scaleFactor; canvas = createSurface(NULL, canvasW, canvasH); } else #endif { canvasW = screenW; canvasH = screenH; #if OJ_SDL2 canvas = createSurface(NULL, canvasW, canvasH); #else canvas = screen; #endif } #if OJ_SDL2 screen = SDL_CreateRGBSurfaceFrom(canvas->pixels, canvasW, canvasH, canvas->format->BitsPerPixel, canvas->pitch, canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask); Uint32 format = SDL_PIXELFORMAT_RGB888; SDL_RendererInfo info; if (SDL_GetRendererInfo(renderer, &info) >= 0) { for (Uint32 i = 0; i < info.num_texture_formats; i++) { if (SDL_ISPIXELFORMAT_PACKED(info.texture_formats[i])) { format = info.texture_formats[i]; break; } } } texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STREAMING, canvasW, canvasH); if (!texture) { LOG_ERROR("Could not create texture: %s", SDL_GetError()); return false; } textureSurface = SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, canvasW, canvasH, SDL_BITSPERPIXEL(format), format); if (!textureSurface) { LOG_ERROR("Could not create surface: %s", SDL_GetError()); return false; } #endif #if !defined(WIZ) && !defined(GP2X) expose(); #endif return true; } /** * Sets the display palette. * * @param palette The new palette */ void Video::setPalette (SDL_Color *palette) { // Make palette changes invisible until the next draw. Hopefully. clearScreen(SDL_MapRGB(screen->format, 0, 0, 0)); flip(0); changePalette(palette, 0, MAX_PALETTE_COLORS); currentPalette = palette; } /** * Returns the current display palette. * * @return The current display palette */ SDL_Color* Video::getPalette () { return currentPalette; } /** * Sets some colours of the display palette. * * @param palette The palette containing the new colours * @param first The index of the first colour in both the display palette and the specified palette * @param amount The number of colours */ void Video::changePalette (SDL_Color *palette, unsigned char first, unsigned int amount) { #if OJ_SDL2 SDL_SetPaletteColors(screen->format->palette, palette, first, amount); #else SDL_SetPalette(screen, SDL_PHYSPAL, palette, first, amount); #endif } /** * Restores a surface's palette. * * @param surface Surface with a modified palette */ void Video::restoreSurfacePalette (SDL_Surface* surface) { setLogicalPalette(surface, logicalPalette, 0, MAX_PALETTE_COLORS); } /** * Returns the minimum possible screen width. * * @return The minimum width */ int Video::getMinWidth () { return minW; } /** * Returns the maximum possible screen width. * * @return The maximum width */ int Video::getMaxWidth () { return maxW; } /** * Returns the minimum possible screen height. * * @return The minimum height */ int Video::getMinHeight () { return minH; } /** * Returns the maximum possible screen height. * * @return The maximum height */ int Video::getMaxHeight () { return maxH; } /** * Returns the current width of the window or screen. * * @return The width */ int Video::getWidth () { return screenW; } /** * Returns the current height of the window or screen. * * @return The height */ int Video::getHeight () { return screenH; } /** * Sets the window title. * * @param the title or NULL, to use default */ void Video::setTitle (const char *title) { const char titleBase[] = "OpenJazz"; int titleLen = strlen(titleBase) + 1; if (title != NULL) { titleLen = strlen(titleBase) + 3 + strlen(title) + 1; } char *windowTitle = new char[titleLen]; strcpy(windowTitle, titleBase); if (title != NULL) { strcat(windowTitle, " - "); strcat(windowTitle, title); } #if OJ_SDL2 SDL_SetWindowTitle(window, windowTitle); #else SDL_WM_SetCaption(windowTitle, NULL); #endif delete[] windowTitle; } #ifdef SCALE /** * Returns the current scaling factor. * * @return The scaling factor */ int Video::getScaleFactor () { return scaleFactor; } /** * Sets the scaling factor. * * @param newScaleFactor The new scaling factor */ int Video::setScaleFactor (int newScaleFactor) { if ((SW * newScaleFactor <= screenW) && (SH * newScaleFactor <= screenH)) { scaleFactor = newScaleFactor; if (screen) reset(screenW, screenH); } return scaleFactor; } #endif #ifndef FULLSCREEN_ONLY /** * Determines whether or not full-screen mode is being used. * * @return Whether or not full-screen mode is being used */ bool Video::isFullscreen () { return fullscreen; } #endif /** * Refresh display palette. */ void Video::expose () { setLogicalPalette(screen, logicalPalette, 0, MAX_PALETTE_COLORS); changePalette(currentPalette, 0, MAX_PALETTE_COLORS); } /** * Update video based on a system event. * * @param event The system event. Events not affecting video will be ignored */ void Video::update (SDL_Event *event) { #if !defined(FULLSCREEN_ONLY) || !defined(NO_RESIZE) switch (event->type) { #ifndef FULLSCREEN_ONLY case SDL_KEYDOWN: // If Alt + Enter has been pressed, switch between windowed and full-screen mode. if ((event->key.keysym.sym == SDLK_RETURN) && (event->key.keysym.mod & KMOD_ALT)) { fullscreen = !fullscreen; if (fullscreen) SDL_ShowCursor(SDL_DISABLE); findResolutions(); reset(screenW, screenH); if (!fullscreen) SDL_ShowCursor(SDL_ENABLE); } break; #endif #if !OJ_SDL2 #ifndef NO_RESIZE case SDL_VIDEORESIZE: reset(event->resize.w, event->resize.h); break; #endif case SDL_VIDEOEXPOSE: expose(); break; #endif } #endif } /** * Draw graphics to screen. * * @param mspf Ticks per frame * @param paletteEffects Palette effects to use * @param effectsStopped Whether the effects should be applied without advancing */ void Video::flip (int mspf, PaletteEffect* paletteEffects, bool effectsStopped) { SDL_Color shownPalette[MAX_PALETTE_COLORS]; #ifdef SCALE if (canvas != NULL && canvas != screen) { // Copy everything that has been drawn so far if (setup.scale2x) scale(scaleFactor, screen->pixels, screen->pitch, canvas->pixels, canvas->pitch, screen->format->BytesPerPixel, canvas->w, canvas->h); else SDL_SoftStretch(canvas, NULL, screen, NULL); } #endif // Apply palette effects if (paletteEffects) { // The palette is emulated, compile all palette changes and apply at once. memcpy(shownPalette, currentPalette, sizeof(SDL_Color) * MAX_PALETTE_COLORS); paletteEffects->apply(shownPalette, false, mspf, effectsStopped); changePalette(shownPalette, 0, MAX_PALETTE_COLORS); } // Show what has been drawn #if OJ_SDL2 SDL_BlitSurface(screen, NULL, textureSurface, NULL); SDL_UpdateTexture(texture, NULL, textureSurface->pixels, textureSurface->pitch); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); #else SDL_Flip(screen); #endif } /** * Fill the screen with a colour. * * @param index Index of the colour to use */ void Video::clearScreen (int index) { #if defined(CAANOO) || defined(WIZ) || defined(GP2X) || defined(GAMESHELL) // always 240 lines cleared to black memset(video.screen->pixels, index, 320*240); #elif defined (_3DS) memset(video.screen->pixels, index, 400*240); #else SDL_FillRect(canvas, NULL, index); #endif } /** * Fill a specified rectangle of the screen with a colour. * * @param x X-coordinate of the left side of the rectangle * @param y Y-coordinate of the top of the rectangle * @param width Width of the rectangle * @param height Height of the rectangle * @param index Index of the colour to use */ void drawRect (int x, int y, int width, int height, int index) { SDL_Rect dst; dst.x = x; dst.y = y; dst.w = width; dst.h = height; SDL_FillRect(canvas, &dst, index); } /** * Sets the Color key of provided surface. * * @param surface Surface to change * @param index Color index */ void enableColorKey (SDL_Surface* surface, unsigned int index) { #if OJ_SDL2 SDL_SetColorKey(surface, SDL_TRUE, index); #else SDL_SetColorKey(surface, SDL_SRCCOLORKEY, index); #endif } /** * Returns the Color key of provided surface. * * @param surface Surface to query * * @return color index */ unsigned int getColorKey (SDL_Surface* surface) { #if OJ_SDL2 Uint32 key; if (SDL_GetColorKey(surface, &key) < 0) { LOG_WARN("Could not get Color Key: %s\n", SDL_GetError()); return -1; } return key; #else return surface->format->colorkey; #endif } /** * Sets the palette colors of provided surface. * * @param surface Surface to change * @param palette Palette to copy colors from * @param start index of first color copy * @param length number of colors to copy */ void setLogicalPalette (SDL_Surface* surface, SDL_Color *palette, int start, int length) { #if OJ_SDL2 if (surface->format->palette) { SDL_SetPaletteColors(surface->format->palette, palette, start, length); } else LOG_WARN("Could not change palette on surface without palette."); #else SDL_SetPalette(surface, SDL_LOGPAL, palette, start, length); #endif } openjazz-20240919/src/io/gfx/video.h000066400000000000000000000124721467303304200170770ustar00rootroot00000000000000 /** * * @file video.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 13th July 2009: Created graphics.h from parts of OpenJazz.h * - 26th July 2009: Renamed graphics.h to video.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _VIDEO_H #define _VIDEO_H #include "setup.h" #include "paletteeffects.h" #include #if SDL_VERSION_ATLEAST(2, 0, 0) #define OJ_SDL2 1 #else #define OJ_SDL2 0 #endif // Constants // Original screen dimensions #define SW 320 #define SH 200 #define MIN_SCALE 1 #ifdef SCALE #define MAX_SCALE 4 #else #define MAX_SCALE 1 #endif // Maximum screen dimensions #define MAX_SCREEN_WIDTH (32 * 256 * MAX_SCALE) #define MAX_SCREEN_HEIGHT (32 * 64 * MAX_SCALE) // Fullscreen and Window flags are only for SDL1.2 currently #define WINDOWED_FLAGS (SDL_RESIZABLE | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE) #if defined(CAANOO) || defined(WIZ) || defined(GP2X) || defined(GAMESHELL) #define DEFAULT_SCREEN_WIDTH 320 #define DEFAULT_SCREEN_HEIGHT 240 #define FULLSCREEN_ONLY #define NO_RESIZE #define FULLSCREEN_FLAGS (SDL_FULLSCREEN | SDL_SWSURFACE | SDL_HWPALETTE) #elif defined(DINGOO) #define DEFAULT_SCREEN_WIDTH 320 #define DEFAULT_SCREEN_HEIGHT 240 #define FULLSCREEN_ONLY #define NO_RESIZE #define FULLSCREEN_FLAGS 0 #elif defined(PSP) #define DEFAULT_SCREEN_WIDTH 480 #define DEFAULT_SCREEN_HEIGHT 272 #define FULLSCREEN_ONLY #define NO_RESIZE #define FULLSCREEN_FLAGS (SDL_FULLSCREEN | SDL_SWSURFACE | SDL_HWPALETTE) #elif defined(_3DS) #define DEFAULT_SCREEN_WIDTH 400 #define DEFAULT_SCREEN_HEIGHT 240 #define FULLSCREEN_ONLY #define NO_RESIZE #define FULLSCREEN_FLAGS (SDL_SWSURFACE | SDL_TOPSCR | SDL_CONSOLEBOTTOM) #elif defined(__wii__) #define DEFAULT_SCREEN_WIDTH 640 #define DEFAULT_SCREEN_HEIGHT 480 #define FULLSCREEN_ONLY #define NO_RESIZE #define FULLSCREEN_FLAGS (SDL_FULLSCREEN | SDL_SWSURFACE | SDL_HWPALETTE) #elif defined(__vita__) #define DEFAULT_SCREEN_WIDTH 960 #define DEFAULT_SCREEN_HEIGHT 540 #define FULLSCREEN_ONLY #define NO_RESIZE #define FULLSCREEN_FLAGS (SDL_FULLSCREEN | SDL_HWSURFACE) #elif defined(EMSCRIPTEN) #define DEFAULT_SCREEN_WIDTH 640 #define DEFAULT_SCREEN_HEIGHT 480 #define FULLSCREEN_FLAGS (SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE) #else #define DEFAULT_SCREEN_WIDTH SW #define DEFAULT_SCREEN_HEIGHT SH #define FULLSCREEN_FLAGS (SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWPALETTE) #endif // Time interval #define T_MENU_FRAME 20 // Class /// Video output class Video { private: #if OJ_SDL2 SDL_Window* window; ///< Output window SDL_Renderer* renderer; ///< Output renderer SDL_Texture* texture; ///< Output texture SDL_Surface* textureSurface; #endif SDL_Surface* screen; ///< Output surface // Palettes SDL_Color* currentPalette; ///< Current palette SDL_Color logicalPalette[MAX_PALETTE_COLORS]; ///< Logical palette (greyscale) int minW; ///< Smallest possible width int maxW; ///< Largest possible width int minH; ///< Smallest possible height int maxH; ///< Largest possible height int screenW; ///< Real width int screenH; ///< Real height #ifdef SCALE int scaleFactor; ///< Scaling factor #endif bool fullscreen; ///< Full-screen mode void findResolutions (); void expose (); public: Video (); bool init (SetupOptions cfg); void deinit (); bool reset (int width, int height); void commonDeinit (); void setPalette (SDL_Color *palette); SDL_Color* getPalette (); void changePalette (SDL_Color *palette, unsigned char first, unsigned int amount); void restoreSurfacePalette (SDL_Surface *surface); int getMinWidth (); int getMaxWidth (); int getMinHeight (); int getMaxHeight (); int getWidth (); int getHeight (); void setTitle (const char *title); #ifdef SCALE int getScaleFactor (); int setScaleFactor (int newScaleFactor); #endif #ifndef FULLSCREEN_ONLY bool isFullscreen (); #endif void update (SDL_Event *event); void flip (int mspf, PaletteEffect* paletteEffects = NULL, bool effectsStopped = false); void clearScreen (int index); }; // Variables EXTERN SDL_Surface* canvas; ///< Surface used for drawing EXTERN int canvasW; ///< Drawing surface width EXTERN int canvasH; ///< Drawing surface height EXTERN Video video; ///< Video output // Functions EXTERN SDL_Surface* createSurface (unsigned char* pixels, int width, int height); EXTERN void drawRect (int x, int y, int width, int height, int index); EXTERN void enableColorKey (SDL_Surface* surface, unsigned int index); EXTERN unsigned int getColorKey (SDL_Surface* surface); EXTERN void setLogicalPalette (SDL_Surface* surface, SDL_Color *palette, int start, int length); #endif openjazz-20240919/src/io/log.cpp000066400000000000000000000075171467303304200163250ustar00rootroot00000000000000 /** * * @file log.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Logs to console and file. * */ #include "log.h" #include #include #include #include #include #ifdef __vita__ #include #endif static struct level_info { const char *name; const char *color; } levels[7] = { { "MAX", "\x1b[95m" }, { "TRACE", "\x1b[94m" }, { "DEBUG", "\x1b[36m" }, { "INFO", "\x1b[32m" }, { "WARN", "\x1b[33m" }, { "ERROR", "\x1b[31m" }, { "FATAL", "\x1b[35m" } }; /** * Create logfile, set defaults */ Log::Log () { level = LL_DEBUG; quiet = false; logfile = fopen("openjazz.log", "w"); color_stdout = false; color_stderr = false; // these systems may use colored terminal output #if defined(__linux__) || defined(__APPLE__) // allow users to disable all colors (see https://no-color.org/) if (!getenv("NO_COLOR")) { color_stdout = !!isatty(fileno(stdout)); color_stderr = !!isatty(fileno(stderr)); } #endif #ifdef NDEBUG // be more quiet in release builds level = LL_INFO; #endif } /** * Close logfile */ Log::~Log () { if (logfile) fclose(logfile); } /** * Set new loglevel. * * @param new_level The loglevel */ void Log::setLevel(int new_level) { if (new_level > LL_FATAL) level = LL_FATAL; else if (new_level < LL_MAX) level = LL_MAX; else level = new_level; } /** * Get the loglevel. * * @return The loglevel */ int Log::getLevel() { return level; } /** * Enable/Disable quiet logging. * * @param enable Whether to enable quiet logging */ void Log::setQuiet(bool enable) { quiet = enable; } /** * Add a message to the log. * * @param lvl Verbosity level * @param file Source file * @param line Source line * @param fmt printf formatted message */ void Log::log(int lvl, const char *file, int line, const char *fmt, ...) { // skip if nothing to write if (!logfile && (lvl < level || quiet)) return; // extract file name (like basename) const char *src = strrchr(file, '\\'); if (!src) src = strrchr(file, '/'); if (!src) src = file; else src++; // get current time time_t t = time(NULL); struct tm *now = localtime(&t); // log to console, up to set verbosity if (!quiet && lvl >= level) { // choose stderr/stdout depending on loglevel, use color if available FILE *stream = stdout; bool color = color_stdout; if (lvl < LL_INFO) { stream = stderr; color = color_stderr; } #ifdef __vita__ // on vita we can only read stdout easily (with psp2shell) #define LOG(...) sceClibPrintf(__VA_ARGS__) #else #define LOG(...) fprintf(stream, __VA_ARGS__) #endif char timebuf[9]; strftime(timebuf, 9, "%H:%M:%S", now); if (color) LOG("%s %s%-5s\x1b[0m ", timebuf, levels[lvl].color, levels[lvl].name); else LOG("%s %-5s ", timebuf, levels[lvl].name); va_list args; va_start(args, fmt); #ifdef __vita__ // use an immediate buffer for output char outbuffer[1024]; vsprintf(outbuffer, fmt, args); LOG(outbuffer); #else vfprintf(stream, fmt, args); #endif va_end(args); // only include source information if doing debug logs if (level > LL_DEBUG) LOG("\n"); else if (color) LOG(" \x1b[90m(%s:%d)\x1b[0m\n", src, line); else LOG(" (%s:%d)\n", src, line); fflush(stream); } // Log to file, do debug logs by default, higher if wanted if (logfile && (lvl >= LL_DEBUG || level <= lvl)) { char timebuf[20]; strftime(timebuf, 20, "%Y-%m-%d %H:%M:%S", now); fprintf(logfile, "%s %-5s %s:%d: ", timebuf, levels[lvl].name, src, line); va_list args; va_start(args, fmt); vfprintf(logfile, fmt, args); va_end(args); fprintf(logfile, "\n"); fflush(logfile); } } openjazz-20240919/src/io/log.h000066400000000000000000000032471467303304200157660ustar00rootroot00000000000000 /** * * @file log.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _LOG_H #define _LOG_H #include "OpenJazz.h" #include // Loglevels enum { LL_MAX, LL_TRACE, LL_DEBUG, LL_INFO, LL_WARN, LL_ERROR, LL_FATAL }; /* Let the compiler help with parameter formats * (arguments are shifted by 1, because of hidden "this" pointer) */ #ifdef __GNUG__ #define LIKE_PRINTF __attribute__((format(printf, 5, 6))) #else #define LIKE_PRINTF #endif // Class /// Configuration class Log { public: Log(); ~Log(); void setLevel(int level); int getLevel(); void setQuiet(bool enable); void log(int level, const char *file, int line, const char *fmt, ...) LIKE_PRINTF; private: Log(const Log&); // non construction-copyable Log& operator=(const Log&); // non copyable FILE *logfile; int level; bool quiet; bool color_stdout, color_stderr; }; // Variable EXTERN Log logger; // Helper macros #define LOG_MAX(...) logger.log(LL_MAX, __FILE__, __LINE__, __VA_ARGS__) #define LOG_TRACE(...) logger.log(LL_TRACE, __FILE__, __LINE__, __VA_ARGS__) #define LOG_DEBUG(...) logger.log(LL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) #define LOG_INFO(...) logger.log(LL_INFO, __FILE__, __LINE__, __VA_ARGS__) #define LOG_WARN(...) logger.log(LL_WARN, __FILE__, __LINE__, __VA_ARGS__) #define LOG_ERROR(...) logger.log(LL_ERROR, __FILE__, __LINE__, __VA_ARGS__) #define LOG_FATAL(...) logger.log(LL_FATAL, __FILE__, __LINE__, __VA_ARGS__) #undef LIKE_PRINTF #endif openjazz-20240919/src/io/network.cpp000066400000000000000000000201251467303304200172230ustar00rootroot00000000000000 /** * * @file network.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c and menu.c * - 3rd of February 2009: Renamed level.c to level.cpp and menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 3rd June 2009: Created network.cpp from parts of game.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with a platform-specific networking API. * * On most platforms, USE_SOCKETS should be defined. * */ #include "controls.h" #include "gfx/font.h" #include "gfx/video.h" #include "network.h" #include "loop.h" #include "util.h" #include "io/log.h" #ifdef USE_SOCKETS #ifdef _WIN32 #include #define ioctl ioctlsocket #define socklen_t int #ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK #endif #else #include #include #include #include #include #include #include #include #include #endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #ifdef _3DS #include <3ds.h> #include #include static u32* socBuffer = NULL; #define SOC_BUFFERSIZE 0x100000 // maybe 0x80000 is enough #endif #elif defined(__wii__) #include #elif defined(USE_SDL_NET) #include #endif /** * Initialise networking. */ Network::Network () { #ifdef USE_SOCKETS #ifdef _WIN32 WSADATA WSAData; // Start Windows Sockets WSAStartup(MAKEWORD(1, 0), &WSAData); #elif defined(_3DS) socBuffer = static_cast(memalign(0x1000, SOC_BUFFERSIZE)); socInit(socBuffer, SOC_BUFFERSIZE); #endif #elif defined USE_SDL_NET # ifdef __wii__ char ip[16]; // Initialize Wii networking (using dhcp) if_config(ip, NULL, NULL, true, 20); # endif SDLNet_Init(); #endif } /** * De-initialise networking. */ Network::~Network () { #ifdef USE_SOCKETS #ifdef _WIN32 // Shut down Windows Sockets WSACleanup(); #elif defined(_3DS) socExit(); free(socBuffer); #endif #elif defined USE_SDL_NET SDLNet_Quit(); # ifdef __wii__ net_deinit(); # endif #endif } /** * Open a host connection. * * @return Connection socket or error code */ int Network::host () { #ifdef USE_SOCKETS sockaddr_in sockAddr; int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) return E_N_SOCKET; // Make the socket non-blocking #ifdef _3DS fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); #else int nonblock = 1; ioctl(sock, FIONBIO, (u_long *)&nonblock); #endif memset(&sockAddr, 0, sizeof(sockaddr_in)); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = INADDR_ANY; sockAddr.sin_port = htons(NET_PORT); if (bind(sock, (sockaddr *)&sockAddr, sizeof(sockaddr_in))) { close(sock); return E_N_BIND; } if (listen(sock, MAX_CLIENTS) == -1) { close(sock); return E_N_LISTEN; } return sock; #elif defined USE_SDL_NET ipAddress.port = NET_PORT; ipAddress.host = 0; socket = SDLNet_TCP_Open(&ipAddress); if (socket == NULL) return E_N_SOCKET; return (int)socket; #else return E_N_OTHER; #endif } /** * Open a client connection to the specified server. * * @param address Address of the server * * @return Connection socket or error code */ int Network::join (char *address) { #ifdef USE_SOCKETS sockaddr_in sockAddr; fd_set writefds; timeval timeouttv; unsigned int timeout; int sock, con; // Create socket sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) return E_N_SOCKET; // Make socket non-blocking #ifdef _3DS fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); #else con = 1; ioctl(sock, FIONBIO, (u_long *)&con); #endif // Connect to server memset(&sockAddr, 0, sizeof(sockaddr_in)); sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(NET_PORT); #ifdef _WIN32 sockAddr.sin_addr.s_addr = inet_addr(address); #else if (inet_aton(address, &(sockAddr.sin_addr)) == 0) return E_N_ADDRESS; #endif // Initiate connection con = connect(sock, (sockaddr *)&sockAddr, sizeof(sockAddr)); // If the connection completed, return if (con == 0) return sock; // Wait for connection to complete con = 0; timeout = globalTicks + T_TIMEOUT; while (!con) { if (loop(NORMAL_LOOP) == E_QUIT) { close(sock); return E_QUIT; } if (controls.release(C_ESCAPE)) { close(sock); return E_RETURN; } video.clearScreen(0); fontmn2->showString("CONNECTING TO SERVER", canvasW >> 2, (canvasH >> 1) - 16); FD_ZERO(&writefds); FD_SET(sock, &writefds); timeouttv.tv_sec = 0; timeouttv.tv_usec = T_MENU_FRAME; con = select(sock + 1, NULL, &writefds, NULL, &timeouttv); if (con == -1) { LOG_WARN("Could not connect to server - code: %d", getError()); close(sock); return E_N_CONNECT; } if (globalTicks > timeout) { close(sock); return E_TIMEOUT; } } return sock; #elif defined USE_SDL_NET video.clearScreen(0); fontmn2->showString("CONNECTING TO SERVER", canvasW >> 2, (canvasH >> 1) - 16); loop(NORMAL_LOOP); ipAddress.port = NET_PORT; ipAddress.host = inet_addr(address); socket = SDLNet_TCP_Open(&ipAddress); if (socket == NULL) return -1; return (int)socket; #else return E_N_OTHER; #endif } /** * Accept a connection to a client * * @param sock The host connection socket * * @return Client connection socket, or -1 for no connection */ int Network::accept (int sock) { #ifdef USE_SOCKETS sockaddr_in sockAddr; int clientSocket, length; length = sizeof(sockaddr_in); clientSocket = ::accept(sock, (sockaddr *)&sockAddr, (socklen_t *)&length); if (clientSocket != -1) { // Make the socket non-blocking #ifdef _3DS fcntl(clientSocket, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); #else length = 1; ioctl(clientSocket, FIONBIO, (u_long *)&length); #endif } return clientSocket; #elif defined USE_SDL_NET clientSocket = SDLNet_TCP_Accept((TCPsocket)sock); if (clientSocket == NULL) return -1; return (int)&clientSocket; #else return -1; #endif } /** * Close a connection. * * @param sock The connection socket */ void Network::close (int sock) { #ifdef USE_SOCKETS #ifdef _WIN32 closesocket(sock); #else ::close(sock); #endif #elif defined USE_SDL_NET SDLNet_TCP_Close((TCPsocket)sock); #endif } /** * Send data over the specified connection. * * @param sock Connection socket * @param buffer Data to be sent * * @return Number of bytes sent, or -1 for failure */ int Network::send (int sock, unsigned char *buffer) { #ifdef USE_SOCKETS return ::send(sock, reinterpret_cast(buffer), buffer[0], MSG_NOSIGNAL); #elif defined USE_SDL_NET return SDLNet_TCP_Send((TCPsocket)sock, reinterpret_cast(buffer), buffer[0]); #else return 0; #endif } /** * Receive data from the specified connection. * * @param sock Connection socket * @param buffer Buffer to receive data * @param length The size of the buffer, in bytes * * @return Number of bytes received, or -1 for failure */ int Network::recv (int sock, unsigned char *buffer, int length) { #ifdef USE_SOCKETS return ::recv(sock, reinterpret_cast(buffer), length, MSG_NOSIGNAL); #elif defined USE_SDL_NET return SDLNet_TCP_Recv((TCPsocket)sock, buffer, length); #else return 0; #endif } /** * Check if a given socket is connected. * * @param sock The socket * * @return True if connected */ bool Network::isConnected (int sock) { #ifdef USE_SOCKETS int length; char buffer; // Check for incoming data length = ::recv(sock, &buffer, 1, MSG_PEEK | MSG_NOSIGNAL); // Still connected if data was received or if there was no data to receive return (length != -1) || (getError() == EWOULDBLOCK); #elif defined USE_SDL_NET return SDLNet_SocketReady((TCPsocket)sock); #else return false; #endif } /** * Get the last error code. * * @return Network library error code */ int Network::getError () { #ifdef USE_SOCKETS #ifdef _WIN32 return WSAGetLastError(); #else return errno; #endif #elif defined USE_SDL_NET return (int)SDLNet_GetError(); #else return 0; #endif } openjazz-20240919/src/io/network.h000066400000000000000000000025341467303304200166740ustar00rootroot00000000000000 /** * * @file network.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 3rd June 2009: Created network.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _NETWORK_H #define _NETWORK_H #include "OpenJazz.h" #ifdef USE_SDL_NET #include #endif // Constants // Defaults #if defined(DINGOO) #define NET_ADDRESS "10.1.0.1" #else #define NET_ADDRESS "192.168.0.1" #endif #define NET_PORT 10052 // Timeout interval #define T_TIMEOUT 30000 // Client limit #define MAX_CLIENTS 31 // Level file #define LEVEL_FILE "openjazz.tmp" // Class /// Networking class Network { public: #ifdef USE_SDL_NET TCPsocket socket; TCPsocket clientSocket; IPaddress ipAddress; SDLNet_SocketSet socketset; #endif Network (); ~Network (); int host (); int join (char *address); int accept (int sock); void close (int sock); int send (int sock, unsigned char *buffer); int recv (int sock, unsigned char *buffer, int length); bool isConnected (int sock); int getError (); }; // Variables EXTERN char *netAddress; /// Server address EXTERN Network *net; #endif openjazz-20240919/src/io/sound.cpp000066400000000000000000000322211467303304200166620ustar00rootroot00000000000000 /** * * @file sound.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created sound.c * - 3rd February 2009: Renamed sound.c to sound.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading, playing and freeing of music and sound effects. * */ #include "file.h" #include "sound.h" #include "util.h" #include "io/log.h" #include #include #include #if SDL_VERSION_ATLEAST(2, 0, 0) #define OJ_SDL2 1 #else #define OJ_SDL2 0 #endif #if defined(__SYMBIAN32__) || defined(_3DS) || defined(PSP) || defined(__vita__) #define SOUND_FREQ 22050 #else #define SOUND_FREQ 44100 #endif #if defined(GP2X) || defined(PSP) || defined(_3DS) || defined(__vita__) #define SOUND_SAMPLES 512 #else #define SOUND_SAMPLES 2048 #endif #ifdef __SYMBIAN32__ #define MUSIC_RESAMPLEMODE MODPLUG_RESAMPLE_LINEAR #define MUSIC_FLAGS MODPLUG_ENABLE_MEGABASS #elif defined(CAANOO) || defined(WIZ) || defined(GP2X) || defined(DINGOO) || defined(PSP) #define MUSIC_RESAMPLEMODE MODPLUG_RESAMPLE_LINEAR #define MUSIC_FLAGS 0 #else #define MUSIC_RESAMPLEMODE MODPLUG_RESAMPLE_FIR #define MUSIC_FLAGS MODPLUG_ENABLE_NOISE_REDUCTION | MODPLUG_ENABLE_REVERB | MODPLUG_ENABLE_MEGABASS | MODPLUG_ENABLE_SURROUND #endif #define clamp_vol(vol, min, max) (((vol) < (min)) ? (min) : (((vol) > (max)) ? (max) : (vol))) // Datatype /// Raw sound effect data typedef struct { unsigned char *data; char *name; int length; } RawSound; /// Resampled sound effect data typedef struct { unsigned char *data; int length; int position; } Sound; // Variables static RawSound *rawSounds; static int nRawSounds; static Sound sounds[SE::MAX] = {}; static bool soundsLoaded = false; static ModPlugFile *musicFile = nullptr; static SDL_AudioSpec audioSpec = {}; static bool musicPaused = false; static int musicVolume = MAX_VOLUME >> 1; // 50% static int soundVolume = MAX_VOLUME >> 2; // 25% static char *currentMusic = nullptr; static MusicTempo musicTempo = MusicTempo::NORMAL; #if OJ_SDL2 static SDL_AudioDeviceID audioDevice = 0; #endif // Helpers static void LockAudio() { #if OJ_SDL2 SDL_LockAudioDevice(audioDevice); #else SDL_LockAudio(); #endif } static void UnlockAudio() { #if OJ_SDL2 SDL_UnlockAudioDevice(audioDevice); #else SDL_UnlockAudio(); #endif } #if !OJ_SDL2 int SDL_AUDIO_BITSIZE(int format) { if (format == AUDIO_U8 || audioSpec.format == AUDIO_S8) return 8; else if (format == AUDIO_S16MSB || audioSpec.format == AUDIO_S16LSB || format == AUDIO_U16MSB || audioSpec.format == AUDIO_U16LSB) return 16; LOG_ERROR("Unsupported Audio format."); return 0; } #endif /** * Callback used to provide data to the audio subsystem. * * @param userdata N/A * @param stream Output stream * @param len Length of data to be placed in the output stream */ void audioCallback (void * /*userdata*/, unsigned char * stream, int len) { // Clear audio buffer memset(stream, '\0', len * sizeof(unsigned char)); if (musicFile && !musicPaused) { // Read the next portion of music into the stream ModPlug_Read(musicFile, stream, len); } if (!soundsLoaded) return; for (int i = SE::NONE; i < SE::MAX; i++) { if (!sounds[i].data || sounds[i].position < 0) continue; int rest = sounds[i].length - sounds[i].position; int length = 0; int position = sounds[i].position; if (len < rest) { // Play as much of the clip as possible length = len; sounds[i].position += len; } else { // Play the remainder of the clip length = rest; sounds[i].position = -1; } // Add the next portion of the sound clip to the audio stream #if OJ_SDL2 SDL_MixAudioFormat(stream, sounds[i].data + position, audioSpec.format, length, soundVolume * SDL_MIX_MAXVOLUME / MAX_VOLUME); #else SDL_MixAudio(stream, sounds[i].data + position, length, soundVolume * SDL_MIX_MAXVOLUME / MAX_VOLUME); #endif } } /** * Initialise audio. */ void openAudio () { // Set up SDL audio SDL_AudioSpec asDesired = {}; asDesired.freq = SOUND_FREQ; asDesired.format = AUDIO_S16; asDesired.channels = 2; asDesired.samples = SOUND_SAMPLES; asDesired.callback = audioCallback; asDesired.userdata = nullptr; bool audioOk = false; #if OJ_SDL2 audioDevice = SDL_OpenAudioDevice(nullptr, 0, &asDesired, &audioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE); if(!audioDevice || SDL_AUDIO_ISFLOAT(audioSpec.format) || (SDL_AUDIO_BITSIZE(audioSpec.format) != 8 && SDL_AUDIO_BITSIZE(audioSpec.format) != 16)) { LOG_DEBUG("SDL audio format unsupported, letting SDL convert it."); if(audioDevice) SDL_CloseAudioDevice(audioDevice); audioDevice = SDL_OpenAudioDevice(nullptr, 0, &asDesired, &audioSpec, 0); } audioOk = (audioDevice != 0); #else audioOk = (SDL_OpenAudio(&asDesired, &audioSpec) == 0); #endif if(!audioOk) { LOG_ERROR("Unable to open audio: %s", SDL_GetError()); return; } LOG_DEBUG("Opened %dHz Audio at %d bit, %d channels with %d samples", audioSpec.freq, SDL_AUDIO_BITSIZE(audioSpec.format), audioSpec.channels, audioSpec.samples); // Load sounds soundsLoaded = loadSounds("SOUNDS.000"); // Start audio for sfx to work #if OJ_SDL2 SDL_PauseAudioDevice(audioDevice, 0); #else SDL_PauseAudio(0); #endif } /** * Stop audio. */ void closeAudio () { stopMusic(); #if OJ_SDL2 SDL_CloseAudioDevice(audioDevice); audioDevice = 0; #else SDL_CloseAudio(); #endif if (rawSounds) { for (int i = 0; i < nRawSounds; i++) { delete[] rawSounds[i].data; delete[] rawSounds[i].name; } delete[] rawSounds; } if (soundsLoaded) freeSounds(); } /** * Play music from the specified file. * * @param fileName Name of a file containing music data. * @param restart Restart music when same file is played. */ void playMusic (const char * fileName, bool restart) { /* Only stop any existing music playing, if a different file should be played or a restart has been requested. */ if ((currentMusic && (strcmp(fileName, currentMusic) == 0)) && !restart) return; stopMusic(); LockAudio(); // Load the music file File *file; try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { UnlockAudio(); return; } // Save current music filename if (currentMusic) delete[] currentMusic; currentMusic = createString(fileName); // Find the size of the file int size = file->getSize(); // Read the entire file into memory file->seek(0, true); unsigned char *psmData = file->loadBlock(size); delete file; // Set up libpsmplug ModPlug_Settings settings = {}; settings.mFlags = MUSIC_FLAGS; settings.mChannels = audioSpec.channels; settings.mBits = SDL_AUDIO_BITSIZE(audioSpec.format); settings.mFrequency = audioSpec.freq; settings.mResamplingMode = MUSIC_RESAMPLEMODE; settings.mReverbDepth = 25; settings.mReverbDelay = 40; settings.mBassAmount = 50; settings.mBassRange = 10; settings.mSurroundDepth = 50; settings.mSurroundDelay = 40; // unlimited looping settings.mLoopCount = -1; ModPlug_SetSettings(&settings); // Load the file into libmodplug musicFile = ModPlug_Load(psmData, size); delete[] psmData; if (!musicFile) { LOG_ERROR("Could not play music file: %s", fileName); delete[] currentMusic; currentMusic = nullptr; } // Re-apply volume setting setMusicVolume(musicVolume); // Start the audio playing musicPaused = false; UnlockAudio(); } /** * Pauses and Unpauses the current music. * * @param pause set to true to pause */ void pauseMusic (bool pause) { musicPaused = pause; } /** * Stop the current music. */ void stopMusic () { // Stop the music playing LockAudio(); if (musicFile) { ModPlug_Unload(musicFile); musicFile = nullptr; } // Cleanup if (currentMusic) { delete[] currentMusic; currentMusic = nullptr; } UnlockAudio(); } /** * Gets the current music volume * * @return music volume (0-100) */ int getMusicVolume () { return musicVolume; } /** * Sets the music volume * * @param volume new volume (0-100) */ void setMusicVolume (int volume) { musicVolume = clamp_vol(volume, 0, MAX_VOLUME); // do not access music player settings when not playing if (!musicFile) return; ModPlug_SetMasterVolume(musicFile, musicVolume * 2.56); } /** * Gets the current music tempo * * @return music tempo (MUSIC_NORMAL, MUSIC_FAST) */ MusicTempo getMusicTempo () { return musicTempo; } /** * Sets the music tempo * * @param tempo new tempo (MUSIC_NORMAL, MUSIC_FAST) */ void setMusicTempo (MusicTempo tempo) { musicTempo = tempo; // do not access music player settings when not playing if (!musicFile) return; ModPlug_SetMusicTempoFactor(musicFile, static_cast(tempo)); } /** * Load raw sound clips from the specified file. * * @param fileName Name of a file containing sound clips */ bool loadSounds (const char *fileName) { File *file; try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { return false; } // Locate the header data file->seek(file->getSize() - 4, true); int headerOffset = file->loadInt(); // Calculate number of sounds nRawSounds = (file->getSize() - headerOffset) / 18; LOG_TRACE("Loading %d sounds...", nRawSounds); assert(nRawSounds < SE::MAX); // Load sound clips rawSounds = new RawSound[nRawSounds]; for (int i = 0; i < nRawSounds; i++) { file->seek(headerOffset + (i * 18), true); // Read the name of the clip rawSounds[i].name = reinterpret_cast(file->loadBlock(12)); // Read the offset of the clip int offset = file->loadInt(); // Read the length of the clip rawSounds[i].length = file->loadShort(); // Read the clip file->seek(offset, true); rawSounds[i].data = file->loadBlock(rawSounds[i].length); } delete file; resampleSounds(); return true; } /** * Resample sound clip data. */ void resampleSound (int index, const char* name, int rate) { // Skip SE::NONE int se = index + 1; if(!isValidSoundIndex(static_cast(se))) { LOG_ERROR("Cannot resample Sound Index %d", se); return; } if (sounds[se].data) { LOG_TRACE("Overwriting Sound index %d: %s", se, name); delete[] sounds[se].data; sounds[se].data = nullptr; } // Search for matching sound for (int i = 0; i < nRawSounds; i++) { if (strcmp(name, rawSounds[i].name) != 0) continue; #if OJ_SDL2 // We let SDL2 resample as needed SDL_AudioCVT cvt; int res = SDL_BuildAudioCVT(&cvt, AUDIO_S8, 1, rate, audioSpec.format, audioSpec.channels, audioSpec.freq); if (res >= 0) { cvt.len = rawSounds[i].length; cvt.buf = new unsigned char[cvt.len * cvt.len_mult]; if(!cvt.buf) { LOG_ERROR("Cannot create conversion buffer."); return; } memcpy(cvt.buf, rawSounds[i].data, cvt.len); sounds[se].length = cvt.len; // only convert, if needed if (res > 0) { if((res = SDL_ConvertAudio(&cvt)) == 0) { // successful sounds[se].length = cvt.len_cvt; } } } if(res < 0) { LOG_WARN("Cannot resample sound effect: %s", SDL_GetError()); return; } // From here it does not matter, if converted or already right samplerate sounds[se].data = new unsigned char[sounds[se].length]; if(!sounds[se].data) { LOG_ERROR("Cannot create buffer for resampled sound effect."); return; } // Copy data over memcpy(sounds[se].data, cvt.buf, sounds[se].length * sizeof(unsigned char)); delete[](cvt.buf); #else // Calculate the resampling factor int rsFactor; if (SDL_AUDIO_BITSIZE(audioSpec.format) == 8) rsFactor = (F2 * audioSpec.freq) / rate; else rsFactor = (F4 * audioSpec.freq) / rate; sounds[se].length = MUL(rawSounds[i].length, rsFactor); // Allocate the buffer for the resampled clip sounds[se].data = new unsigned char[sounds[se].length]; if(!sounds[se].data) { LOG_ERROR("Cannot create buffer for resampled sound effect."); return; } // Resample the clip for (int sample = 0; sample < sounds[se].length; sample++) sounds[se].data[sample] = rawSounds[i].data[DIV(sample, rsFactor)]; #endif sounds[se].position = -1; return; } } /** * Resample all sound clips to matching indices. */ void resampleSounds() { for (int i = 0; i < nRawSounds; i++) { resampleSound(i, rawSounds[i].name, 11025); } } /** * Delete resampled sound clip data. */ void freeSounds() { if (!soundsLoaded) return; for (int i = SE::NONE; i < SE::MAX; i++) { if (sounds[i].data) delete[] sounds[i].data; } } /** * Set the sound clip to be played. * * @param index Number of the sound to play */ void playSound(SE::Type index) { if (!soundsLoaded) return; if (!isValidSoundIndex(index)) { LOG_WARN("Cannot play invalid sound %d", index); return; } sounds[index].position = 0; } /** * Check if a sound clip is playing. * * @param index Number of the sound to check * * @return Whether the sound is playing */ bool isSoundPlaying (SE::Type index) { if (!soundsLoaded || !isValidSoundIndex(index)) return false; return (sounds[index].position > 0); } /** * Gets the current sound effect volume * * @return sound volume (0-100) */ int getSoundVolume () { return soundVolume; } /** * Sets the sound effect volume * * @param volume new volume (0-100) */ void setSoundVolume (int volume) { soundVolume = clamp_vol(volume, 0, MAX_VOLUME); } openjazz-20240919/src/io/sound.h000066400000000000000000000032661467303304200163360ustar00rootroot00000000000000 /** * * @file sound.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 2nd June 2009: Created sound.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2010 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef OJ_SOUND_H #define OJ_SOUND_H #include "OpenJazz.h" // Constants // Sound effects namespace SE { enum Type : int { NONE = 0, INVULN, MACHGUN, BOOM, OW, YUM, FIRE, UPLOOP, ONEUP, PHOTON, WAIT, ORB, JUMPA, GODLIKE, YEAHOO, BIRDY, FLAMER, ELECTR, SPRING, ROCKET, STOP, BLOCK, CUSTOM_22, CUSTOM_23, CUSTOM_24, CUSTOM_25, CUSTOM_26, CUSTOM_27, CUSTOM_28, CUSTOM_29, CUSTOM_30, CUSTOM_31, CUSTOM_32, MAX }; }; #define MAX_VOLUME 100 enum class MusicTempo { NORMAL = 128, FAST = 80 }; // Variables #if defined(WIZ) || defined(GP2X) EXTERN int volume; EXTERN int volume_direction; #endif // Functions void openAudio(); void closeAudio(); void playMusic(const char *fileName, bool restart = false); void pauseMusic(bool pause); void stopMusic(); int getMusicVolume(); void setMusicVolume(int volume); MusicTempo getMusicTempo(); void setMusicTempo(MusicTempo tempo); bool loadSounds(const char *fileName); void resampleSound(int index, const char* name, int rate); void resampleSounds(); void freeSounds(); void playSound(SE::Type se); bool isSoundPlaying(SE::Type se); int getSoundVolume(); void setSoundVolume(int volume); inline void playConfirmSound() { playSound(SE::ORB); } inline bool isValidSoundIndex(SE::Type index) { return (index >= SE::NONE && index < SE::MAX); } #endif openjazz-20240919/src/jj1/000077500000000000000000000000001467303304200151035ustar00rootroot00000000000000openjazz-20240919/src/jj1/bonuslevel/000077500000000000000000000000001467303304200172615ustar00rootroot00000000000000openjazz-20240919/src/jj1/bonuslevel/jj1bonuslevel.cpp000066400000000000000000000326361467303304200225620ustar00rootroot00000000000000 /** * * @file jj1bonuslevel.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created bonus.c * - 3rd February 2009: Renamed bonus.c to bonus.cpp * - 1st August 2012: Renamed bonus.cpp to jj1bonuslevel.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading, running and freeing of bonus levels. * */ #include "jj1bonuslevelplayer.h" #include "jj1bonuslevel.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/paletteeffects.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" #include /** * Load sprites. * * @return Error code */ int JJ1BonusLevel::loadSprites () { File *file; unsigned char* pixels; try { file = new File("BONUS.000", PATH_TYPE_GAME); } catch (int e) { return e; } file->seek(2, true); sprites = file->loadShort(256); spriteSet = new Sprite[sprites]; for (int i = 0; i < sprites; i++) { // Load dimensions int width = file->loadShort(SW); int height = file->loadShort(SH); int pixelsLength = file->loadShort(); int maskLength = file->loadShort(); // Sprites can be either masked or not masked. if (pixelsLength != 0xFFFF) { // Masked width <<= 2; int pos = file->tell() + (pixelsLength << 2) + maskLength; // Read scrambled, masked pixel data pixels = file->loadPixels(width * height, 0); spriteSet[i].setPixels(pixels, width, height, 0); delete[] pixels; file->seek(pos, true); } else if (width) { // Not masked // Read pixel data pixels = file->loadBlock(width * height); spriteSet[i].setPixels(pixels, width, height, 0); delete[] pixels; } else { // Zero-length sprite // Create blank sprite spriteSet[i].clearPixels(); } } delete file; return E_NONE; } /** * Load the tileset. * * @param fileName Name of the file containing the tileset * * @return Error code */ int JJ1BonusLevel::loadTiles (char *fileName) { File *file; unsigned char *pixels; unsigned char *sorted; int count, x, y; direction = 0; try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { return e; } // Load background pixels = file->loadRLE(832 * 20); sorted = new unsigned char[512 * 20]; for (count = 0; count < 20; count++) memcpy(sorted + (count * 512), pixels + (count * 832), 512); background = createSurface(sorted, 512, 20); delete[] sorted; delete[] pixels; // Load palette file->loadPalette(palette); // Load tile graphics pixels = file->loadRLE(1024 * 60); tileSet = createSurface(pixels, 32, 32 * 60); // Create mask for (count = 0; count < 60; count++) { memset(mask[count], 0, 64); for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) { if ((pixels[(count << 10) + (y << 5) + x] & 240) == 192) mask[count][((y << 1) & 56) + ((x >> 2) & 7)] = 1; } } } delete[] pixels; delete file; return E_NONE; } /** * Create a JJ1 bonus level. * * @param owner The current game * @param fileName Name of the file containing the level data. * @param multi Whether or not the level will be multi-player */ JJ1BonusLevel::JJ1BonusLevel (Game* owner, char * fileName, bool multi) : Level(owner) { Anim* pAnims[BPANIMS]; File *file; unsigned char *buffer; char *string, *fileString; int count, x, y; try { font = new Font(true); } catch (int e) { throw; } try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { delete font; throw; } // Load sprites count = loadSprites(); if (count < 0) { delete file; delete font; throw count; } // Load tileset file->seek(90, true); string = file->loadString(); fileString = createFileName(string, 0); x = loadTiles(fileString); delete[] string; delete[] fileString; if (x != E_NONE) throw x; // Load music file->seek(121, true); fileString = file->loadString(); playMusic(fileString); delete[] fileString; // Load animations file->seek(134, true); buffer = file->loadBlock(BANIMS << 6); // Create animation set based on that data for (count = 0; count < BANIMS; count++) { animSet[count].setData(buffer[(count << 6) + 6], buffer[count << 6], buffer[(count << 6) + 1], buffer[(count << 6) + 3], buffer[(count << 6) + 4], buffer[(count << 6) + 2], buffer[(count << 6) + 5]); for (y = 0; y < buffer[(count << 6) + 6]; y++) { // Get frame x = buffer[(count << 6) + 7 + y]; if (x > sprites) x = sprites; // Assign sprite and vertical offset animSet[count].setFrame(y, true); animSet[count].setFrameData(spriteSet + x, buffer[(count << 6) + 26 + y], buffer[(count << 6) + 45 + y]); } } delete[] buffer; // Load tiles file->seek(2694, true); buffer = file->loadRLE(BLW * BLH); for (y = 0; y < BLH; y++) { for (x = 0; x < BLW; x++) { grid[y][x].tile = buffer[x + (y * BLW)]; if (grid[y][x].tile > 59) grid[y][x].tile = 59; } } delete[] buffer; file->skipRLE(); // Mysterious block // Load events buffer = file->loadRLE(BLW * BLH); for (y = 0; y < BLW; y++) { for (x = 0; x < BLH; x++) { grid[y][x].event = buffer[x + (y * BLW)]; } } delete[] buffer; file->seek(178, false); // Set the tick at which the level will end endTime = file->loadShort() * 1000; // Number of gems to collect items = file->loadShort(); // The players' coordinates x = file->loadShort(); y = file->loadShort(); // Generate player's animation set references for (count = 0; count < BPANIMS; count++) pAnims[count] = animSet + count; createLevelPlayers(LT_JJ1BONUS, pAnims, NULL, false, x, y); delete file; // Palette animations // Spinny whirly thing paletteEffects = new RotatePaletteEffect(112, 16, F32, NULL); // Track sides paletteEffects = new RotatePaletteEffect(192, 16, F32, paletteEffects); // Bouncy things paletteEffects = new RotatePaletteEffect(240, 16, F32, paletteEffects); // Adjust panelBigFont to use bonus level palette panelBigFont->mapPalette(0, 32, 15, -16); multiplayer = multi; video.setTitle("Bonus Level"); } /** * Delete the JJ1 bonus level. */ JJ1BonusLevel::~JJ1BonusLevel () { // Restore panelBigFont palette panelBigFont->restorePalette(); SDL_FreeSurface(tileSet); delete[] spriteSet; delete font; video.setTitle(NULL); } /** * Determine whether or not the given point is in the event area of its tile. * * @param x X-coordinate * @param y Y-coordinate * * @return True if in the event area */ bool JJ1BonusLevel::isEvent (fixed x, fixed y) { return ((x & 32767) > F12) && ((x & 32767) < F20) && ((y & 32767) > F12) && ((y & 32767) < F20); } /** * Determine whether or not the given point is solid. * * @param x X-coordinate * @param y Y-coordinate * * @return Solidity */ bool JJ1BonusLevel::checkMask (fixed x, fixed y) { JJ1BonusLevelGridElement *ge; ge = grid[FTOT(y) & 255] + (FTOT(x) & 255); // Hand if ((ge->event == 3) && isEvent(x, y)) return true; // Check the mask in the tile in question return mask[ge->tile][((y >> 9) & 56) + ((x >> 12) & 7)]; } /** * Interpret data received from client/server * * @param buffer Received data */ void JJ1BonusLevel::receive (unsigned char* buffer) { switch (buffer[1]) { case MT_L_PROP: if (buffer[2] == 2) { if (stage == LS_NORMAL) endTime += buffer[3] * 1000; } break; case MT_L_GRID: if (buffer[4] == 0) grid[buffer[3]][buffer[2]].tile = buffer[5]; else if (buffer[4] == 2) grid[buffer[3]][buffer[2]].event = buffer[5]; break; case MT_L_STAGE: stage = LevelStage(buffer[2]); break; } } /** * Level iteration. * * @return Error code */ int JJ1BonusLevel::step () { // Check if time has run out if (ticks > endTime) return LOST; // Apply controls to local player for (int i = 0; i < PCONTROLS; i++) localPlayer->setControl(i, controls.getState(i)); // Process players for (int i = 0; i < nPlayers; i++) { JJ1BonusLevelPlayer* bonusPlayer = players[i].getJJ1BonusLevelPlayer(); fixed playerX = bonusPlayer->getX(); fixed playerY = bonusPlayer->getY(); bonusPlayer->step(ticks, 16, this); if ((bonusPlayer->getZ() < FH) && isEvent(playerX, playerY)) { int gridX = FTOT(playerX) & 255; int gridY = FTOT(playerY) & 255; switch (grid[gridY][gridX].event) { case 1: // Extra time addTimer(60); grid[gridY][gridX].event = 0; break; case 2: // Gem bonusPlayer->addGem(); grid[gridY][gridX].event = 0; if (bonusPlayer->getGems() >= items) { players[i].addLife(); return WON; } break; case 4: // Exit return LOST; default: // Do nothing break; } } } direction = localPlayer->getJJ1BonusLevelPlayer()->getDirection(); return E_NONE; } /** * Draw the level. */ void JJ1BonusLevel::draw () { JJ1BonusLevelPlayer *bonusPlayer; Sprite* sprite; SDL_Rect dst; int x, y; // Draw the background for (x = -(direction & 1023); x < canvasW; x += background->w) { dst.x = x; dst.y = (canvasH >> 1) - 4; SDL_BlitSurface(background, NULL, canvas, &dst); } x = 171; for (y = (canvasH >> 1) - 5; (y >= 0) && (x > 128); y--) drawRect(0, y, canvasW, 1, x--); if (y > 0) drawRect(0, 0, canvasW, y + 1, 128); bonusPlayer = localPlayer->getJJ1BonusLevelPlayer(); // Draw the ground fixed playerX = bonusPlayer->getX(); fixed playerY = bonusPlayer->getY(); fixed playerSin = fSin(direction); fixed playerCos = fCos(direction); if (SDL_MUSTLOCK(canvas)) SDL_LockSurface(canvas); for (y = 1; y <= (canvasH >> 1) - 15; y++) { fixed distance = DIV(ITOF(800), ITOF(92) - (ITOF(y * 84) / ((canvasH >> 1) - 16))); fixed sideX = MUL(distance, playerCos); fixed sideY = MUL(distance, playerSin); fixed fwdX = playerX + MUL(distance - F16, playerSin) - (sideX >> 1); fixed fwdY = playerY - MUL(distance - F16, playerCos) - (sideY >> 1); unsigned char* row = static_cast(canvas->pixels) + (canvas->pitch * (canvasH - y)); for (x = 0; x < canvasW; x++) { fixed nX = ITOF(x) / canvasW; int levelX = FTOI(fwdX + MUL(nX, sideX)); int levelY = FTOI(fwdY + MUL(nX, sideY)); row[x] = static_cast(tileSet->pixels) [(grid[ITOT(levelY) & 255][ITOT(levelX) & 255].tile << 10) + ((levelY & 31) * tileSet->pitch) + (levelX & 31)]; } } if (SDL_MUSTLOCK(canvas)) SDL_UnlockSurface(canvas); // Draw nearby events for (y = -6; y < 6; y++) { fixed sY = TTOF(((direction - FQ) & 512)? y: -y) + F16 - (playerY & 32767); for (x = -6; x < 6; x++) { fixed sX = TTOF((direction & 512)? x: -x) + F16 - (playerX & 32767); fixed divisor = F16 + MUL(sX, playerSin) - MUL(sY, playerCos); if (FTOI(divisor) > 8) { switch (grid[((((direction - FQ) & 512)? y: -y) + FTOT(playerY)) & 255][(((direction & 512)? x: -x) + FTOT(playerX)) & 255].event) { case 0: // No event sprite = NULL; break; case 1: // Extra time sprite = spriteSet + 46; break; case 2: // Gem sprite = spriteSet + 47; break; case 3: // Hand sprite = spriteSet + 48; break; case 4: // Exit sprite = spriteSet + 49; break; case 5: // Bounce sprite = spriteSet + 50; break; default: sprite = spriteSet + 14; break; } if (sprite) { fixed nX = DIV(MUL(sX, playerCos) + MUL(sY, playerSin), divisor); dst.x = FTOI(nX * canvasW) + (canvasW >> 1); dst.y = canvasH >> 1; sprite->drawScaled(dst.x, dst.y, DIV(F64 * canvasW / SW, divisor)); } } } } // Show the player bonusPlayer->draw(ticks); // Show gem count font->showString("*", 0, 0); font->showNumber(bonusPlayer->getGems() / 10, 50, 0); font->showNumber(bonusPlayer->getGems() % 10, 68, 0); font->showString("/", 65, 0); font->showNumber(items, 124, 0); // Show time remaining if (endTime > ticks) x = (endTime - ticks) / 1000; else x = 0; font->showNumber(x / 60, 250, 0); font->showString(":", 247, 0); font->showNumber((x / 10) % 6, 274, 0); font->showNumber(x % 10, 291, 0); } /** * Play the level. * * @return Error code */ int JJ1BonusLevel::play () { bool pmenu, pmessage; int option; unsigned int returnTime; tickOffset = globalTicks; ticks = T_STEP; steps = 0; pmessage = pmenu = false; option = 0; returnTime = 0; video.setPalette(palette); while (true) { int ret = loop(pmenu, option, pmessage); if (ret < 0) return ret; // Check if level has been won if (returnTime && (ticks > returnTime)) { if (localPlayer->getJJ1BonusLevelPlayer()->getGems() >= items) { if (playScene("BONUS.0SC") == E_QUIT) return E_QUIT; return WON; } return LOST; } // Process frame-by-frame activity while ((getTimeChange() >= T_STEP) && (stage == LS_NORMAL)) { ret = step(); steps++; if (ret < 0) return ret; else if (ret) { stage = LS_END; paletteEffects = new WhiteOutPaletteEffect(T_BONUS_END, paletteEffects); returnTime = ticks + T_BONUS_END; } } // Draw the graphics if ((ticks < returnTime) && !paused) direction += (ticks - prevTicks) * T_BONUS_END / (returnTime - ticks); draw(); // If paused, draw "PAUSE" if (pmessage && !pmenu) font->showString("pause", (canvasW >> 1) - 44, 32); // Draw statistics, menu etc. drawOverlay(0, pmenu, option, 0, 31, 16); } return E_NONE; } openjazz-20240919/src/jj1/bonuslevel/jj1bonuslevel.h000066400000000000000000000035161467303304200222220ustar00rootroot00000000000000 /** * * @file jj1bonuslevel.h * * Part of the OpenJazz project * * @par History: * - 3rd February 2009: Created bonus.h * - 1st August 2012: Renamed bonus.h to jj1bonuslevel.h * * @par Licence: * Copyright (c) 2009-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _BONUS_H #define _BONUS_H #include "io/gfx/anim.h" #include "level/level.h" // Constants // General #define BLW 256 /* Bonus level width */ #define BLH 256 /* Bonus level height */ #define BANIMS 32 #define T_BONUS_END 2000 // Datatype /// JJ1 bonus level grid element typedef struct { unsigned char tile; ///< Indexes the tile set unsigned char event; ///< Event type } JJ1BonusLevelGridElement; // Classes class Font; /// JJ1 bonus level class JJ1BonusLevel : public Level { private: SDL_Surface* tileSet; ///< Tile images SDL_Surface* background; ///< Background image Font* font; ///< On-screen message font Sprite* spriteSet; ///< Sprite images Anim animSet[BANIMS]; ///< Animations JJ1BonusLevelGridElement grid[BLH][BLW]; ///< Level grid char mask[60][64]; ///< Tile masks (at most 60 tiles, all with 8 * 8 masks) fixed direction; ///< Player's direction JJ1BonusLevel(const JJ1BonusLevel&); // non construction-copyable JJ1BonusLevel& operator=(const JJ1BonusLevel&); // non copyable int loadSprites (); int loadTiles (char* fileName); bool isEvent (fixed x, fixed y); int step (); void draw (); public: JJ1BonusLevel (Game* owner, char* fileName, bool multi); ~JJ1BonusLevel (); bool checkMask (fixed x, fixed y); void receive (unsigned char* buffer); int play (); }; #endif openjazz-20240919/src/jj1/bonuslevel/jj1bonuslevelplayer.cpp000066400000000000000000000156231467303304200237740ustar00rootroot00000000000000 /** * * @file jj1bonuslevelplayer.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 18th July 2009: Created playerframe.cpp from parts of player.cpp * - 24th June 2010: Created bonusplayer.cpp from parts of player.cpp and * playerframe.cpp * - 1st August 2012: Renamed bonusplayer.cpp to jj1bonuslevelplayer.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with players in bonus levels. * */ #include "jj1bonuslevel.h" #include "jj1bonuslevelplayer.h" #include "game/game.h" #include "io/controls.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "util.h" #include /** * Create a JJ1 bonus level player. * * @param parent The game player corresponding to this level player. * @param newAnims Animations * @param startX Starting position x-coordinate * @param startY Starting position y-coordinate * @param flockSize The number of birds accompanying the player */ JJ1BonusLevelPlayer::JJ1BonusLevelPlayer (Player* parent, Anim **newAnims, unsigned char startX, unsigned char startY, int flockSize) { player = parent; birds = flockSize; memcpy(anims, newAnims, BPANIMS * sizeof(Anim*)); reset(startX, startY); // Create the player's palette for (int i = 0; i < MAX_PALETTE_COLORS; i++) palette[i].r = palette[i].g = palette[i].b = i; /// @todo Custom colours } /** * Delete the JJ1 bonus level player. */ JJ1BonusLevelPlayer::~JJ1BonusLevelPlayer () { } /** * Reset the player's position, energy etc. * * @param startX New x-coordinate * @param startY New y-coordinate */ void JJ1BonusLevelPlayer::reset (int startX, int startY) { x = TTOF(startX) + F16; y = TTOF(startY) + F16; z = 0; dx = 0; dy = 0; dz = 0; direction = FQ; dr = 0; da = 0; gems = 0; animType = PA_WALK; running = false; } /** * Add to the player's gem tally. */ void JJ1BonusLevelPlayer::addGem () { gems++; } /** * Determine the direction the player is facing. * * @return The player's direction */ fixed JJ1BonusLevelPlayer::getDirection () { return direction; } /** * Determine the number of gems the player has collected. * * @return Number of gems collected */ int JJ1BonusLevelPlayer::getGems () { return gems; } /** * Determine the player's Z-coordinate (altitude). * * @return Player's Z-coordinate */ fixed JJ1BonusLevelPlayer::getZ () { return z; } /** * Determine how many birds are accompanying the player. * * @return The number of birds accompanying the player */ int JJ1BonusLevelPlayer::countBirds () { return birds; } /** * Player iteration. * * @param ticks Time * @param msps Ticks per step * @param bonus Bonus level */ void JJ1BonusLevelPlayer::step (unsigned int ticks, int msps, JJ1BonusLevel* bonus) { (void)ticks; fixed cdx, cdy; // Bonus stages use polar coordinates for movement (but not position) if (animType == PA_CRASH) { if (dr < 0) dr += PRA_REBOUND * msps; else { dr = 0; animType = PA_OTHER; } } else { if (player->getControl(C_FIRE)) { running = true; dr = PRS_RUN; } if (player->getControl(C_UP)) { // Walk/run forwards if (dr < 0) dr += PRA_REVERSE * msps; else if (dr < PRS_WALK) dr += PRA_WALK * msps; animType = PA_WALK; } else if (player->getControl(C_DOWN)) { // Walk/run back running = false; if (dr > 0) dr -= PRA_REVERSE * msps; else if (dr > PRS_REVERSE) dr -= PRA_WALK * msps; animType = PA_WALK; } else { // Slow down if (!running && (dr > 0)) { if (dr < PRA_STOP * msps) dr = 0; else dr -= PRA_STOP * msps; } if (dr < 0) { if (dr > -PRA_STOP * msps) dr = 0; else dr += PRA_STOP * msps; } if (dr == 0) running = false; animType = PA_OTHER; } if (player->getControl(C_LEFT)) { if (da > -PAS_TURN) da -= PAA_TURN * msps; animType = PA_LEFT; } else if (player->getControl(C_RIGHT)) { if (da < PAS_TURN) da += PAA_TURN * msps; animType = PA_RIGHT; } else { // Slow down rotation if (da > 0) { if (da < PAA_STOP * msps) da = 0; else da -= PAA_STOP * msps; } if (da < 0) { if (da > -PAA_STOP * msps) da = 0; else da += PAA_STOP * msps; } } if ((z == 0) && player->getControl(C_JUMP)) { // Jump dz = PZS_JUMP; if (dr < PRS_JUMP) dr = PRS_JUMP; } if (dz > PZS_FALL) dz += PZA_GRAVITY * msps; if (z > 0) animType = PA_JUMP; } // Apply trajectory direction += (da * msps) >> 10; cdx = (MUL(fSin(direction), dr) * msps) >> 10; cdy = (MUL(-fCos(direction), dr) * msps) >> 10; if (!bonus->checkMask(x + cdx, y)) x += cdx; if (!bonus->checkMask(x, y + cdy)) y += cdy; z += (dz * msps) >> 10; if (z > F1) z = F1; if (z < 0) z = 0; // React to running collision if (running && bonus->checkMask(x + cdx, y + cdy)) { running = false; dr = PRS_CRASH; da = 0; animType = PA_CRASH; } } /** * Draw the player. * * @param ticks Time */ void JJ1BonusLevelPlayer::draw (unsigned int ticks) { Anim* anim; anim = anims[animType]; anim->setFrame(ticks / 75, true); if (canvasW <= SW) anim->draw(ITOF((canvasW - anim->getWidth()) >> 1), ITOF(canvasH - anim->getHeight() - 16 - FTOI(z * 80))); else anim->drawScaled(ITOF(canvasW >> 1), ITOF(canvasH - ((((anim->getHeight() >> 1) + 16 + FTOI(z * 80)) * canvasW) / SW)), ITOF(canvasW) / SW); } /** * Fill a buffer with player data. * * @param buffer The buffer */ void JJ1BonusLevelPlayer::send (unsigned char *buffer) { // Copy data to be sent to clients/server buffer[9] = birds; buffer[23] = 0; buffer[25] = 0; buffer[26] = 0; buffer[27] = direction >> 2; buffer[29] = 0; buffer[30] = 0; buffer[31] = 0; buffer[32] = 0; buffer[33] = z >> 24; buffer[34] = (z >> 16) & 255; buffer[35] = (z >> 8) & 255; buffer[36] = z & 255; buffer[37] = x >> 24; buffer[38] = (x >> 16) & 255; buffer[39] = (x >> 8) & 255; buffer[40] = x & 255; buffer[41] = y >> 24; buffer[42] = (y >> 16) & 255; buffer[43] = (y >> 8) & 255; buffer[44] = y & 255; } /** * Adjust player data based on the contents of a given buffer. * * @param buffer The buffer */ void JJ1BonusLevelPlayer::receive (unsigned char *buffer) { // Interpret data received from client/server switch (buffer[1]) { case MT_P_TEMP: birds = buffer[9]; direction = buffer[27] << 2; z = (buffer[33] << 24) + (buffer[34] << 16) + (buffer[35] << 8) + buffer[36]; x = (buffer[37] << 24) + (buffer[38] << 16) + (buffer[39] << 8) + buffer[40]; y = (buffer[41] << 24) + (buffer[42] << 16) + (buffer[43] << 8) + buffer[44]; break; } } openjazz-20240919/src/jj1/bonuslevel/jj1bonuslevelplayer.h000066400000000000000000000047641467303304200234450ustar00rootroot00000000000000 /** * * @file jj1bonuslevelplayer.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created player.h from parts of OpenJazz.h * - 24th June 2010: Created bonusplayer.h from parts of player.h * - 1st August 2012: Renamed bonusplayer.h to jj1bonuslevelplayer.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _BONUSPLAYER_H #define _BONUSPLAYER_H #include "level/levelplayer.h" #include "player/player.h" #include "OpenJazz.h" // Constants // Bonus level animations #define PA_WALK 0 #define PA_LEFT 1 #define PA_RIGHT 2 #define PA_USER 3 #define PA_JUMP 4 #define PA_CRASH 5 #define PA_OTHER 6 #define BPANIMS 7 // Player speeds #define PRS_CRASH -ITOF(60) #define PRS_REVERSE -ITOF(30) #define PRS_JUMP ITOF(40) #define PRS_WALK ITOF(40) #define PRS_RUN ITOF(80) #define PAS_TURN 384 #define PZS_FALL -ITOF(2) #define PZS_JUMP ITOF(3) // Player accelerations #define PRA_REBOUND 150 #define PRA_REVERSE 450 #define PRA_STOP 200 #define PRA_WALK 100 #define PAA_STOP 6 #define PAA_TURN 1 #define PZA_GRAVITY -6 // Classes class Anim; class JJ1BonusLevel; /// JJ1 bonus level player class JJ1BonusLevelPlayer final : public LevelPlayer { private: Anim* anims[BPANIMS]; ///< Animations int birds; ///< Number of birds (not present in bonus levels) fixed z; ///< Z-coordinate (altitude) fixed direction; ///< Direction fixed dr; ///< Forward speed fixed da; ///< Angular speed fixed dz; ///< Vertical speed bool running; ///< Rolling/running unsigned char animType; ///< Current animation int gems; ///< Number of gems collected public: JJ1BonusLevelPlayer (Player* parent, Anim** newAnims, unsigned char startX, unsigned char startY, int flockSize); ~JJ1BonusLevelPlayer (); void reset (int startX, int startY) override; void addGem (); int countBirds () override; fixed getDirection (); int getGems (); fixed getZ (); void send (unsigned char* buffer) override; void receive (unsigned char* buffer) override; void step (unsigned int ticks, int msps, JJ1BonusLevel* bonus); void draw (unsigned int ticks); }; #endif openjazz-20240919/src/jj1/level/000077500000000000000000000000001467303304200162125ustar00rootroot00000000000000openjazz-20240919/src/jj1/level/event/000077500000000000000000000000001467303304200173335ustar00rootroot00000000000000openjazz-20240919/src/jj1/level/event/jj1bridge.cpp000066400000000000000000000105241467303304200217020ustar00rootroot00000000000000 /** * * @file jj1bridge.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp * - 19th July 2009: Created eventframe.cpp from parts of events.cpp * - 19th July 2009: Renamed events.cpp to event.cpp * - 2nd March 2010: Created bridge.cpp from parts of event.cpp and eventframe.cpp * - 1st August 2012: Renamed bridge.cpp to jj1bridge.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the functions of bridge events. * */ #include "../jj1level.h" #include "../jj1levelplayer.h" #include "jj1event.h" /** * Create bridge. * * @param gX X-coordinate * @param gY Y-coordinate */ JJ1Bridge::JJ1Bridge (unsigned char gX, unsigned char gY) : JJ1Event(gX, gY) { y = TTOF(gY) + ITOF(set->multiB); setAnimType(E_LEFTANIM); // Bridges should ignore the default yOffsets noAnimOffset = true; // leftDipX and rightDipX used to store leftmost and rightmost player on bridge // Start with minimum values leftDipX = set->multiA * set->pieceSize * F4; rightDipX = 0; } /** * Bridge iteration. * * @param ticks Time * * @return Remaining event */ JJ1Event* JJ1Bridge::step (unsigned int ticks) { set = prepareStep(ticks); if (!set) return remove(false); fixed bridgeLength = set->multiA * set->pieceSize * F4; // Gradually stop the bridge sagging if (leftDipX < bridgeLength) leftDipX += 5120; if (leftDipX > bridgeLength) leftDipX = bridgeLength; if (rightDipX > 0) rightDipX -= 5120; if (rightDipX < 0) rightDipX = 0; for (int i = 0; i < nPlayers; i++) { JJ1LevelPlayer* levelPlayer = players[i].getJJ1LevelPlayer(); fixed playerDipX = levelPlayer->getX() + PXO_MID - x; fixed playerDipY; if (playerDipX < bridgeLength >> 1) playerDipY = playerDipX >> 3; else playerDipY = (bridgeLength - playerDipX) >> 3; if (levelPlayer->overlap(x, y - F8 + playerDipY - F4, bridgeLength, F8) && !level->checkMaskDown(x + playerDipX, y - F8 + playerDipY - F32)) { // Player is on the bridge if (playerDipX < leftDipX) leftDipX = playerDipX; if (playerDipX > rightDipX) rightDipX = playerDipX; levelPlayer->setPlatform(gridX, gridY, 0, y - F8 - F1 + playerDipY); } else levelPlayer->clearEvent(gridX, gridY); } return this; } /** * Draw bridge. * * @param ticks Time * @param change Time since last iteration */ void JJ1Bridge::draw (unsigned int ticks, int change) { int count; if (next) next->draw(ticks, change); // If the event has been removed from the grid, do not show it if (!set) return; // Check if the event has anything to draw if ((animType == E_NOANIM) || ((set->anims[animType] & 0x7F) == 0)) return; unsigned char frame = ticks / (set->animSpeed << 5); setAnimFrame(frame + gridX + gridY, true); // Draw the bridge fixed leftDipY, rightDipY; fixed bridgeLength = set->multiA * set->pieceSize * F4; fixed anchorY = getDrawY(change) - F10 - anim->getOffset(); if (rightDipX >= leftDipX) { leftDipY = (leftDipX <= (bridgeLength >> 1)) ? leftDipX >> 3: (bridgeLength - leftDipX) >> 3; rightDipY = (rightDipX <= (bridgeLength >> 1)) ? rightDipX >> 3: (bridgeLength - rightDipX) >> 3; for (count = 0; count < bridgeLength; count += F4 * set->pieceSize) { if (count < leftDipX) anim->draw(getDrawX(change) + count, anchorY + (count * leftDipY / leftDipX)); else if (count < rightDipX) anim->draw(getDrawX(change) + count, anchorY + leftDipY + ((count - leftDipX) * (rightDipY - leftDipY) / (rightDipX - leftDipX))); else anim->draw(getDrawX(change) + count, anchorY + ((bridgeLength - count) * rightDipY / (bridgeLength - rightDipX))); } } else { // No players on the bridge, de-sagging in progress // Midpoint leftDipY = (leftDipX + rightDipX) >> 1; // Dip rightDipY = (rightDipX < bridgeLength - leftDipX) ? rightDipX >> 3: (bridgeLength - leftDipX) >> 3; for (count = 0; count < bridgeLength; count += F4 * set->pieceSize) { if (count < leftDipY) anim->draw(getDrawX(change) + count, anchorY + (count * rightDipY / leftDipY)); else anim->draw(getDrawX(change) + count, anchorY + ((bridgeLength - count) * rightDipY / (bridgeLength - leftDipY))); } } } openjazz-20240919/src/jj1/level/event/jj1event.cpp000066400000000000000000000153741467303304200215770ustar00rootroot00000000000000 /** * * @file jj1event.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 11th February 2009: Created bullet.cpp from parts of events.cpp * - 1st March 2009: Created bird.cpp from parts of events.cpp * - 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp * - 19th July 2009: Created eventframe.cpp from parts of events.cpp * - 19th July 2009: Renamed events.cpp to event.cpp * - 2nd March 2010: Created guardians.cpp from parts of event.cpp and * eventframe.cpp * - 2nd March 2010: Created bridge.cpp from parts of event.cpp and eventframe.cpp * - 5th February 2011: Moved parts of eventframe.cpp to event.cpp * - 1st August 2012: Renamed event.cpp to jj1event.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with events in ordinary levels. * */ #include "../jj1level.h" #include "jj1event.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" /** * Create event * * @param gX X-coordinate * @param gY Y-coordinate */ JJ1Event::JJ1Event (unsigned char gX, unsigned char gY) { set = level->getEvent(gX, gY); x = TTOF(gX); y = TTOF(gY + 1); dx = 0; dy = 0; next = level->getEvents(); gridX = gX; gridY = gY; flashTime = 0; animType = E_NOANIM; anim = NULL; noAnimOffset = false; drawnX = x; drawnY = y; width = F32; height = F32; } /** * Delete all events */ JJ1Event::~JJ1Event () { if (next) delete next; } /** * Delete this event * * @param permanently Whether or not to delete the event from the level * * @return The next event */ JJ1Event* JJ1Event::remove (bool permanently) { JJ1Event *oldNext; if (permanently) level->clearEvent(gridX, gridY); oldNext = next; next = NULL; delete this; return oldNext; } /** * Get the next event * * @return The next event */ JJ1Event * JJ1Event::getNext () { return next; } /** * Initiate the destruction of the event * * @param ticks Time */ void JJ1Event::destroy (unsigned int ticks) { setAnimType(E_LFINISHANIM | (animType & 1)); level->setEventTime(gridX, gridY, ticks); playSound(set->sound); } /** * Deal with bullet collisions * * @param source Source of the hit(s) * @param hits Number of hits to inflict * @param ticks Current time * * @return Whether or not the hit was successful */ bool JJ1Event::hit (JJ1LevelPlayer *source, int hits, unsigned int ticks) { int hitsRemaining; // Check if event has already been destroyed if (((animType & ~1) == E_LFINISHANIM) || (ticks < flashTime)) return false; hitsRemaining = level->hitEvent(gridX, gridY, hits, source, ticks); // If the event cannot be hit, do not register hit if (hitsRemaining < 0) return false; // Check if the hit has destroyed the event if (hitsRemaining == 0) destroy(ticks); // The event has been hit, so it should flash flashTime = ticks + T_FLASH; // Register hit return true; } /** * Determine whether or not the event is an enemy * * @return Whether or not the event is an enemy */ bool JJ1Event::isEnemy () { return set->strength && (set->modifier == 0); } /** * Determine whether or not the event is from the given position * * @return Whether or not the event is from the given position */ bool JJ1Event::isFrom (unsigned char gX, unsigned char gY) { return (gX == gridX) && (gY == gridY); } /** * Calculate the width and height of the event */ void JJ1Event::calcDimensions () { if (animType == E_NOANIM) { width = F32; height = F32; } else { width = ITOF(anim->getWidth()); height = ITOF(anim->getHeight()); // Blank sprites for e.g. invisible springs if ((width == F1) && (height == F1)) { width = F32; height = F32; } } } /** * Determine whether or not the event is overlapping the given area * * @param areaX The x-coordinate of the left of the area * @param areaY The y-coordinate of the top of the area * @param areaWidth The width of the area * @param areaHeight The height of the area * * @return Whether or not there is an overlap */ bool JJ1Event::overlap (fixed areaX, fixed areaY, fixed areaWidth, fixed areaHeight) { return (drawnX + width >= areaX) && (drawnX < areaX + areaWidth) && (drawnY + height >= areaY) && (drawnY < areaY + areaHeight); } /** * Sets the animation type and updates the current animation and dimensions * * @param type The new animation type */ void JJ1Event::setAnimType(unsigned char type) { if (type == animType) return; animType = type; if (animType == E_NOANIM) anim = NULL; // If there is no shooting animation, use the normal animation instead else if (((animType & ~1) == E_LSHOOTANIM) && (set->anims[animType] == 0)) anim = level->getAnim(set->anims[animType & 1] & 0x7F); else anim = level->getAnim(set->anims[animType] & 0x7F); calcDimensions(); } /** * Sets the animation frame and updates the current dimensions */ void JJ1Event::setAnimFrame (int frame, bool looping) { if (!anim) return; anim->setFrame(frame, looping); calcDimensions(); } /** * Functionality required by all event types on each iteration * * @param ticks Time * * @return Animation */ JJ1EventType* JJ1Event::prepareStep (unsigned int ticks) { // Process the next event if (next) next = next->step(ticks); // If the event has been removed from the grid, destroy it if (!set) return NULL; // If the event and its origin are off-screen, the event is not in the // process of self-destruction, remove it if (((animType & ~1) != E_LFINISHANIM) && ((x < viewX - F192) || (x > viewX + ITOF(canvasW) + F192) || (y < viewY - F160) || (y > viewY + ITOF(canvasH) + F160)) && ((gridX < FTOT(viewX) - 1) || (gridX > ITOT(FTOI(viewX) + canvasW) + 1) || (gridY < FTOT(viewY) - 1) || (gridY > ITOT(FTOI(viewY) + canvasH) + 1))) return NULL; return set; } /** * Draw the event's energy bar * * @param ticks Time */ void JJ1Event::drawEnergy (unsigned int ticks) { Anim* miscAnim; int hits; if (!set || set->modifier != 8) { if (next) next->drawEnergy(ticks); return; } else if (set->strength) { // Draw boss energy bar hits = level->getEventHits(gridX, gridY) * 100 / set->strength; // Devan head miscAnim = level->getMiscAnim(MA_DEVHEAD); miscAnim->setFrame(0, true); if (ticks < flashTime) miscAnim->flashPalette(0); miscAnim->draw(ITOF(canvasW - 44), ITOF(hits + 48)); if (ticks < flashTime) miscAnim->restorePalette(); // Bar drawRect(canvasW - 40, hits + 40, 12, 100 - hits, (ticks < flashTime)? 0: 32); } } openjazz-20240919/src/jj1/level/event/jj1event.h000066400000000000000000000062701467303304200212370ustar00rootroot00000000000000 /** * * @file jj1event.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 4th February 2009: Created events.h from parts of level.h * - 11th February 2009: Created bullet.h from parts of events.h * - 1st March 2009: Created bird.h from parts of events.h * - 19th July 2009: Renamed events.h to event.h * - 2nd March 2010: Created guardians.h from parts of event.h * - 1st August 2012: Renamed event.h to jj1event.h * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _EVENT_H #define _EVENT_H #include "../jj1level.h" #include "io/gfx/anim.h" #include "level/movable.h" #include "OpenJazz.h" // Constants // Animations #define E_LEFTANIM 0 #define E_RIGHTANIM 1 #define E_LFINISHANIM 2 #define E_RFINISHANIM 3 #define E_LSHOOTANIM 4 #define E_RSHOOTANIM 5 #define E_NOANIM 6 // Delays #define T_FLASH 100 // Speed factors #define ES_SLOW ITOF(80) #define ES_FAST ITOF(240) // Classes class Anim; class JJ1LevelPlayer; /// JJ1 level event class JJ1Event : public Movable { private: void calcDimensions (); protected: JJ1Event* next; ///< Next event JJ1EventType* set; ///< Type Anim* anim; ///< Current animation fixed drawnX, drawnY; ///< Current drawing co-ordinates fixed width, height; ///< Current dimensions unsigned char gridX, gridY; ///< Grid position of the event unsigned char animType; ///< Animation type (E_LEFTANIM, etc.) unsigned int flashTime; ///< Time flash will end bool noAnimOffset; JJ1Event (unsigned char gX, unsigned char gY); JJ1Event* remove (bool permanently); void destroy (unsigned int ticks); void setAnimType (unsigned char type); void setAnimFrame (int frame, bool looping); JJ1EventType* prepareStep (unsigned int ticks); public: virtual ~JJ1Event (); JJ1Event* getNext (); bool hit (JJ1LevelPlayer *source, int hits, unsigned int ticks); bool isEnemy (); bool isFrom (unsigned char gX, unsigned char gY); bool overlap (fixed areaX, fixed areaY, fixed areaWidth, fixed areaHeight); virtual JJ1Event* step (unsigned int ticks) = 0; virtual void draw (unsigned int ticks, int change) = 0; void drawEnergy (unsigned int ticks); }; /// Standard JJ1 level event class JJ1StandardEvent : public JJ1Event { private: fixed node; ///< Current event path node bool onlyLAnimOffset; bool onlyRAnimOffset; void move (unsigned int ticks); public: JJ1StandardEvent (JJ1EventType* event, unsigned char gX, unsigned char gY, fixed startX, fixed startY); JJ1Event* step (unsigned int ticks); void draw (unsigned int ticks, int change); }; /// JJ1 level bridge class JJ1Bridge : public JJ1Event { private: fixed leftDipX; fixed rightDipX; public: JJ1Bridge (unsigned char gX, unsigned char gY); JJ1Event* step (unsigned int ticks); void draw (unsigned int ticks, int change); }; #endif openjazz-20240919/src/jj1/level/event/jj1guardians.cpp000066400000000000000000000170141467303304200224240ustar00rootroot00000000000000 /** * * @file jj1guardians.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp * - 19th July 2009: Created eventframe.cpp from parts of events.cpp * - 19th July 2009: Renamed events.cpp to event.cpp * - 2nd March 2010: Created guardians.cpp from parts of event.cpp and eventframe.cpp * - 1st August 2012: Renamed guardians.cpp to jj1guardians.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the functions of guardian events. * */ #include "../jj1bullet.h" #include "../jj1level.h" #include "jj1guardians.h" #include "io/gfx/video.h" #include "util.h" #include /** * Create guardian. * * @param gX X-coordinate * @param gY Y-coordinate */ Guardian::Guardian(unsigned char gX, unsigned char gY) : JJ1Event(gX, gY) { setAnimType(E_LEFTANIM); stage = 0; } /** * Create episode B guardian. * * @param gX X-coordinate * @param gY Y-coordinate */ DeckGuardian::DeckGuardian (unsigned char gX, unsigned char gY) : Guardian(gX, gY) { } /** * Episode B guardian iteration. * * @param ticks Time * * @return Remaining event */ JJ1Event* DeckGuardian::step (unsigned int ticks) { set = prepareStep(ticks); if (!set) return remove(false); int count = level->getEventHits(gridX, gridY); if (count < 8) stage = 0; else if (count < 16) stage = 1; else if (count < 24) stage = 2; else stage = 3; // If the event has been destroyed, play its finishing animation and set its // reaction time if (set->strength && (level->getEventHits(gridX, gridY) >= set->strength) && ((animType & ~1) != E_LFINISHANIM)) { destroy(ticks); } // If the reaction time has expired if (level->getEventTime(gridX, gridY) && (ticks > level->getEventTime(gridX, gridY))) { if ((animType & ~1) != E_LFINISHANIM) { level->setEventTime(gridX, gridY, 0); } } return this; } /** * Draw episode B guardian. * * @param ticks Time * @param change Time since last iteration */ void DeckGuardian::draw (unsigned int ticks, int change) { if (next) next->draw(ticks, change); // If the event has been removed from the grid, do not show it if (!set) return; // Draw the boss if (stage < 3) { // Draw unit Anim* unitAnim = level->getAnim(29 + stage); if (stage == 0) { width = F8; drawnX = x - F64; } else if (stage == 1) { width = F8; drawnX = x + F32 - F8; } else if (stage == 2) { width = F64 + F32; drawnX = x - F64; } drawnY = y + F32; height = F32; if (ticks < flashTime) unitAnim->flashPalette(0); if (stage == 0) unitAnim->draw(getDrawX(change) - F64, getDrawY(change) + F32); else if (stage == 1) unitAnim->draw(getDrawX(change) + F32 - F8 - F4, getDrawY(change) + F32); else unitAnim->draw(getDrawX(change) + F8 - F64, getDrawY(change) + F32); if (ticks < flashTime) unitAnim->restorePalette(); } } /** * Create episode 1 guardian. * * @param gX X-coordinate * @param gY Y-coordinate */ MedGuardian::MedGuardian(unsigned char gX, unsigned char gY) : Guardian(gX, gY) { direction = 1; shoot = false; } /** * Episode 1 guardian iteration. * * @param ticks Time * * @return Remaining event */ JJ1Event* MedGuardian::step(unsigned int ticks) { fixed sin = fSin(ticks / 2); fixed cos = fCos(ticks / 2); set = prepareStep(ticks); if (!set) return remove(false); if (level->getEventHits(gridX, gridY) >= set->strength / 2) stage = 1; if (level->getEventHits(gridX, gridY) >= set->strength) stage = 2; // Stage 0: Move in an eight shape and fire the occasional shot if (stage == 0) { if (direction == 1) { // Lower right part of the eight setAnimType(E_LEFTANIM); dx = TTOF(gridX) + (sin * 96) - x + ITOF(96); dy = TTOF(gridY) - (cos * 64) - y; if (cos > 0) direction = 2; } if (direction == 2) { // Upper left part of the eight setAnimType(E_LEFTANIM); dx = TTOF(gridX) - (sin * 96) - x - ITOF(96); dy = TTOF(gridY) - (cos * 64) - y; if (cos < 0) direction = 3; } if (direction == 3) { // Lower left part of the eight setAnimType(E_RIGHTANIM); dx = TTOF(gridX) - (sin * 96) - x - ITOF(96); dy = TTOF(gridY) - (cos * 64) - y; if (cos > 0) direction = 4; } if (direction == 4) { // Upper right part of the eight setAnimType(E_RIGHTANIM); dx = TTOF(gridX) + (sin * 96) - x + ITOF(96); dy = TTOF(gridY) - (cos * 64) - y; if (cos < 0) direction = 1; } // Decide if there should be a shot if ((ticks % (set->bulletPeriod * 25) > (unsigned int)(set->bulletPeriod * 25) - 300)) { level->setEventTime(gridX, gridY, ticks + 300); shoot = true; } // Shoot if there is a shot if (level->getEventTime(gridX, gridY) && (ticks > level->getEventTime(gridX, gridY)) && shoot) { if (set->bullet < 32) level->createBullet(NULL, gridX, gridY, x + anim->getAccessoryShootX(), y + anim->getAccessoryShootY(), set->bullet, (animType != E_LEFTANIM), ticks); shoot = false; } } // Stage 1: Hop back and forth destroying the bottom row of tiles if (stage == 1) { fixed startPos = TTOF(gridY) + ITOF(40); if (direction < 5) { // Move up or down towards the starting position for hopping direction = (y > startPos) ? 5 : 6; } // Move up to the correct height if (direction == 5) { if (y > startPos) { dx = 0; dy = -ITOF(2); } else direction = 7; } // Move down to the correct height if (direction == 6) { if (y < startPos) { dx = 0; dy = ITOF(2); } else direction = 7; } // Cosinus should be near zero before we start hopping. if (direction == 7) { dx = 0; dy = 0; if (cos > -100 && cos < 100) direction = 8; } // Start hopping if (direction == 8) { if (level->checkMaskUp(x, y) || level->checkMaskUp(x + width, y)) setAnimType((animType == E_LEFTANIM) ? E_RIGHTANIM : E_LEFTANIM); dy = startPos - abs(cos * 96) - y; dx = abs(cos * 6); if (animType == E_LEFTANIM) dx *= -1; if (cos < 0 && level->checkMaskDown(x + ITOF(anim->getWidth() / 2), y + TTOF(1))) direction = 9; } // Destroy the block underneath if (direction == 9) { // Shake a bit dx = (FTOI(x) % 2) ? ITOF(1) : -ITOF(1); dy = 0; // Remove the tile if (cos > 0 && cos < 100) { level->setTile( FTOT(x + ITOF((anim->getWidth() / 2))), FTOT(y) + 1, set->magnitude); direction = 8; } } } // Stage 2: End of behavior if (stage == 2) { dx = 0; dy = ITOF(4); } x += dx; y += dy; dx = dx << 6; dy = dy << 6; return this; } /** * Draw episode 1 guardian. * * @param ticks Time * @param change Time since last iteration */ void MedGuardian::draw(unsigned int ticks, int change) { Anim *stageAnim; unsigned char frame; if (next) next->draw(ticks, change); fixed xChange = getDrawX(change); fixed yChange = getDrawY(change); frame = ticks / (set->animSpeed << 5); if (stage == 0) stageAnim = anim; else stageAnim = level->getAnim(set->anims[E_LFINISHANIM | (animType & 1)] & 0x7F); stageAnim->setFrame(frame + gridX + gridY, true); if (ticks < flashTime) stageAnim->flashPalette(0); drawnX = x + anim->getXOffset(); drawnY = y + anim->getYOffset() + stageAnim->getOffset(); stageAnim->draw(xChange, yChange); if (ticks < flashTime) stageAnim->restorePalette(); } openjazz-20240919/src/jj1/level/event/jj1guardians.h000066400000000000000000000027051467303304200220720ustar00rootroot00000000000000 /** * * @file jj1guardians.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 4th February 2009: Created events.h from parts of level.h * - 19th July 2009: Renamed events.h to event.h * - 2nd March 2010: Created guardians.h from parts of event.h * - 1st August 2012: Renamed guardians.h to jj1guardians.h * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _GUARDIANS_H #define _GUARDIANS_H #include "jj1event.h" // Class /// Guardian event base class class Guardian : public JJ1Event { protected: int stage; Guardian (unsigned char gX, unsigned char gY); }; /// Episode B guardian class DeckGuardian : public Guardian { public: DeckGuardian (unsigned char gX, unsigned char gY); bool overlap (fixed left, fixed top, fixed width, fixed height); JJ1Event* step (unsigned int ticks); void draw (unsigned int ticks, int change); }; /// Episode 1 guardian class MedGuardian : public Guardian { private: unsigned char direction; bool shoot; public: MedGuardian (unsigned char gX, unsigned char gY); //bool overlap (fixed left, fixed top, fixed width, fixed height); JJ1Event* step (unsigned int ticks); void draw (unsigned int ticks, int change); }; #endif openjazz-20240919/src/jj1/level/event/jj1standardevent.cpp000066400000000000000000000512461467303304200233160ustar00rootroot00000000000000 /** * * @file jj1standardevent.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp * - 19th July 2009: Created eventframe.cpp from parts of events.cpp * - 2nd March 2010: Created guardians.cpp from parts of event.cpp and eventframe.cpp * - 2nd March 2010: Created bridge.cpp from parts of event.cpp and eventframe.cpp * - 5th February 2011: Moved parts of eventframe.cpp to event.cpp * - 5th February 2011: Renamed eventframe.cpp to standardevent.cpp * - 1st August 2012: Renamed standardevent.cpp to jj1standardevent.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the once-per-frame functions of ordinary events. * */ #include "../jj1bullet.h" #include "../jj1level.h" #include "../jj1levelplayer.h" #include "jj1event.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" #include /** * Create standard event. * * @param event The event type * @param gX The grid X-coordinate of the origin of the event * @param gY The grid Y-coordinate of the origin of the event * @param startX The initial X-coordinate of the event * @param startY The initial Y-coordinate of the event */ JJ1StandardEvent::JJ1StandardEvent (JJ1EventType* event, unsigned char gX, unsigned char gY, fixed startX, fixed startY) : JJ1Event(gX, gY) { set = event; x = startX; y = startY; node = 0; onlyLAnimOffset = false; onlyRAnimOffset = false; switch (set->movement) { case 2: // Walk from side to side case 4: // Walk from side to side and down hills setAnimType(E_LEFTANIM); onlyLAnimOffset = true; break; case 6: // Use the path from the level file case 7: // Flying snake behavior setAnimType(E_LEFTANIM); noAnimOffset = true; break; case 21: // Destructible block case 25: // Float up / Belt case 37: // Sucker tubes case 38: // Sucker tubes case 40: // Monochrome case 42: // Reflection case 45: // Semitransparency case 57: // Bubbles animType = E_LEFTANIM; setAnimType(E_NOANIM); break; case 26: // Flip animation setAnimType(E_RIGHTANIM); onlyRAnimOffset = true; break; default: setAnimType(E_LEFTANIM); break; } } /** * Move standard event. * * @param ticks Time */ void JJ1StandardEvent::move (unsigned int ticks) { JJ1LevelPlayer* levelPlayer; int length; fixed angle; if ((animType & ~1) == E_LSHOOTANIM) { dx = 0; dy = 0; return; } levelPlayer = localPlayer->getJJ1LevelPlayer(); // Handle movement switch (set->movement) { case 1: // Sink down dy = ES_FAST; break; case 2: // Walk from side to side if (animType == E_LEFTANIM) dx = -ES_FAST; else if (animType == E_RIGHTANIM) dx = ES_FAST; else dx = 0; break; case 3: // Seek jazz if (levelPlayer->getX() + PXO_R < x) dx = -ES_FAST; else if (levelPlayer->getX() + PXO_L > x + width) dx = ES_FAST; else dx = 0; break; case 4: // Walk from side to side and down hills if (!level->checkMaskDown(x + (width >> 1), y)) { // Fall downwards dx = 0; dy = ES_FAST; } else { // Walk from side to side if (animType == E_LEFTANIM) dx = -ES_FAST; else if (animType == E_RIGHTANIM) dx = ES_FAST; dy = 0; } break; case 5: /// @todo Find out what behaviour 5 is break; case 6: case 7: node = (node + FH) % ITOF(level->path[set->multiA].length); // Use the path from the level file dx = TTOF(gridX) + ITOF(level->path[set->multiA].x[FTOI(node)]) - x; dy = TTOF(gridY) + ITOF(level->path[set->multiA].y[FTOI(node)]) - y; x += dx; y += dy; dx = dx << 6; dy = dy << 6; return; case 8: /// @todo Bird-esque following break; case 9: /// @todo Find out what behaviour 9 is break; case 10: /// @todo Find out what behaviour 10 is break; case 11: // Sink to ground if (!level->checkMaskDown(x + (width >> 1), y)) dy = ES_FAST; else dy = 0; break; case 12: // Move back and forth horizontally if (animType == E_LEFTANIM) dx = -ES_SLOW; else if (animType == E_RIGHTANIM) dx = ES_SLOW; else dx = 0; break; case 13: // Move up and down if (animType == E_LEFTANIM) dy = -ES_SLOW; else if (animType == E_RIGHTANIM) dy = ES_SLOW; else dy = 0; break; case 14: /// @todo Move back and forth rapidly break; case 15: /// @todo Rise or lower to meet jazz break; case 16: // Move across level to the left or right if (set->magnitude == 0) dx = -ES_SLOW; else dx = set->magnitude * ES_SLOW; break; case 17: /// @todo Find out what behaviour 17 is break; case 18: /// @todo Find out what behaviour 18 is break; case 19: /// @todo Find out what behaviour 19 is break; case 20: /// @todo Find out what behaviour 20 is break; case 21: // Destructible block if (level->getEventHits(gridX, gridY) >= set->strength) level->setTile(gridX, gridY, set->multiA); break; case 22: /// @todo Fall down in random spot and repeat break; case 23: /// @todo Find out what behaviour 23 is break; case 24: /// @todo Crawl along ground and go downstairs break; case 26: /// @todo Find out what behaviour 26 is break; case 27: /// @todo Face jazz break; case 29: // Rotate length = set->pieceSize * set->pieces; angle = (set->angle << 2) + (set->magnitude * ticks / 13); dx = TTOF(gridX) + (fSin(angle) * length) - x; dy = TTOF(gridY) + ((fCos(angle) + F1) * length) - y; x += dx; y += dy; dx = dx << 6; dy = dy << 6; return; case 30: // Swing length = set->pieceSize * set->pieces; angle = (set->angle << 2) + (set->magnitude * ticks / 13); dx = TTOF(gridX) + (fSin(angle) * length) - x; dy = TTOF(gridY) + ((abs(fCos(angle)) + F1) * length) - y; x += dx; y += dy; dx = dx << 6; dy = dy << 6; return; case 31: // Move horizontally if (animType == E_LEFTANIM) dx = -ES_FAST; else dx = ES_FAST; break; case 32: // Move horizontally if (animType == E_LEFTANIM) dx = -ES_FAST; else dx = ES_FAST; break; case 33: // Sparks-esque following if (levelPlayer->getFacing() && (x + width < levelPlayer->getX())) { dx = ES_FAST; if (y + height < levelPlayer->getY() + PYO_TOP) dy = ES_SLOW; else if (y > levelPlayer->getY()) dy = -ES_SLOW; else dy = 0; } else if (!levelPlayer->getFacing() && (x > levelPlayer->getX() + F32)) { dx = -ES_FAST; if (y + height < levelPlayer->getY() + PYO_TOP) dy = ES_SLOW; else if (y > levelPlayer->getY()) dy = -ES_SLOW; else dy = 0; } else { dx = 0; dy = 0; } break; case 34: // Launching event if (ticks > level->getEventTime(gridX, gridY)) { if (animType == E_LEFTANIM) dy = -(F16 + y - (TTOF(gridY) - (set->multiA * F12))) * 10; else dy = (F16 + y - (TTOF(gridY) - (set->multiA * F12))) * 10; } else { dy = TTOF(gridY) + F16 - y; y += dy; dy = dy << 6; return; } break; case 35: // Non-floating Sparks-esque following if (levelPlayer->getFacing() && (x + width < levelPlayer->getX() + PXO_L - F4)) { if (level->checkMaskDown(x + width, y + F4) && !level->checkMaskDown(x + width + F4, y - (height >> 1))) dx = ES_FAST; else dx = 0; } else if (!levelPlayer->getFacing() && (x > levelPlayer->getX() + PXO_R + F4)) { if (level->checkMaskDown(x, y + F4) && !level->checkMaskDown(x - F4, y - (height >> 1))) dx = -ES_FAST; else dx = 0; } else dx = 0; break; case 36: // Walk from side to side and down hills, staying on-screen if (!level->checkMaskDown(x + (width >> 1), y)) { // Fall downwards dx = 0; dy = ES_FAST; } else { // Walk from side to side, staying on-screen if (animType == E_LEFTANIM) dx = -ES_FAST; else if (animType == E_RIGHTANIM) dx = ES_FAST; else dx = 0; dy = 0; } break; case 39: /// @todo Collapsing floor break; case 40: /// @todo Find out what behaviour 40 is break; case 41: /// @todo Switch left & right anim periodically break; case 42: /// @todo Find out what behaviour 42 is break; case 43: /// @todo Find out what behaviour 43 is break; case 44: /// @todo Leap to greet Jazz very quickly break; case 45: /// @todo Find out what behaviour 45 is break; case 46: /// @todo "Final" boss break; case 53: // Dreempipes turtles if (y > level->getWaterLevel()) { if (animType == E_LEFTANIM) dx = -ES_SLOW; else if (animType == E_RIGHTANIM) dx = ES_SLOW; else dx = 0; } else dx = 0; break; default: // Do nothing for the following: // 0: Static // 25: Float up / Belt // 37/38: Repel /// @todo Remaining event behaviours break; } dx /= set->speed; dy /= set->speed; x += dx >> 6; y += dy >> 6; } /** * Event iteration. * * @param ticks Time * * @return Remaining event */ JJ1Event* JJ1StandardEvent::step (unsigned int ticks) { JJ1LevelPlayer* levelPlayer; int count; unsigned int eventTime; int hits; set = prepareStep(ticks); hits = level->getEventHits(gridX, gridY); // If the event is off-screen, remove it (permanently if it's been deflected by a shield) if (!set) return remove(hits == 255); // If the event has been deflected by a shield, move it if (hits == 255) { dy = 200 * F1; x += dx >> 6; y += dy >> 6; return this; } // Get the time of the event's next action eventTime = level->getEventTime(gridX, gridY); // If the event's finish animation has expired, remove it if (eventTime && ((animType & ~1) == E_LFINISHANIM)) { if (anim == NULL) return remove(true); if (((int)(ticks - eventTime) / (int)(set->animSpeed << 3)) > anim->getLength()) return remove(true); } // Get the player levelPlayer = localPlayer->getJJ1LevelPlayer(); // Move move(ticks); // Choose animation and direction if ((animType & ~1) == E_LEFTANIM) { switch (set->movement) { case 2: // Walk from side to side if (animType == E_LEFTANIM) { if (!level->checkMaskDown(x, y + F4) || level->checkMaskDown(x - F4, y - (height >> 1))) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (!level->checkMaskDown(x + width, y + F4) || level->checkMaskDown(x + width + F4, y - (height >> 1))) setAnimType(E_LEFTANIM); } break; case 3: // Seek jazz if (levelPlayer->getX() + PXO_R < x) setAnimType(E_LEFTANIM); else if (levelPlayer->getX() + PXO_L > x + width) setAnimType(E_RIGHTANIM); break; case 4: // Walk from side to side and down hills if (level->checkMaskDown(x + (width >> 1), y)) { // Walk from side to side if (animType == E_LEFTANIM) { if (level->checkMaskDown(x - F4, y - (height >> 1) - F12)) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (level->checkMaskDown(x + width + F4, y - (height >> 1) - F12)) setAnimType(E_LEFTANIM); } } break; case 6: // Use the path from the level file // Check movement direction if ((FTOI(node) < 3) || (level->path[set->multiA].x[FTOI(node)] <= level->path[set->multiA].x[FTOI(node) - 3])) setAnimType(E_LEFTANIM); else setAnimType(E_RIGHTANIM); break; case 7: // Move back and forth horizontally with tail if (animType == E_LEFTANIM) { if (x < TTOF(gridX)) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (x > TTOF(gridX) + F100) setAnimType(E_LEFTANIM); } break; case 12: // Move back and forth horizontally if (animType == E_LEFTANIM) { if (level->checkMaskDown(x - F4, y - (height >> 1))) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (level->checkMaskDown(x + width + F4, y - (height >> 1))) setAnimType(E_LEFTANIM); } break; case 13: // Move up and down if (animType == E_LEFTANIM) { if (level->checkMaskDown(x + (width >> 1), y - height - F4)) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (level->checkMaskDown(x + (width >> 1), y + F4)) setAnimType(E_LEFTANIM); } break; case 26: // Flip animation if (levelPlayer->overlap(x, y - height, width, height)) setAnimType(E_LEFTANIM); else setAnimType(E_RIGHTANIM); break; case 31: // Moving platform if (animType == E_LEFTANIM) { if (level->checkMaskDown(x, y - (height >> 1))) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (level->checkMaskDown(x + width, y - (height >> 1))) setAnimType(E_LEFTANIM); } break; case 32: // Moving platform if (x < TTOF(gridX) - (set->pieceSize << 14)) setAnimType(E_RIGHTANIM); else if (x > TTOF(gridX + set->pieceSize)) setAnimType(E_LEFTANIM); break; case 33: // Sparks-esque following if (levelPlayer->getFacing() && (x + width < levelPlayer->getX())) { setAnimType(E_RIGHTANIM); } else if (!levelPlayer->getFacing() && (x > levelPlayer->getX() + F32)) { setAnimType(E_LEFTANIM); } break; case 34: // Launching event if (ticks > eventTime) { if (y <= F16 + TTOF(gridY) - (set->multiA * F12)) setAnimType(E_RIGHTANIM); else if (y >= F16 + TTOF(gridY)) { setAnimType(E_LEFTANIM); level->setEventTime(gridX, gridY, ticks + (set->multiB * 50)); } } else setAnimType(E_LEFTANIM); break; case 36: // Walk from side to side and down hills, staying on-screen if (level->checkMaskDown(x + (width >> 1), y)) { // Walk from side to side, staying on-screen if (animType == E_LEFTANIM) { if (level->checkMaskDown(x - F4, y - (height >> 1)) || (x - F4 < viewX)) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (level->checkMaskDown(x + width + F4, y - (height >> 1)) || (x + width + F4 > viewX + ITOF(canvasW))) setAnimType(E_LEFTANIM); } } break; case 53: // Dreempipes turtles if (y > level->getWaterLevel()) { if (animType == E_LEFTANIM) { if (level->checkMaskDown(x - F4, y - (height >> 1))) setAnimType(E_RIGHTANIM); } else if (animType == E_RIGHTANIM) { if (level->checkMaskDown(x + width + F4, y - (height >> 1))) setAnimType(E_LEFTANIM); } else setAnimType(E_LEFTANIM); } break; default: if (levelPlayer->getX() + PXO_MID < x + (width >> 1)) setAnimType(E_LEFTANIM); else setAnimType(E_RIGHTANIM); break; } } // If the event is in its finish stage, nothing else needs to be done if ((animType & ~1) == E_LFINISHANIM) return this; // If the event has been destroyed, play its finishing animation and set its // reaction time if (set->strength && (hits >= set->strength)) { destroy(ticks); } if (set->bulletPeriod) { count = level->getAnim(set->anims[E_LSHOOTANIM | (animType & 1)])->getLength() * set->animSpeed << 5; if ((ticks % (set->bulletPeriod * 32) > (unsigned int)(set->bulletPeriod * 32) - count) && ((animType & ~1) == E_LEFTANIM)) { // Enter firing mode if (animType == E_LEFTANIM) setAnimType(E_LSHOOTANIM); else setAnimType(E_RSHOOTANIM); level->setEventTime(gridX, gridY, ticks + count); } } // If the reaction time has expired if (eventTime && (ticks > eventTime)) { if ((animType & ~1) == E_LSHOOTANIM) { if (set->bullet < 32) level->createBullet( NULL, gridX, gridY, drawnX + anim->getShootX(), drawnY + anim->getShootY(), set->bullet, (animType & 1)? true: false, ticks); setAnimType(E_LEFTANIM | (animType & 1)); } else { level->setEventTime(gridX, gridY, 0); } } if (level->getStage() == LS_END) return this; // Handle contact with player for (count = 0; count < nPlayers; count++) { levelPlayer = players[count].getJJ1LevelPlayer(); // Check if the player is touching the event if (set->modifier == 6) { if (width && height && levelPlayer->overlap(drawnX, drawnY - F4, width - F8, F8) && (levelPlayer->getY() <= F4 + (PYS_FALL >> 6) + drawnY) && !level->checkMaskDown(levelPlayer->getX() + PXO_MID, PYO_TOP + drawnY)) { // Player is on a platform levelPlayer->setPlatform(gridX, gridY, (dx >> 6), drawnY); } else levelPlayer->clearEvent(gridX, gridY); } else { // Check if the player is touching the event if (width && height && levelPlayer->overlap(drawnX + F2, drawnY + F2, width - F4, height - F4)) { // If the player picks up the event, destroy it if (levelPlayer->touchEvent(set, gridX, gridY, ticks)) { if (level->getEventHits(gridX, gridY) == 255) { if (levelPlayer->getX() + PXO_MID > x + (width >> 1)) dx = -400 * F1; else dx = 400 * F1; dy = 200 * F1; } else { destroy(ticks); } } } } } return this; } /** * Draw standard event. * * @param ticks Time * @param change Time since last iteration */ void JJ1StandardEvent::draw (unsigned int ticks, int change) { if (next) next->draw(ticks, change); // Uncomment the following to see the raw location /*drawRect(FTOI(getDrawX(change)), FTOI(getDrawY(change) - height), FTOI(width), FTOI(height), 88);*/ // If the event has been removed from the grid, do not show it if (!set) return; // Check if the event has anything to draw if (animType == E_NOANIM) { drawnX = x; drawnY = y - F32; width = F32; height = F32; return; } if ((animType & ~1) == E_LFINISHANIM) { setAnimFrame((ticks - level->getEventTime(gridX, gridY)) / (set->animSpeed << 3), false); } else if ((animType & ~1) == E_LSHOOTANIM) { setAnimFrame((ticks + (anim->getLength() * set->animSpeed << 5) - level->getEventTime(gridX, gridY)) / (set->animSpeed << 5), false); } else { setAnimFrame((ticks / (set->animSpeed << 5)) + gridX + gridY, true); } // Calculate new positions fixed changeX = getDrawX(change); fixed changeY = getDrawY(change); // Draw the event // Check if an explosive effect should be drawn if (((animType & ~1) == E_LFINISHANIM) && (set->anims[animType] & 0x80)) { // In case of an explosion // Determine position in a half circle path fixed xOffset = fCos((ticks - level->getEventTime(gridX, gridY)) >> 1) * 48 - ITOF(16); fixed yOffset = fSin((ticks - level->getEventTime(gridX, gridY)) >> 1) * 48; int val = gridX + gridY; // Draw the animation in six different positions anim->draw(changeX - yOffset, changeY - xOffset); anim->draw(changeX + yOffset, changeY - xOffset); anim->draw(changeX + ITOF(val % 32) - yOffset, changeY - ITOF(val % 8) - xOffset); anim->draw(changeX - ITOF(val % 16) + yOffset, changeY - ITOF(val % 16) - xOffset); anim->draw(changeX + ITOF(val % 24) - yOffset, changeY + ITOF(val % 12) - xOffset); anim->draw(changeX - ITOF(val % 48) + yOffset, changeY + ITOF(val % 24) - xOffset); } else { // In case an event can be drawn normally fixed offset; if ((ticks < flashTime) && ((ticks >> 4) & 3)) anim->flashPalette(0); // Determine the corect vertical offset // Most animations need a default offset of 1 tile (32 pixels) if ((anim->getWidth() == 1) && (anim->getHeight() == 1)) { offset = -F32; } else if (noAnimOffset) { offset = 0; } else { if (onlyLAnimOffset && (animType == E_RIGHTANIM)) { offset = level->getAnim(set->anims[E_LEFTANIM] & 0x7F)->getOffset(); } else if (onlyRAnimOffset && (animType == E_LEFTANIM)) { offset = level->getAnim(set->anims[E_RIGHTANIM] & 0x7F)->getOffset(); } else { offset = anim->getOffset(); } if (offset == 0) offset = -ITOF(TTOI(1) - 1); } drawnX = x + anim->getXOffset() + F1; drawnY = y + anim->getYOffset() + offset + F1; // Uncomment the following line to see the draw area //drawRect(FTOI(changeX - x + drawnX), FTOI(changeY - y + drawnY), FTOI(width), FTOI(height), 88); anim->draw(changeX + F1, changeY + offset + F1 - anim->getOffset()); if ((ticks < flashTime) && ((ticks >> 4) & 3)) anim->restorePalette(); } // If the event has been destroyed, draw an explosion if (set->strength && ((animType & ~1) == E_LFINISHANIM)) { Anim* miscAnim = level->getMiscAnim(MA_EXPLOSION1); miscAnim->setFrame((ticks - level->getEventTime(gridX, gridY)) >> 3, false); miscAnim->draw(changeX, changeY); } } openjazz-20240919/src/jj1/level/jj1bird.cpp000066400000000000000000000114501467303304200202440ustar00rootroot00000000000000 /** * * @file jj1bird.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp * - 1st March 2009: Created bird.cpp from parts of events.cpp * - 1st August 2012: Renamed bird.cpp to jj1bird.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "jj1bullet.h" #include "event/jj1event.h" #include "jj1level.h" #include "jj1bird.h" #include "jj1levelplayer.h" #include "io/gfx/video.h" /** * Create a bird for the specified player. * * @param birds The player's existing birds (NULL if none) * @param rescuer The player that freed the bird * @param gX The new bird's grid x-coordinate * @param gY The new bird's grid y-coordinate */ JJ1Bird::JJ1Bird (JJ1Bird* birds, JJ1LevelPlayer *rescuer, unsigned char gX, unsigned char gY) { next = birds; player = rescuer; x = TTOF(gX); y = TTOF(gY); dx = 0; dy = 0; fleeing = false; fireTime = 0; } /** * Delete all birds. */ JJ1Bird::~JJ1Bird () { if (next) delete next; } /** * Delete this bird. * * @return The next bird */ JJ1Bird* JJ1Bird::remove () { JJ1Bird* oldNext; oldNext = next; next = NULL; delete this; return oldNext; } /** * Get the player that freed the bird. * * @return The player */ JJ1LevelPlayer * JJ1Bird::getPlayer () { return player; } /** * Notify the bird that the player has been hit. */ void JJ1Bird::hit () { fleeing = true; } /** * Recursively count the number of birds. * * @return The number of birds ahead of this bird, plus one */ int JJ1Bird::getFlockSize () { if (next) return next->getFlockSize() + 1; return 1; } /** * Recursively set the number of birds. * * @param size The number of birds not already counted * * @return Remaining bird (NULL if none) */ JJ1Bird* JJ1Bird::setFlockSize (int size) { if (size <= 0) { delete this; return NULL; } if (size > 1) { if (!next) next = new JJ1Bird(NULL, player, FTOT(x), FTOT(y)); next = next->setFlockSize(size - 1); } return this; } /** * JJ1Bird iteration. * * @param ticks Time * * @return Remaining bird (NULL if none) */ JJ1Bird* JJ1Bird::step (unsigned int ticks) { Movable* leader; JJ1Event* event; // Process the next bird if (next) next = next->step(ticks); if (next) leader = next; else leader = player; if (fleeing) { // Trajectory for flying away dx = F80; dy = -F80; // If the bird has flown off-screen, remove it if (y < viewY - F160) return remove(); } else { // Trajectory for flying towards the leader if ((x < leader->getX() - F160) || (x > leader->getX() + F160)) { // Far away from the leader // Approach the leader at a speed proportional to the distance dx = leader->getX() - x; } else if (x < leader->getX()) { // To the left of the leader, so move right if (dx < F80) dx += 6400; } else { // To the right of the leader, so move left if (dx > -F80) dx -= 6400; } if (y > level->getWaterLevel() - F24) { // Always stay above water y = level->getWaterLevel() - F24; dy = 0; } else { if ((y < leader->getY() - F100) || (y > leader->getY() + F100)) { // Far away from the leader // Approach the leader at a speed proportional to the distance dy = (leader->getY() - F64) - y; } else if (y < leader->getY() - F64) { // Above the leader, so move downwards if (dy < F80) dy += 6400; } else { // Below the leader, so move upwards if (dy > -F80) dy -= 6400; } } if (ticks > fireTime) { // Check for nearby targets bool target = false; event = level->getEvents(); if (player->getFacing()) { while (event && !target) { target = event->isEnemy() && event->overlap(x, y, F160, F100); event = event->getNext(); } } else { while (event && !target) { target = event->isEnemy() && event->overlap(x - F160, y, F160, F100); event = event->getNext(); } } // If there is a target in the vicinity, generate bullets if (target) { level->createBullet(player, 0, 0, x + (player->getFacing()? PXO_R: PXO_L), y, 30, player->getFacing(), ticks); fireTime = ticks + T_BIRD_FIRE; } } } // Apply trajectory x += dx >> 6; y += dy >> 6; return this; } /** * Draw the bird. * * @param ticks Time * @param change Time since last step */ void JJ1Bird::draw (unsigned int ticks, int change) { Anim *anim; if (next) next->draw(ticks, change); anim = level->getMiscAnim((player->getFacing() || fleeing)? MA_RBIRD: MA_LBIRD); anim->setFrame(ticks / 80, true); anim->draw(getDrawX(change), getDrawY(change)); } openjazz-20240919/src/jj1/level/jj1bird.h000066400000000000000000000025621467303304200177150ustar00rootroot00000000000000 /** * * @file jj1bird.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 4th February 2009: Created events.h from parts of level.h * - 1st March 2009: Created bird.h from parts of events.h * - 1st August 2012: Renamed bird.h to jj1bird.h * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _BIRD_H #define _BIRD_H #include "level/movable.h" #include "OpenJazz.h" // Constants // Time interval #define T_BIRD_FIRE 500 // Classes class JJ1LevelPlayer; /// JJ1 bird companion class JJ1Bird : public Movable { private: JJ1Bird* next; JJ1LevelPlayer* player; ///< Player that rescued the bird bool fleeing; ///< Flying away, player having been shot unsigned int fireTime; ///< Next time the bird will fire JJ1Bird* remove (); public: JJ1Bird (JJ1Bird* birds, JJ1LevelPlayer* player, unsigned char gX, unsigned char gY); ~JJ1Bird (); int getFlockSize (); JJ1LevelPlayer* getPlayer (); void hit (); JJ1Bird* setFlockSize (int size); JJ1Bird* step (unsigned int ticks); void draw (unsigned int ticks, int change); }; #endif openjazz-20240919/src/jj1/level/jj1bullet.cpp000066400000000000000000000107761467303304200206250ustar00rootroot00000000000000 /** * * @file jj1bullet.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp * - 11th February 2009: Created bullet.cpp from parts of events.cpp * - 1st August 2012: Renamed bullet.cpp to jj1bullet.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "jj1bullet.h" #include "event/jj1event.h" #include "jj1level.h" #include "jj1bird.h" #include "jj1levelplayer.h" #include "game/game.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include /** * Generic bullet constructor. * * @param nextBullet The next bullet * @param sourcePlayer The player that fired the bullet (if any) * @param startX The starting x-coordinate of the bullet * @param startY The starting y-coordinate of the bullet * @param bullet Type * @param newDirection The direction of the bullet * @param ticks Time */ JJ1Bullet::JJ1Bullet (JJ1Bullet* nextBullet, JJ1LevelPlayer* sourcePlayer, fixed startX, fixed startY, signed char* bullet, int newDirection, unsigned int ticks) { next = nextBullet; source = sourcePlayer; set = bullet; direction = newDirection; x = startX; y = startY; dx = set[B_XSPEED + direction] << 18; dy = set[B_YSPEED + direction] << 16; if (source) { if ((newDirection & 1) == 0) { if (dx > 0) dx = -dx; } else { if (dx < 0) dx = -dx; } if (set[B_BEHAVIOUR] == 4) dx += source->getXSpeed(); } sprite = level->getSprite(reinterpret_cast(set)[B_SPRITE + direction]); time = ticks + T_BULLET; } /** * Delete all bullets. */ JJ1Bullet::~JJ1Bullet () { if (next) delete next; } /** * Delete this bullet. * * @return The next bullet */ JJ1Bullet* JJ1Bullet::remove () { JJ1Bullet* oldNext; oldNext = next; next = NULL; delete this; return oldNext; } /** * Get the player responsible for this bullet. * * @return The player (NULL if fired by an event) */ JJ1LevelPlayer* JJ1Bullet::getSource () { return source; } /** * Bullet iteration. * * @param ticks Time * * @return Remaining bullet */ JJ1Bullet* JJ1Bullet::step (unsigned int ticks) { // Process the next bullet if (next) next = next->step(ticks); if (level->getStage() != LS_END) { // If the time has expired, destroy the bullet if (ticks > time) return remove(); // Check if a player has been hit for (int i = 0; i < nPlayers; i++) { if (players[i].getJJ1LevelPlayer()->overlap(x, y, ITOF(sprite->getWidth()), ITOF(sprite->getHeight()))) { // If the hit was successful, destroy the bullet if (players[i].getJJ1LevelPlayer()->hit(source? source->player: NULL, ticks)) return remove(); } } if (source) { // Check if an event has been hit JJ1Event* event = level->getEvents(); while (event) { // Check if the event has been hit if (event->overlap(x, y, ITOF(sprite->getWidth()), ITOF(sprite->getHeight()))) { // If the event is hittable, hit it and destroy the bullet if (event->hit(source, 1, ticks)) return remove(); } event = event->getNext(); } } } // If the scenery has been hit and this is not a bouncer, destroy the bullet if (level->checkMaskUp(x, y) && (set[B_BEHAVIOUR] != 4)) { // FIXME: rewrite "set" auto se = static_cast(set[B_FINISHSOUND]); playSound(se); return remove(); } // Calculate trajectory if (set[B_BEHAVIOUR] == 4) { if (level->checkMaskDown(x, y - F1)) { // Bounce the bullet away from a vertical surface if (dx < 0) direction |= 1; else direction &= ~1; dx = -dx; dy = 0; } if (level->checkMaskDown(x, y + (dy >> 6) - F1)) { // Bounce the bullet against a horizontal surface if (dy < 0) dy = 0; else dy = -dy - (abs(dx - (set[B_XSPEED + direction] << 18)) >> 1); } else { // Respond to gravity dy += F32 * set[B_GRAVITY]; } } else { dy += F32 * set[B_GRAVITY]; if (source && (abs(source->getXSpeed() + dx) > abs(dx))) { x += source->getXSpeed() >> 6; } } // Apply trajectory x += dx >> 6; y += dy >> 6; // Do not destroy the bullet return this; } /** * Draw the bullet. * * @param change Time since last iteration */ void JJ1Bullet::draw (int change) { if (next) next->draw(change); // Show the bullet sprite->draw(FTOI(getDrawX(change)), FTOI(getDrawY(change)), false); } openjazz-20240919/src/jj1/level/jj1bullet.h000066400000000000000000000034401467303304200202600ustar00rootroot00000000000000 /** * * @file jj1bullet.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 4th February 2009: Created events.h from parts of level.h * - 11th February 2009: Created bullet.h from parts of events.h * - 1st August 2012: Renamed bullet.h to jj1bullet.h * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _BULLET_H #define _BULLET_H #include "level/movable.h" #include "OpenJazz.h" // Constants // Indexes for elements of the bullet set #define B_SPRITE 0 #define B_XSPEED 4 #define B_YSPEED 8 #define B_GRAVITY 12 #define B_FINISHANIM 16 #define B_FINISHSOUND 17 #define B_BEHAVIOUR 18 #define B_BEHAVIOR 18 #define B_STARTSOUND 19 // Survival time #define T_BULLET 1000 #define T_TNT 300 // Classes class JJ1Bird; class JJ1Event; class JJ1LevelPlayer; class Sprite; /// JJ1Bullet class JJ1Bullet : public Movable { private: JJ1Bullet* next; ///< The next bullet JJ1LevelPlayer* source; ///< Source player. If NULL, was fired by an event Sprite* sprite; ///< Sprite signed char* set; ///< Bullet type properties int direction; ///< 0: Left, 1: Right, 2: L (lower), 3: R (lower) unsigned int time; ///< Time at which the bullet will self-destruct JJ1Bullet* remove (); public: JJ1Bullet (JJ1Bullet* nextBullet, JJ1LevelPlayer* sourcePlayer, fixed startX, fixed startY, signed char *bullet, int newDirection, unsigned int ticks); ~JJ1Bullet (); JJ1LevelPlayer* getSource (); JJ1Bullet* step (unsigned int ticks); void draw (int change); }; #endif openjazz-20240919/src/jj1/level/jj1demolevel.cpp000066400000000000000000000067361467303304200213130ustar00rootroot00000000000000 /** * * @file jj1demolevel.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed level.c to level.cpp and levelload.c to * levelload.cpp * - 18th July 2009: Created demolevel.cpp from parts of level.cpp and * levelload.cpp * - 1st August 2012: Renamed demolevel.cpp to jj1demolevel.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading and playing of demo levels. * */ #include "jj1level.h" #include "jj1levelplayer.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "loop.h" #include "util.h" /** * Create a JJ1 demo level. * * @param owner The current game * @param fileName Name of the file containing the macro data. */ JJ1DemoLevel::JJ1DemoLevel (Game* owner, const char* fileName) : JJ1Level(owner) { File* file; char* levelFile; int lNum, wNum, ret; multiplayer = false; try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { throw; } // Check this is a normal level if (file->loadShort() == 0) { delete file; throw E_DEMOTYPE; } // Level file to load lNum = file->loadShort(9); wNum = file->loadShort(999); levelFile = createFileName("LEVEL", lNum, wNum); // Difficulty file->loadShort(); macro = file->loadBlock(1024); delete file; // Load level data ret = load(levelFile, false); delete[] levelFile; if (ret < 0) throw ret; } /** * Delete the JJ1 demo level. */ JJ1DemoLevel::~JJ1DemoLevel () { delete[] macro; } /** * Play the demo. * * @return Error code */ int JJ1DemoLevel::play () { tickOffset = globalTicks; ticks = 17; steps = 0; video.setPalette(palette); playMusic(musicFile); while (true) { // Do general processing if (::loop(NORMAL_LOOP, paletteEffects) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE) || controls.release(C_ENTER)) return E_NONE; if (controls.release(C_STATS)) stats ^= S_SCREEN; timeCalcs(); // Use macro unsigned char macroPoint = macro[(ticks / 76) & 1023]; if (macroPoint & 128) return E_NONE; if (macroPoint & 1) { localPlayer->setControl(C_LEFT, false); localPlayer->setControl(C_RIGHT, false); localPlayer->setControl(C_UP, !(macroPoint & 4)); } else { localPlayer->setControl(C_LEFT, !(macroPoint & 2)); localPlayer->setControl(C_RIGHT, macroPoint & 2); localPlayer->setControl(C_UP, false); } localPlayer->setControl(C_DOWN, macroPoint & 8); localPlayer->setControl(C_FIRE, macroPoint & 16); localPlayer->setControl(C_CHANGE, macroPoint & 32); localPlayer->setControl(C_JUMP, macroPoint & 64); localPlayer->setControl(C_SWIM, macroPoint & 64); // Check if level has been won if (getStage() == LS_END) return WON; // Process frame-by-frame activity // Process step while (getTimeChange() >= T_STEP) { int ret = step(); steps++; if (ret < 0) return ret; } // Handle player reactions if (localPlayer->getJJ1LevelPlayer()->reacted(ticks) == PR_KILLED) return LOST; // Draw the graphics draw(); drawOverlay(LEVEL_BLACK, false, 0, 0, 0, 0); font->showString("demo", (canvasW >> 1) - 36, 32); } return E_NONE; } openjazz-20240919/src/jj1/level/jj1level.cpp000066400000000000000000000423111467303304200204330ustar00rootroot00000000000000 /** * * @file jj1level.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 18th July 2009: Created demolevel.cpp from parts of level.cpp and * levelload.cpp * - 19th July 2009: Created levelframe.cpp from parts of level.cpp * - 19th July 2009: Added parts of levelload.cpp to level.cpp * - 30th March 2010: Created baselevel.cpp from parts of level.cpp and * levelframe.cpp * - 29th June 2010: Created jj2level.cpp from parts of level.cpp * - 1st August 2012: Renamed level.cpp to jj1level.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creating, playing and freeing of levels. * */ #include "jj1bullet.h" #include "event/jj1event.h" #include "jj1level.h" #include "jj1levelplayer.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/paletteeffects.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" #include /** * Base constructor for JJ1DemoLevel sub-class. * * @param owner The current game */ JJ1Level::JJ1Level (Game* owner) : Level(owner) { tileSet = panel = NULL; events = NULL; bullets = NULL; sceneFile = NULL; spriteSet = NULL; sky = false; skyOrb = 0; levelNum = worldNum = nextLevelNum = nextWorldNum = 0; enemies = 0; waterLevel = waterLevelTarget = waterLevelSpeed = 0; energyBar = ammoType = ammoOffset = 0; font = NULL; musicFile = NULL; } /** * Create a JJ1 level. * * @param owner The current game * @param fileName Name of the file containing the level data. * @param checkpoint Whether or not the player(s) will start at a checkpoint * @param multi Whether or not the level will be multi-player */ JJ1Level::JJ1Level (Game* owner, char* fileName, bool checkpoint, bool multi) : Level (owner) { // Load level data int ret = load(fileName, checkpoint); if (ret < 0) throw ret; multiplayer = multi; } /** * Delete HUD graphical data. */ void JJ1Level::deletePanel () { SDL_FreeSurface(panel); SDL_FreeSurface(panelAmmo[0]); SDL_FreeSurface(panelAmmo[1]); SDL_FreeSurface(panelAmmo[2]); SDL_FreeSurface(panelAmmo[3]); SDL_FreeSurface(panelAmmo[4]); SDL_FreeSurface(panelAmmo[5]); } /** * Delete the JJ1 level. */ JJ1Level::~JJ1Level () { // Free events if (events) delete events; // Free bullets if (bullets) delete bullets; for (int i = 0; i < PATHS; i++) { delete[] path[i].x; delete[] path[i].y; } delete[] sceneFile; delete[] musicFile; delete[] spriteSet; SDL_FreeSurface(tileSet); deletePanel(); delete font; resampleSounds(); video.setTitle(NULL); } /** * Determine whether or not the given point is solid when travelling upwards. * * @param x X-coordinate * @param y Y-coordinate * * @return Solidity */ bool JJ1Level::checkMaskUp (fixed x, fixed y) { GridElement *ge; // Anything off the edge of the map is solid if ((x < 0) || (y < 0) || (x >= TTOF(LW)) || (y >= TTOF(LH))) return true; ge = grid[FTOT(y)] + FTOT(x); // JJ1Event 122 is one-way if (ge->event == 122) return false; // Check the mask in the tile in question return mask[ge->tile][((y >> 9) & 56) + ((x >> 12) & 7)]; } /** * Determine whether or not the given point is solid when travelling downwards. * * @param x X-coordinate * @param y Y-coordinate * * @return Solidity */ bool JJ1Level::checkMaskDown (fixed x, fixed y) { // Anything off the edge of the map is solid if ((x < 0) || (y < 0) || (x >= TTOF(LW)) || (y >= TTOF(LH))) return true; // Check the mask in the tile in question return mask[grid[FTOT(y)][FTOT(x)].tile][((y >> 9) & 56) + ((x >> 12) & 7)]; } /** * Determine whether or not the given point should cause damage to the player. * * @param x X-coordinate * @param y Y-coordinate * * @return Painful solidity */ bool JJ1Level::checkSpikes (fixed x, fixed y) { GridElement *ge; // Anything off the edge of the map is not spikes // Ignore the bottom, as it is deadly anyway if ((x < 0) || (y < 0) || (x > TTOF(LW))) return false; ge = grid[FTOT(y)] + FTOT(x); // JJ1Event 126 is spikes if (ge->event != 126) return false; // Check the mask in the tile in question return mask[ge->tile][((y >> 9) & 56) + ((x >> 12) & 7)]; } /** * Determine the level's world number. * * @return World number */ int JJ1Level::getWorld() { return worldNum; } /** * Set which level will come next. * * @param nextLevel Next level's number * @param nextWorld Next level's world number */ void JJ1Level::setNext (int nextLevel, int nextWorld) { nextLevelNum = nextLevel; nextWorldNum = nextWorld; if (multiplayer) { unsigned char buffer[MTL_L_PROP]; buffer[0] = MTL_L_PROP; buffer[1] = MT_L_PROP; buffer[2] = 0; // set next level buffer[3] = nextLevel; buffer[4] = nextWorld; game->send(buffer); } } /** * Set the tile at the given location. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param tile The new tile */ void JJ1Level::setTile (unsigned char gridX, unsigned char gridY, unsigned char tile) { grid[gridY][gridX].tile = tile; if (multiplayer) { unsigned char buffer[MTL_L_GRID]; buffer[0] = MTL_L_GRID; buffer[1] = MT_L_GRID; buffer[2] = gridX; buffer[3] = gridY; buffer[4] = 0; // tile variable buffer[5] = tile; game->send(buffer); } } /** * Get the active events. * * @return The first active event */ JJ1Event* JJ1Level::getEvents () { return events; } /** * Get the event data for the event from the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * * @return JJ1Event data */ JJ1EventType* JJ1Level::getEvent (unsigned char gridX, unsigned char gridY) { int event = grid[gridY][gridX].event; if (event) return eventSet + event; return NULL; } /** * Get the hits incurred by the event from the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * * @return Number of hits */ unsigned char JJ1Level::getEventHits (unsigned char gridX, unsigned char gridY) { return grid[gridY][gridX].hits; } /** * Get the set time for the event from the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * * @return Time */ unsigned int JJ1Level::getEventTime (unsigned char gridX, unsigned char gridY) { return grid[gridY][gridX].time; } /** * Remove the event from the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile */ void JJ1Level::clearEvent (unsigned char gridX, unsigned char gridY) { // Ignore if the event has been un-destroyed if (!grid[gridY][gridX].hits && eventSet[grid[gridY][gridX].event].strength) return; grid[gridY][gridX].event = 0; if (multiplayer) { unsigned char buffer[MTL_L_GRID]; buffer[0] = MTL_L_GRID; buffer[1] = MT_L_GRID; buffer[2] = gridX; buffer[3] = gridY; buffer[4] = 2; // event variable buffer[5] = 0; game->send(buffer); } } /** * Register hit(s) on the event for the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param hits The number of hits to attempt to inflict on the event * @param source The player that inflicted the hit(s) * @param time Time * * @return The remaining number of hits until the event is destroyed */ int JJ1Level::hitEvent (unsigned char gridX, unsigned char gridY, int hits, JJ1LevelPlayer* source, unsigned int time) { GridElement* ge; int hitsToKill; ge = grid[gridY] + gridX; hitsToKill = eventSet[ge->event].strength; // If the event cannot be hit, return negative if (!hitsToKill || (ge->hits == 255)) return -1; // If the event has already been destroyed, do nothing if (ge->hits >= hitsToKill) return 0; // Check if the event has been killed if (ge->hits + hits >= hitsToKill) { // Notify the player that shot the bullet // If this returns false, ignore the hit if (!source->takeEvent(eventSet + ge->event, gridX, gridY, ticks)) { return hitsToKill - ge->hits; } ge->hits = (hits == 255)? 255: hitsToKill; ge->time = time; } else { ge->hits += hits; } if (multiplayer) { unsigned char buffer[MTL_L_GRID]; buffer[0] = MTL_L_GRID; buffer[1] = MT_L_GRID; buffer[2] = gridX; buffer[3] = gridY; buffer[4] = 3; // hits variable buffer[5] = ge->hits; game->send(buffer); } return hitsToKill - ge->hits; } /** * Set the time of the event from the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param time Time */ void JJ1Level::setEventTime (unsigned char gridX, unsigned char gridY, unsigned int time) { grid[gridY][gridX].time = time; } /** * Get a sprite. * * @param sprite Sprite number * * @return Sprite */ Sprite* JJ1Level::getSprite (unsigned char sprite) { return spriteSet + sprite; } /** * Get an animation. * * @param anim Animation number * * @return Animation */ Anim* JJ1Level::getAnim (unsigned char anim) { return animSet + anim; } /** * Get a "miscellaneous" animation. * * @param anim Animation number * * @return Animation */ Anim* JJ1Level::getMiscAnim (unsigned char anim) { return animSet + miscAnims[anim]; } /** * Get a player animation. * * @param anim Animation number * * @return Animation */ Anim* JJ1Level::getPlayerAnim (unsigned char anim) { return animSet + playerAnims[anim]; } /** * Set the water level. * * @param gridY New water level y-coordinate */ void JJ1Level::setWaterLevel (unsigned char gridY) { waterLevelTarget = TTOF(gridY) + F2; if (multiplayer) { unsigned char buffer[MTL_L_PROP]; buffer[0] = MTL_L_PROP; buffer[1] = MT_L_PROP; buffer[2] = 1; // set water level buffer[3] = gridY; buffer[4] = 0; // Doesn't really matter game->send(buffer); } } /** * Determine the water level. * * @return The y-coordinate of the water level */ fixed JJ1Level::getWaterLevel () { return waterLevel; } /** * Create new bullet(s) (or event(s), if applicable) * * @param sourcePlayer The player that fired the bullet (if any) * @param gridX The grid X-coordinate of the origin of the event that fired the bullet (if any) * @param gridY The grid Y-coordinate of the origin of the event that fired the bullet (if any) * @param startX The initial X-coordinate of the bullet * @param startY The initial Y-coordinate of the bullet * @param bullet Type * @param facing The direction of the bullet * @param time Time */ void JJ1Level::createBullet (JJ1LevelPlayer* sourcePlayer, unsigned char gridX, unsigned char gridY, fixed startX, fixed startY, unsigned char bullet, bool facing, unsigned int time) { signed char* set; int direction; set = bulletSet[bullet]; direction = facing? 1: 0; if (set[B_GRAVITY | direction] == 4) { events = new JJ1StandardEvent(eventSet + set[B_SPRITE | direction], gridX, gridY, startX, startY + F32); } else if (set[B_SPRITE | direction] != 0) { // Create new bullet level->bullets = new JJ1Bullet(level->bullets, sourcePlayer, startX, startY, set, direction, time); if (set[B_XSPEED | direction | 2] != 0) { // Create the other bullet level->bullets = new JJ1Bullet(level->bullets, sourcePlayer, startX, startY, set, direction | 2, time); } // FIXME: rewrite "set" auto se = static_cast(set[B_STARTSOUND]); playSound(se); } } /** * Start a flash palette effect. * * @param red Red component of flash colour * @param green Green component of flash colour * @param blue Blue component of flash colour * @param duration Duration of the flash effect */ void JJ1Level::flash (unsigned char red, unsigned char green, unsigned char blue, int duration) { paletteEffects = new FlashPaletteEffect(red, green, blue, duration, paletteEffects); } /** * Play the bonus level. * * @return Error code */ int JJ1Level::playBonus () { char *bonusFile; int ret; if (!localPlayer->getJJ1LevelPlayer()->hasGem()) return E_NONE; delete paletteEffects; paletteEffects = NULL; bonusFile = createFileName("BONUSMAP", 0); // If the gem has been collected, play the bonus level ret = game->playLevel(bonusFile); delete[] bonusFile; return ret; } /** * Interpret data received from client/server * * @param buffer Received data */ void JJ1Level::receive (unsigned char* buffer) { switch (buffer[1]) { case MT_L_PROP: if (buffer[2] == 0) { nextLevelNum = buffer[3]; nextWorldNum = buffer[4]; } else if (buffer[2] == 1) { waterLevelTarget = TTOF(buffer[3]); } else if (buffer[2] == 2) { if (stage == LS_NORMAL) endTime += buffer[3] * 1000; } break; case MT_L_GRID: if (buffer[4] == 0) grid[buffer[3]][buffer[2]].tile = buffer[5]; else if (buffer[4] == 2) grid[buffer[3]][buffer[2]].event = buffer[5]; else if (buffer[4] == 3) grid[buffer[3]][buffer[2]].hits = buffer[5]; break; case MT_L_STAGE: stage = LevelStage(buffer[2]); break; } } /** * Play the level. * * @return Error code */ int JJ1Level::play () { JJ1LevelPlayer* levelPlayer = localPlayer->getJJ1LevelPlayer(); tickOffset = globalTicks; ticks = T_STEP; steps = 0; bool pmessage = false, pmenu = false; int option = 0; unsigned int returnTime = 0; int timeBonus = -1; int perfect = 0; video.setPalette(palette); playMusic(musicFile); while (true) { int ret = loop(pmenu, option, pmessage); if (ret < 0) return ret; // Check if level has been won if (game && returnTime && (ticks > returnTime)) { // Play "Level cleared" cutscene when changing worlds if (nextWorldNum != worldNum) { if (playScene(sceneFile) == E_QUIT) return E_QUIT; ret = game->setLevel(NULL); if (ret < 0) return ret; } if (!multiplayer) { // If the gem has been collected, play the bonus level ret = playBonus(); if (ret < 0) return ret; } // Advance to next level char *string = createFileName("LEVEL", nextLevelNum, nextWorldNum); ret = game->setLevel(string); delete[] string; if (ret < 0) return ret; return WON; } // Process frame-by-frame activity while (getTimeChange() >= T_STEP) { bool playerWasAlive = (localPlayer->getJJ1LevelPlayer()->getEnergy() != 0); // Apply controls to local player for (int i = 0; i < PCONTROLS; i++) localPlayer->setControl(i, controls.getState(i)); ret = step(); steps++; if (ret) return ret; if (!multiplayer && playerWasAlive && (localPlayer->getJJ1LevelPlayer()->getEnergy() == 0)) flash(0, 0, 0, T_END << 1); } // Draw the graphics draw(); // If paused, draw "PAUSE" if (pmessage && !pmenu) font->showString("pause", (canvasW >> 1) - 44, 32); // If paused, silence music pauseMusic(pmessage && !pmenu); if (stage == LS_END) { // The level is over, so draw play statistics & bonuses // Apply time bonus if (timeBonus) { int bonusCount = (ticks - prevTicks) / 100; if (!bonusCount) bonusCount = 1; if (timeBonus == -1) { if (ticks < endTime) timeBonus = ((endTime - ticks) / 60000) * 100; else timeBonus = 0; if ((levelPlayer->getEnemies() == enemies) && (levelPlayer->getItems() == items)) perfect = 100; } else if (timeBonus - bonusCount >= 0) { localPlayer->addScore(bonusCount * 10); timeBonus -= bonusCount; } else { localPlayer->addScore(timeBonus * 10); timeBonus = 0; } if (timeBonus == 0) { returnTime = ticks + T_END; paletteEffects = new WhiteOutPaletteEffect(T_END, paletteEffects); playSound(SE::ORB); } } // Display statistics & bonuses font->showString("time", (canvasW >> 1) - 152, (canvasH >> 1) - 60); font->showNumber(timeBonus, (canvasW >> 1) + 124, (canvasH >> 1) - 60); font->showString("enemies", (canvasW >> 1) - 152, (canvasH >> 1) - 40); if (enemies) font->showNumber((levelPlayer->getEnemies() * 100) / enemies, (canvasW >> 1) + 124, (canvasH >> 1) - 40); else font->showNumber(0, (canvasW >> 1) + 124, (canvasH >> 1) - 40); font->showString("%", (canvasW >> 1) + 124, (canvasH >> 1) - 40); font->showString("items", (canvasW >> 1) - 152, (canvasH >> 1) - 20); if (items) font->showNumber((levelPlayer->getItems() * 100) / items, (canvasW >> 1) + 124, (canvasH >> 1) - 20); else font->showNumber(0, (canvasW >> 1) + 124, (canvasH >> 1) - 20); font->showString("%", (canvasW >> 1) + 124, (canvasH >> 1) - 20); font->showString("perfect", (canvasW >> 1) - 152, canvasH >> 1); font->showNumber(perfect, (canvasW >> 1) + 124, canvasH >> 1); font->showString("score", (canvasW >> 1) - 152, (canvasH >> 1) + 40); font->showNumber(localPlayer->getScore(), (canvasW >> 1) + 124, (canvasH >> 1) + 40); } // Draw statistics, menu etc. drawOverlay(LEVEL_BLACK, pmenu, option, 15, 47, -16); } return E_NONE; } openjazz-20240919/src/jj1/level/jj1level.h000066400000000000000000000213231467303304200201000ustar00rootroot00000000000000 /** * * @file jj1level.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 4th February 2009: Created events.h from parts of level.h * - 19th March 2009: Created sprite.h from parts of level.h * - 30th March 2010: Created baselevel.h from parts of level.h * - 29th June 2010: Created jj2level.h from parts of level.h * - 1st August 2012: Renamed level.h to jj1level.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ /* "Tile" is a flexible term. Here it is used to refer specifically to the individual elements of the tile set. "Tiles" in the context of level units are referred to as grid elements. */ #ifndef _LEVEL_H #define _LEVEL_H #include "level/level.h" #include "io/gfx/anim.h" #include "OpenJazz.h" // Constants // General #define LW 256 /* Level width */ #define LH 64 /* Level height */ #define EVENTS 127 #define ELENGTH 32 /* Length of events, in bytes */ #define BULLETS 32 #define BLENGTH 20 /* Length of bullets, in bytes */ #define ANIMS 128 #define PATHS 16 #define TKEY 127 /* Tileset colour key */ // Player animations #define PA_LWALK 0 #define PA_RWALK 1 #define PA_LJUMP 2 #define PA_RJUMP 3 #define PA_LSPIN 4 #define PA_RSPIN 5 #define PA_LSHOOT 6 #define PA_RSHOOT 7 #define PA_LCROUCH 8 #define PA_RCROUCH 9 #define PA_LFALL 10 #define PA_RFALL 11 #define PA_LHURT 12 #define PA_RHURT 13 #define PA_LLEAN 14 #define PA_RLEAN 15 #define PA_LBOARD 16 #define PA_RBOARD 17 #define PA_LSTAND 18 #define PA_RSTAND 19 #define PA_LBORED 20 #define PA_RBORED 21 #define PA_LEDGE 22 #define PA_REDGE 23 #define PA_LOOKUP 24 #define PA_LOOKDOWN 25 #define PA_LSWIM 26 #define PA_RSWIM 27 #define PA_LRUN 28 #define PA_RRUN 29 #define PA_LDIE 30 #define PA_RDIE 31 #define PA_LSUCK 32 #define PA_RSUCK 33 #define PA_LSTOP 34 /* SUCK and STOP refer almost always to the same animation */ #define PA_RSTOP 35 #define PA_RSPRING 36 #define PA_LSPRING 37 /* Surely these are the wrong way round? */ #define JJ1PANIMS 38 /* Number of player animations. May be higher. */ // Miscellaneous animations #define MA_SPARKLE 0 #define MA_DEVHEAD 1 #define MA_EXPLOSION1 2 #define MA_EXPLOSION2 3 #define MA_4SHIELD 4 #define MA_LBOARD 5 #define MA_RBOARD 6 #define MA_LBIRD 7 #define MA_RBIRD 8 #define MA_ICY 9 #define MA_1SHIELD 10 #define JJ1MANIMS 11 // Black palette index #define LEVEL_BLACK 31 // Fade delays #define T_START 500 #define T_END 1000 // Datatypes /// JJ1 level grid element typedef struct { unsigned char tile; ///< Indexes the tile set unsigned char bg; ///< 0 = Effect background, 1 = Black background unsigned char event; ///< Indexes the event set unsigned char hits; ///< Number of times the event has been shot int time; ///< Point at which the event will do something, e.g. terminate } GridElement; /// JJ1 level event type typedef struct { unsigned char anims[6]; ///< Indices of animations signed char difficulty; ///< The minimum difficulty level at which the event is used signed char reflection; ///< Whether or not to show a reflection signed char movement; ///< Movement type signed char magnitude; ///< Usage depends on event type signed char strength; ///< Number of hits required to destroy the event signed char modifier; ///< Modifier unsigned char points; ///< Points obtained by getting/destroying the event unsigned char bullet; ///< Type of bullet the event fires unsigned char bulletPeriod; ///< The time between successive bullet shots unsigned char speed; ///< The speed at which the event moves unsigned char animSpeed; ///< The speed of the event's animation SE::Type sound; ///< The sound played on the appropriate trigger signed char multiA; ///< Usage depends on event type signed char multiB; ///< Usage depends on event type signed char pieceSize; ///< Size of pieces in bridges, swinging balls chains, etc. signed char pieces; ///< Number of pieces in bridges, swinging ball chains, etc. signed char angle; ///< Initial angle of swinging balls, etc. } JJ1EventType; /// Pre-defined JJ1 event movement path typedef struct { short int* x; ///< X-coordinates for each node short int* y; ///< Y-coordinates for each node unsigned char length; ///< Number of nodes } JJ1EventPath; // Classes class Font; class JJ1Bullet; class JJ1Event; class JJ1LevelPlayer; /// JJ1 level class JJ1Level : public Level { private: SDL_Surface* tileSet; ///< Tile images SDL_Surface* panel; ///< HUD background image SDL_Surface* panelAmmo[6]; ///< HUD ammo type images JJ1Event* events; ///< Active events JJ1Bullet* bullets; ///< Active bullets char* sceneFile; ///< File name of cutscene to play when level has been completed Sprite* spriteSet; ///< Sprites Anim animSet[ANIMS]; ///< Animations char miscAnims[JJ1MANIMS]; ///< Further animations char playerAnims[JJ1PANIMS]; ///< Default player animations signed char bulletSet[BULLETS][BLENGTH]; ///< Bullet types JJ1EventType eventSet[EVENTS]; ///< Event types char mask[240][64]; ///< Tile masks. At most 240 tiles, all with 8 * 8 masks GridElement grid[LH][LW]; ///< Level grid. All levels are the same size SDL_Color skyPalette[MAX_PALETTE_COLORS]; ///< Full palette for sky background bool sky; ///< Whether or not to use sky background unsigned char skyOrb; ///< The tile to use as the background sun/moon/etc. int levelNum; ///< Number of current level int worldNum; ///< Number of current world int nextLevelNum; ///< Number of next level int nextWorldNum; ///< Number of next world int enemies; ///< Number of enemies to kill fixed waterLevel; ///< Height of water fixed waterLevelTarget; ///< Future height of water fixed waterLevelSpeed; ///< Rate of water level change fixed energyBar; ///< HUD energy bar fullness int ammoType; ///< HUD ammo type fixed ammoOffset; ///< HUD ammo offset void deletePanel (); int loadPanel (); void loadSprite (File* file, Sprite* sprite); int loadSprites (char* fileName); int loadTiles (char* fileName); int playBonus (); protected: Font* font; ///< On-screen message font char* musicFile; ///< Music file name explicit JJ1Level(Game* owner); int load (char* fileName, bool checkpoint); int step (); void draw (); public: JJ1EventPath path[PATHS]; ///< Pre-defined event movement paths JJ1Level (Game* owner, char* fileName, bool checkpoint, bool multi); ~JJ1Level (); bool checkMaskUp (fixed x, fixed y); bool checkMaskDown (fixed x, fixed y); bool checkSpikes (fixed x, fixed y); int getWorld (); void setNext (int nextLevel, int nextWorld); void setTile (unsigned char gridX, unsigned char gridY, unsigned char tile); JJ1Event* getEvents (); JJ1EventType* getEvent (unsigned char gridX, unsigned char gridY); unsigned char getEventHits (unsigned char gridX, unsigned char gridY); unsigned int getEventTime (unsigned char gridX, unsigned char gridY); void clearEvent (unsigned char gridX, unsigned char gridY); int hitEvent (unsigned char gridX, unsigned char gridY, int hits, JJ1LevelPlayer* source, unsigned int time); void setEventTime (unsigned char gridX, unsigned char gridY, unsigned int time); Sprite* getSprite (unsigned char sprite); Anim* getAnim (unsigned char anim); Anim* getMiscAnim (unsigned char anim); Anim* getPlayerAnim (unsigned char anim); void createBullet (JJ1LevelPlayer* sourcePlayer, unsigned char gridX, unsigned char gridY, fixed startX, fixed startY, unsigned char bullet, bool facing, unsigned int time); void setWaterLevel (unsigned char gridY); fixed getWaterLevel (); void flash (unsigned char red, unsigned char green, unsigned char blue, int duration); void receive (unsigned char* buffer); virtual int play (); }; /// JJ1 level played as a demo class JJ1DemoLevel : public JJ1Level { private: unsigned char* macro; ///< Sequence of player control codes public: JJ1DemoLevel (Game* owner, const char* fileName); ~JJ1DemoLevel (); int play (); }; // Variables EXTERN JJ1Level* level; ///< JJ1 level #endif openjazz-20240919/src/jj1/level/jj1levelframe.cpp000066400000000000000000000246001467303304200214470ustar00rootroot00000000000000 /** * * @file jj1levelframe.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Created levelframe.cpp from parts of level.cpp * - 30th March 2010: Created baselevel.cpp from parts of level.cpp and * levelframe.cpp * - 29th June 2010: Created jj2levelframe.cpp from parts of levelframe.cpp * - 1st August 2012: Renamed levelframe.cpp to jj1levelframe.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the once-per-frame functions for levels. * */ #include "jj1bullet.h" #include "event/jj1event.h" #include "event/jj1guardians.h" #include "jj1level.h" #include "jj1levelplayer.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "util.h" /** * Level iteration. * * @return Error code */ int JJ1Level::step () { JJ1Event *event; int viewH; int x, y; // Can we see below the panel? if (canvasW > SW) viewH = canvasH; else viewH = canvasH - 33; // Search for active events for (y = FTOT(viewY) - 5; y < ITOT(FTOI(viewY) + viewH) + 5; y++) { for (x = FTOT(viewX) - 5; x < ITOT(FTOI(viewX) + canvasW) + 5; x++) { if ((x >= 0) && (y >= 0) && (x < LW) && (y < LH) && grid[y][x].event && (grid[y][x].event < 121) && (eventSet[grid[y][x].event].difficulty <= game->getDifficulty())) { event = events; while (event) { // If the event has been found, stop searching if (event->isFrom(x, y)) break; event = event->getNext(); } // If the event wasn't found, create it if (!event) { switch (getEvent(x, y)->movement) { case 28: events = new JJ1Bridge(x, y); break; case 41: events = new MedGuardian(x, y); break; case 60: events = new DeckGuardian(x, y); break; default: events = new JJ1StandardEvent(eventSet + grid[y][x].event, x, y, TTOF(x), TTOF(y + 1)); break; } } } } } // Process bullets if (bullets) bullets = bullets->step(ticks); // Determine the players' trajectories for (x = 0; x < nPlayers; x++) players[x].getJJ1LevelPlayer()->control(ticks); // Process active events if (events) events = events->step(ticks); // Apply as much of those trajectories as possible, without going into the // scenery for (x = 0; x < nPlayers; x++) players[x].getJJ1LevelPlayer()->move(ticks); // Check if time has run out if (ticks > endTime) { if (multiplayer) { game->getMode()->outOfTime(); } else { if ((game->getDifficulty() >= 2) && (stage == LS_NORMAL)) localPlayer->getJJ1LevelPlayer()->kill(NULL, endTime); } } // Handle change in ammo selection x = localPlayer->getAmmoType() + 1; if (x != ammoType) { // Change the ammo type display on the panel ammoType = x; ammoOffset = ITOF(26); } if (ammoOffset > 0) { // Descending ammoOffset -= F1; // Avoid an offset of 0, which prevents changes if (ammoOffset == 0) ammoOffset = -1; } // Handle change in water level if (waterLevel < waterLevelTarget) waterLevelSpeed += 3200; else waterLevelSpeed -= 3200; if (waterLevelSpeed > 80000) waterLevelSpeed = 80000; if (waterLevelSpeed < -80000) waterLevelSpeed = -80000; waterLevel += waterLevelSpeed >> 6; // Handle player reactions for (x = 0; x < nPlayers; x++) { if (players[x].getJJ1LevelPlayer()->reacted(ticks) == PR_KILLED) { players[x].clearAmmo(); if (!multiplayer) return LOST; game->resetPlayer(players + x); } } return E_NONE; } /** * Draw the level. */ void JJ1Level::draw () { GridElement *ge; SDL_Rect src, dst; int viewH; int vX, vY; int x, y; unsigned int change; // Calculate change since last step change = getTimeChange(); // Calculate viewport if (game && (stage == LS_END)) game->view(paused? 0: ((ticks - prevTicks) * 160)); else localPlayer->getJJ1LevelPlayer()->view(ticks, paused? 0: (ticks - prevTicks), change); // Can we see below the panel? if (canvasW > SW) viewH = canvasH; else viewH = canvasH - 33; // Ensure the new viewport is within the level if (FTOI(viewX) + canvasW >= TTOI(LW)) viewX = ITOF(TTOI(LW) - canvasW); if (viewX < 0) viewX = 0; if (FTOI(viewY) + viewH >= TTOI(LH)) viewY = ITOF(TTOI(LH) - viewH); if (viewY < 0) viewY = 0; // Use the viewport dst.x = 0; dst.y = 0; vX = FTOI(viewX); vY = FTOI(viewY); dst.w = canvasW; dst.h = viewH; SDL_SetClipRect(canvas, &dst); // Set tile drawing dimensions src.w = TTOI(1); src.h = TTOI(1); src.x = 0; // If there is a sky, draw it if (sky) { // Background scale int bgScale; if (canvasW > 320) bgScale = ((canvasH - 1) / 100) + 1; else bgScale = ((canvasH - 34) / 100) + 1; for (y = 0; y < viewH; y += bgScale) drawRect(0, y, canvasW, bgScale, 156 + (y / bgScale)); // Show sun / moon / etc. if (skyOrb) { dst.x = ((canvasW * 4) / 5) - (vX & 3); dst.y = ((canvasH - 33) * 3) / 25; src.y = TTOI(skyOrb + (vX & 3)); SDL_BlitSurface(tileSet, &src, canvas, &dst); } } else { // If there is no sky, draw a blank background // This is only very occasionally actually visible video.clearScreen(127); } // Show background tiles for (y = 0; y <= ITOT(viewH - 1) + 1; y++) { for (x = 0; x <= ITOT(canvasW - 1) + 1; x++) { if ((x + ITOT(vX) >= 256) || (y + ITOT(vY) >= 64)) { drawRect(TTOI(x) - (vX & 31), TTOI(y) - (vY & 31), 32, 32, LEVEL_BLACK); continue; } // Get the grid element from the given coordinates ge = grid[y + ITOT(vY)] + x + ITOT(vX); // If this tile uses a black background, draw it if (ge->bg) drawRect(TTOI(x) - (vX & 31), TTOI(y) - (vY & 31), 32, 32, LEVEL_BLACK); // If this is not a foreground tile, draw it if ((ge->event != 124) && (ge->event != 125) && (eventSet[ge->event].movement != 37) && (eventSet[ge->event].movement != 38)) { dst.x = TTOI(x) - (vX & 31); dst.y = TTOI(y) - (vY & 31); src.y = TTOI(ge->tile); SDL_BlitSurface(tileSet, &src, canvas, &dst); } } } // Show active events if (events) events->draw(ticks, change); // Show the players for (x = 0; x < nPlayers; x++) players[x].getJJ1LevelPlayer()->draw(ticks, change); // Show bullets if (bullets) bullets->draw(change); // Show foreground tiles for (y = 0; y <= ITOT(viewH - 1) + 1; y++) { for (x = 0; x <= ITOT(canvasW - 1) + 1; x++) { if ((x + ITOT(vX) >= 256) || (y + ITOT(vY) >= 64)) continue; // Get the grid element from the given coordinates ge = grid[y + ITOT(vY)] + x + ITOT(vX); // If this is an "animated" foreground tile, draw it if (ge->event == 123) { dst.x = TTOI(x) - (vX & 31); dst.y = TTOI(y) - (vY & 31); if (ticks & 64) src.y = TTOI(eventSet[ge->event].multiB); else src.y = TTOI(eventSet[ge->event].multiA); SDL_BlitSurface(tileSet, &src, canvas, &dst); } // If this is a foreground tile, draw it if ((ge->event == 124) || (ge->event == 125) || (eventSet[ge->event].movement == 37) || (eventSet[ge->event].movement == 38)) { dst.x = TTOI(x) - (vX & 31); dst.y = TTOI(y) - (vY & 31); src.y = TTOI(ge->tile); SDL_BlitSurface(tileSet, &src, canvas, &dst); } } } // Temporary lines showing the water level drawRect(0, FTOI(waterLevel - viewY), canvasW, 2, 24); drawRect(0, FTOI(waterLevel - viewY) + 3, canvasW, 1, 24); drawRect(0, FTOI(waterLevel - viewY) + 6, canvasW, 1, 24); drawRect(0, FTOI(waterLevel - viewY) + 10, canvasW, 1, 24); // Show active guardian's energy bar if (events) events->drawEnergy(ticks); // If this is a competitive game, draw the score if (multiplayer) game->getMode()->drawScore(font); // Show panel SDL_SetClipRect(canvas, NULL); if (ammoOffset != 0) { if (ammoOffset < 0) { // Finished descending ammoOffset = 0; } src.x = 0; src.y = FTOI(ammoOffset); src.w = 64; src.h = 26 - src.y; dst.x = 248; dst.y = 3; SDL_BlitSurface(panelAmmo[ammoType], &src, panel, &dst); } dst.x = 0; dst.y = canvasH - 33; SDL_BlitSurface(panel, NULL, canvas, &dst); drawRect(0, canvasH - 1, SW, 1, LEVEL_BLACK); // Show panel data // Show score panelSmallFont->showNumber(localPlayer->getScore(), 84, canvasH - 27); // Show time remaining if (endTime > ticks) x = endTime - ticks; else x = 0; y = x / (60 * 1000); panelSmallFont->showNumber(y, 116, canvasH - 27); x -= (y * 60 * 1000); y = x / 1000; panelSmallFont->showNumber(y, 136, canvasH - 27); x -= (y * 1000); y = x / 100; panelSmallFont->showNumber(y, 148, canvasH - 27); // Show lives panelSmallFont->showNumber(localPlayer->getLives(), 124, canvasH - 13); // Show planet number if (worldNum <= 41) // Main game levels panelSmallFont->showNumber((worldNum % 3) + 1, 184, canvasH - 13); else if ((worldNum >= 50) && (worldNum <= 52)) // Christmas levels panelSmallFont->showNumber(worldNum - 49, 184, canvasH - 13); else panelSmallFont->showNumber(worldNum, 184, canvasH - 13); // Show level number panelSmallFont->showNumber(levelNum + 1, 196, canvasH - 13); // Show ammo if (localPlayer->getAmmoType() == -1) { panelSmallFont->showString(":", 225, canvasH - 13); panelSmallFont->showString(";", 233, canvasH - 13); } else { x = localPlayer->getAmmo(); // Trailing 0s if (x < 100) { panelSmallFont->showNumber(0, 229, canvasH - 13); if (x < 10) panelSmallFont->showNumber(0, 237, canvasH - 13); } panelSmallFont->showNumber(x > 999? 999: x, 245, canvasH - 13); } // Draw the health bar dst.x = 20; x = localPlayer->getJJ1LevelPlayer()->getEnergy(); y = (ticks - prevTicks) * 40; if (FTOI(energyBar) < (x << 4)) { if ((x << 14) - energyBar < y) energyBar = x << 14; else energyBar += y; } else if (FTOI(energyBar) > (x << 4)) { if (energyBar - (x << 14) < y) energyBar = x << 14; else energyBar -= y; } if (energyBar > F1) { dst.w = FTOI(energyBar) - 1; // Choose energy bar colour if (x == 4) x = 24; else if (x == 3) x = 17; else if (x == 2) x = 80; else if (x <= 1) x = 32 + (((ticks / 75) * 4) & 15); // Draw energy bar drawRect(dst.x, canvasH - 13, dst.w, 7, x); dst.x += dst.w; dst.w = 64 - dst.w; } else dst.w = 64; // Fill in remaining energy bar space with black drawRect(dst.x, canvasH - 13, dst.w, 7, LEVEL_BLACK); } openjazz-20240919/src/jj1/level/jj1levelload.cpp000066400000000000000000000507751467303304200213100ustar00rootroot00000000000000 /** * * @file jj1levelload.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed levelload.c to levelload.cpp * - 18th July 2009: Created demolevel.cpp from parts of level.cpp and * levelload.cpp * - 19th July 2009: Added parts of levelload.cpp to level.cpp * - 28th June 2010: Created levelloadjj2.cpp from parts of levelload.cpp * - 1st August 2012: Renamed levelload.cpp to jj1levelload.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading of level data. * */ #include "jj1bullet.h" #include "event/jj1event.h" #include "jj1level.h" #include "jj1levelplayer.h" #include "game/game.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "loop.h" #include "util.h" #include "io/log.h" #include #define SKEY 254 /* Sprite colour key */ /** * Load the HUD graphical data. * * @return Error code */ int JJ1Level::loadPanel () { File* file; unsigned char* pixels; unsigned char* sorted; int type, x, y; try { file = new File("PANEL.000", PATH_TYPE_GAME); } catch (int e) { return e; } pixels = file->loadRLE(46272); delete file; // Create the panel background panel = createSurface(pixels, SW, 32); // De-scramble the panel's ammo graphics sorted = new unsigned char[64 * 26]; for (type = 0; type < 6; type++) { for (y = 0; y < 26; y++) { for (x = 0; x < 64; x++) sorted[(y * 64) + x] = pixels[(type * 64 * 32) + (y * 64) + (x >> 2) + ((x & 3) << 4) + (55 * 320)]; } panelAmmo[type] = createSurface(sorted, 64, 26); } delete[] sorted; delete[] pixels; return E_NONE; } /** * Load a sprite. * * @param file File from which to load the sprite data * @param sprite Sprite that will receive the loaded data */ void JJ1Level::loadSprite (File* file, Sprite* sprite) { unsigned char* pixels; int pos, maskOffset; int width, height; // Load dimensions width = file->loadShort() << 2; height = file->loadShort(); file->seek(2, false); maskOffset = file->loadShort(); pos = file->loadShort() << 2; // Sprites can be either masked or not masked. if (maskOffset) { // Masked height++; // Skip to mask file->seek(maskOffset, false); // Find the end of the data pos += file->tell() + ((width >> 2) * height); // Read scrambled, masked pixel data pixels = file->loadPixels(width * height, SKEY); sprite->setPixels(pixels, width, height, SKEY); delete[] pixels; file->seek(pos, true); } else if (width) { // Not masked // Read scrambled pixel data pixels = file->loadPixels(width * height); sprite->setPixels(pixels, width, height, SKEY); delete[] pixels; } } /** * Load sprites. * * @param fileName Name of the file containing the level-specific sprites * * @return Error code */ int JJ1Level::loadSprites (char * fileName) { File* mainFile = NULL; File* specFile = NULL; unsigned char* buffer; int count; // Open fileName try { specFile = new File(fileName, PATH_TYPE_GAME); } catch (int e) { return e; } // This function loads all the sprites, not just those in fileName try { mainFile = new File("MAINCHAR.000", PATH_TYPE_GAME); } catch (int e) { delete specFile; return e; } sprites = specFile->loadShort(256); // Include space in the sprite set for the blank sprite at the end spriteSet = new Sprite[sprites + 1]; // Read offsets buffer = specFile->loadBlock(sprites * 2); for (count = 0; count < sprites; count++) spriteSet[count].setOffset(buffer[count] << 2, buffer[sprites + count]); delete[] buffer; // Skip to where the sprites start in mainchar.000 mainFile->seek(2, true); // Loop through all the sprites to be loaded for (count = 0; count < sprites; count++) { bool loaded = false; if (mainFile->loadChar() == 0xFF) { // Go to the next sprite/file indicator mainFile->seek(1, false); } else { // Return to the start of the sprite mainFile->seek(-1, false); // Load the individual sprite data loadSprite(mainFile, spriteSet + count); loaded = true; } if (specFile->loadChar() == 0xFF) { // Go to the next sprite/file indicator specFile->seek(1, false); } else { // Return to the start of the sprite specFile->seek(-1, false); // Load the individual sprite data loadSprite(specFile, spriteSet + count); loaded = true; } /* If both fileName and mainchar.000 have file indicators, create a blank sprite */ if (!loaded) spriteSet[count].clearPixels(); // Check if the next sprite exists // If not, create blank sprites for the remainder if (specFile->tell() >= specFile->getSize()) { for (count++; count < sprites; count++) { spriteSet[count].clearPixels(); } } } delete mainFile; delete specFile; // Include a blank sprite at the end spriteSet[sprites].clearPixels(); return E_NONE; } /** * Load the tileset. * * @param fileName Name of the file containing the tileset * * @return The number of tiles loaded */ int JJ1Level::loadTiles (char* fileName) { File* file; try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { return e; } // Load the palette file->loadPalette(palette); // Load the background palette file->loadPalette(skyPalette); // Skip the second, identical, background palette file->skipRLE(); // Load the tile pixel indices int tiles = 240; // Never more than 240 tiles unsigned char* buffer = new unsigned char[tiles << 10]; file->seek(4, false); int pos = 0; int fileSize = file->getSize(); // Read the RLE pixels // file::loadRLE() cannot be used, for reasons that will become clear while ((pos < (tiles << 10)) && (file->tell() < fileSize)) { int rle = file->loadChar(); if (rle & 128) { int index = file->loadChar(); for (int i = 0; i < (rle & 127); i++) buffer[pos++] = index; } else if (rle) { for (int i = 0; i < rle; i++) buffer[pos++] = file->loadChar(); } else { // This happens at the end of each tile // 0 pixels means 1 pixel, apparently buffer[pos++] = file->loadChar(); file->seek(2, false); /* I assume this is the length of the next tile block */ if (pos == (60 << 10)) file->seek(2, false); if (pos == (120 << 10)) file->seek(2, false); if (pos == (180 << 10)) file->seek(2, false); } } delete file; // Work out how many tiles were actually loaded // Should be a multiple of 60 tiles = pos >> 10; tileSet = createSurface(buffer, TTOI(1), TTOI(tiles)); enableColorKey(tileSet, TKEY); delete[] buffer; return tiles; } /** * Load the level. * * @param fileName Name of the file containing the level data * @param checkpoint Whether or not the player(s) will start at a checkpoint * * @return Error code */ int JJ1Level::load (char* fileName, bool checkpoint) { Anim* pAnims[JJ1PANIMS]; unsigned short int soundRates[32]; File* file; unsigned char* buffer; const char* ext; char* string = NULL; char* levelname = NULL; int tiles; int count, x, y, type; unsigned char startX, startY; // FIXME: actually use these int animSpeed, jumpHeight; // Load font try { font = new Font(false); } catch (int e) { return e; } // Load panel count = loadPanel(); if (count < 0) { delete font; return count; } // Show loading screen // Open planet.### file if (!strcmp(fileName, LEVEL_FILE)) { // Using the downloaded level file string = createString("DOWNLOADED"); } else { // Load the planet's name from the planet.### file string = createFileName("PLANET", fileName + strlen(fileName) - 3); try { file = new File(string, PATH_TYPE_GAME); } catch (int e) { file = NULL; } delete[] string; if (file) { file->seek(2, true); string = file->loadString(); delete file; } else { string = createString("CUSTOM"); } } levelname = new char[strlen(string) + 14]; strcpy(levelname, string); switch (fileName[5]) { case '0': ext = " LEVEL ONE"; break; case '1': ext = " LEVEL TWO"; break; case '2': string[0] = 0; ext = "SECRET LEVEL"; strcat(levelname, " "); break; default: ext = " LEVEL"; break; } strcat(levelname, ext); camelcaseString(levelname); video.setPalette(menuPalette); video.clearScreen(0); video.setTitle(levelname); delete[] levelname; x = (canvasW >> 1) - ((strlen(string) + strlen(ext)) << 2); x = fontmn2->showString("LOADING ", x - 60, (canvasH >> 1) - 16); x = fontmn2->showString(string, x, (canvasH >> 1) - 16); fontmn2->showString(ext, x, (canvasH >> 1) - 16); delete[] string; if (::loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; // Open level file try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { deletePanel(); delete font; return e; } // Load the blocks.### extension // Skip past all level data file->seek(39, true); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->seek(598, false); file->skipRLE(); file->seek(4, false); file->skipRLE(); file->skipRLE(); file->seek(25, false); file->skipRLE(); file->seek(3, false); // Load the level number levelNum = file->loadChar() ^ 210; // Load the world number worldNum = file->loadChar() ^ 4; // Load tile set from appropriate blocks.### // Load tile set extension file->seek(8, false); ext = file->loadString(); // Create tile set file name if (!strcmp(ext, "999")) string = createFileName("BLOCKS", worldNum); else string = createFileName("BLOCKS", ext); delete[] ext; tiles = loadTiles(string); delete[] string; if (tiles < 0) { delete file; deletePanel(); delete font; return tiles; } // Load sprite set from corresponding Sprites.### string = createFileName("SPRITES", worldNum); count = loadSprites(string); delete[] string; if (count < 0) { SDL_FreeSurface(tileSet); delete file; deletePanel(); delete font; return count; } // Skip to tile and event reference data file->seek(39, true); // Load tile and event references buffer = file->loadRLE(LW * LH * 2); // Create grid from data for (x = 0; x < LW; x++) { for (y = 0; y < LH; y++) { grid[y][x].tile = buffer[(y + (x * LH)) << 1]; grid[y][x].bg = buffer[((y + (x * LH)) << 1) + 1] >> 7; grid[y][x].event = buffer[((y + (x * LH)) << 1) + 1] & 127; grid[y][x].hits = 0; grid[y][x].time = 0; } } delete[] buffer; // Ignore tile transparency settings (FIXME: needed for sun tiles at least) file->skipRLE(); // Load mask data buffer = file->loadRLE(tiles * 8); // Unpack bits for (count = 0; count < tiles; count++) { for (y = 0; y < 8; y++) { for (x = 0; x < 8; x++) mask[count][(y << 3) + x] = (buffer[(count << 3) + y] >> x) & 1; } } delete[] buffer; /* Uncomment the code below if you want to see the mask instead of the tile graphics during gameplay */ /*if (SDL_MUSTLOCK(tileSet)) SDL_LockSurface(tileSet); for (count = 0; count < tiles; count++) { for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) { if (mask[count][((y >> 2) << 3) + (x >> 2)] == 1) ((char *)(tileSet->pixels)) [(count * 1024) + (y * 32) + x] = 88; } } } if (SDL_MUSTLOCK(tileSet)) SDL_UnlockSurface(tileSet);*/ // Load special event path buffer = file->loadRLE(PATHS << 9); for (type = 0; type < PATHS; type++) { path[type].length = buffer[type << 9] + (buffer[(type << 9) + 1] << 8); if (path[type].length < 1) path[type].length = 1; path[type].x = new short int[path[type].length]; path[type].y = new short int[path[type].length]; for (count = 0; count < path[type].length; count++) { path[type].x[count] = reinterpret_cast(buffer)[(type << 9) + (count << 1) + 3] << 2; path[type].y[count] = reinterpret_cast(buffer)[(type << 9) + (count << 1) + 2]; } } delete[] buffer; // Load event set buffer = file->loadRLE(EVENTS * ELENGTH); // Fill event set with data for (count = 0; count < EVENTS; count++) { eventSet[count].difficulty = buffer[count * ELENGTH]; eventSet[count].reflection = buffer[(count * ELENGTH) + 2]; eventSet[count].movement = buffer[(count * ELENGTH) + 4]; eventSet[count].anims[E_LEFTANIM] = buffer[(count * ELENGTH) + 5]; eventSet[count].anims[E_RIGHTANIM] = buffer[(count * ELENGTH) + 6]; eventSet[count].magnitude = buffer[(count * ELENGTH) + 8]; eventSet[count].strength = buffer[(count * ELENGTH) + 9]; eventSet[count].modifier = buffer[(count * ELENGTH) + 10]; eventSet[count].points = buffer[(count * ELENGTH) + 11]; eventSet[count].bullet = buffer[(count * ELENGTH) + 12]; eventSet[count].bulletPeriod = buffer[(count * ELENGTH) + 13]; eventSet[count].speed = buffer[(count * ELENGTH) + 15] + 1; eventSet[count].animSpeed = buffer[(count * ELENGTH) + 17] + 1; auto se = static_cast(buffer[(count * ELENGTH) + 21]); if (!isValidSoundIndex(se)) { eventSet[count].sound = SE::NONE; LOG_WARN("Event %d has invalid sound effect %d.", count, se); } else { eventSet[count].sound = se; } eventSet[count].multiA = buffer[(count * ELENGTH) + 22]; eventSet[count].multiB = buffer[(count * ELENGTH) + 23]; eventSet[count].pieceSize = buffer[(count * ELENGTH) + 24]; eventSet[count].pieces = buffer[(count * ELENGTH) + 25]; eventSet[count].angle = buffer[(count * ELENGTH) + 26]; eventSet[count].anims[E_LFINISHANIM] = buffer[(count * ELENGTH) + 28]; eventSet[count].anims[E_RFINISHANIM] = buffer[(count * ELENGTH) + 29]; eventSet[count].anims[E_LSHOOTANIM] = buffer[(count * ELENGTH) + 30]; eventSet[count].anims[E_RSHOOTANIM] = buffer[(count * ELENGTH) + 31]; } // Process grid enemies = items = 0; for (x = 0; x < LW; x++) { for (y = 0; y < LH; y++) { type = grid[y][x].event; if (type) { // If the event hurts and can be killed, it is an enemy // Anything else that scores is an item if ((eventSet[type].modifier == 0) && eventSet[type].strength) enemies++; else if (eventSet[type].points) items++; } } } delete[] buffer; // Skip (usually empty) event names file->skipRLE(); // Load animation set buffer = file->loadRLE(ANIMS << 6); // Create animation set based on that data for (count = 0; count < ANIMS; count++) { animSet[count].setData(buffer[(count << 6) + 6], buffer[count << 6], buffer[(count << 6) + 1], buffer[(count << 6) + 3], buffer[(count << 6) + 4], buffer[(count << 6) + 2], buffer[(count << 6) + 5]); for (y = 0; y < buffer[(count << 6) + 6]; y++) { // Get frame x = buffer[(count << 6) + 7 + y]; if (x > sprites) x = sprites; // Assign sprite and vertical offset animSet[count].setFrame(y, true); animSet[count].setFrameData(spriteSet + x, buffer[(count << 6) + 26 + y], buffer[(count << 6) + 45 + y]); } } delete[] buffer; // Skip (usually empty) animation names file->skipRLE(); // Skip level block names file->seek(153, false); // Load sound map for (count = 0; count < 32; count++) soundRates[count] = file->loadShort(); x = file->tell(); for (count = 0; count < 32; count++) { file->seek(x + (count * 9), true); string = file->loadString(); resampleSound(count, string, soundRates[count]); delete[] string; } file->seek(x + 288, true); // Music file musicFile = file->loadString(); // Skip (usually empty) level start cutscene file->seek(x + 314, true); // End of episode cutscene sceneFile = file->loadString(); // Skip blank bytes file->seek(x + 366, true); // The players' initial coordinates startX = file->loadShort(LW); startY = file->loadShort(LH) + 1; // Next level x = file->loadChar(); y = file->loadChar(); setNext(x, y); // jump height jumpHeight = (file->loadShort() - 0xFFFF) / 2; if (jumpHeight != -5) LOG_TRACE("Uncommon jumpHeight: %i", jumpHeight); // skip some unknown level file->seek(2, false); // Thanks to Doubble Dutch for the water level bytes waterLevelTarget = ITOF(file->loadShort() + 17); waterLevel = waterLevelTarget - F8; waterLevelSpeed = -80000; // Jazz animation speed animSpeed = file->loadChar(); if (animSpeed != 119) LOG_TRACE("Uncommon animationSpeed: %i", animSpeed); // Skip an unknown value (end marker?) file->seek(2, false); // Thanks to Feline and the JCS94 team for the next bits: // Load player's animation set references (always left + right) buffer = file->loadRLE(JJ1PANIMS * 2); string = new char[MTL_P_ANIMS + JJ1PANIMS]; for (x = 0; x < JJ1PANIMS; x++) { playerAnims[x] = buffer[x << 1]; pAnims[x] = animSet + playerAnims[x]; string[MTL_P_ANIMS + x] = playerAnims[x]; } delete[] buffer; if (multiplayer) { string[0] = MTL_P_ANIMS + JJ1PANIMS; string[1] = MT_P_ANIMS; string[2] = 0; game->send(reinterpret_cast(string)); } delete[] string; createLevelPlayers(LT_JJ1, pAnims, NULL, checkpoint, startX, startY); // Load miscellaneous animations miscAnims[MA_SPARKLE] = file->loadChar(); miscAnims[MA_DEVHEAD] = file->loadChar(); miscAnims[MA_EXPLOSION1] = file->loadChar(); miscAnims[MA_EXPLOSION2] = file->loadChar(); // Load bullet set buffer = file->loadRLE(BULLETS * BLENGTH); for (count = 0; count < BULLETS; count++) { memcpy(bulletSet[count], buffer + (count * BLENGTH), BLENGTH); } delete[] buffer; // Skip (usually empty) attack names file->skipRLE(); // Load level properties (magic) // First byte is the background palette effect type type = file->loadChar(); sky = false; switch (type) { case PE_SKY: sky = true; // Sky background effect paletteEffects = new SkyPaletteEffect(156, 100, FH, skyPalette, NULL); break; case PE_2D: // Parallaxing background effect paletteEffects = new P2DPaletteEffect(128, 64, FE, NULL); break; case PE_1D: // Diagonal stripes "parallaxing" background effect paletteEffects = new P1DPaletteEffect(128, 32, FH, NULL); break; case PE_WATER: // The deeper below water, the darker it gets paletteEffects = new WaterPaletteEffect(TTOF(32), NULL); break; default: // No effect paletteEffects = NULL; LOG_TRACE("Unknown palette effect: %d", type); break; } // Palette animations // These are applied to every level without a conflicting background effect // As a result, there are a few levels with things animated that shouldn't // be // In Diamondus: The red/yellow palette animation paletteEffects = new RotatePaletteEffect(112, 4, F32, paletteEffects); // In Diamondus: The waterfall palette animation paletteEffects = new RotatePaletteEffect(116, 8, F16, paletteEffects); // The following were discoverd by Unknown/Violet paletteEffects = new RotatePaletteEffect(124, 3, F16, paletteEffects); if ((type != PE_1D) && (type != PE_2D)) paletteEffects = new RotatePaletteEffect(132, 8, F16, paletteEffects); if ((type != PE_SKY) && (type != PE_2D)) paletteEffects = new RotatePaletteEffect(160, 32, -F16, paletteEffects); if (type != PE_SKY) { paletteEffects = new RotatePaletteEffect(192, 32, -F32, paletteEffects); paletteEffects = new RotatePaletteEffect(224, 16, F16, paletteEffects); } // Level fade-in/white-in effect if (checkpoint) paletteEffects = new FadeInPaletteEffect(T_START, paletteEffects); else paletteEffects = new WhiteInPaletteEffect(T_START, paletteEffects); // Check if a sun/star/distant planet, etc. is visible skyOrb = file->loadChar(); // If so, find out which tile it uses or skip it if (skyOrb) skyOrb = file->loadChar(); else file->loadChar(); // Skip some sound effects and empty animations file->seek(14, false); // 4 shield gem miscAnims[MA_4SHIELD] = file->loadChar(); // Board miscAnims[MA_LBOARD] = file->loadChar(); miscAnims[MA_RBOARD] = file->loadChar(); // Bird miscAnims[MA_LBIRD] = file->loadChar(); miscAnims[MA_RBIRD] = file->loadChar(); // Skip unknown animation file->seek(1, false); // Shiver and slide: 2, only shiver: 1 miscAnims[MA_ICY] = file->loadChar(); // 1 shield gem miscAnims[MA_1SHIELD] = file->loadChar(); // And that's us done! delete file; // Set the tick at which the level will end endTime = (5 - game->getDifficulty()) * 2 * 60 * 1000; events = NULL; bullets = NULL; energyBar = 0; ammoType = 0; ammoOffset = -1; return E_NONE; } openjazz-20240919/src/jj1/level/jj1levelplayer.cpp000066400000000000000000000372701467303304200216600ustar00rootroot00000000000000 /** * * @file jj1levelplayer.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 24th June 2010: Created levelplayer.cpp from parts of player.cpp * - 29th June 2010: Created jj2levelplayer.cpp from parts of levelplayer.cpp * - 1st August 2012: Renamed levelplayer.cpp to jj1levelplayer.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creation and destruction of players in levels, and their * interactions with other level objects. * */ #include "event/jj1event.h" #include "jj1level.h" #include "jj1bird.h" #include "jj1levelplayer.h" #include "game/game.h" #include "io/sound.h" #include "setup.h" #include /** * Create a JJ1 level player. * * @param parent The game player corresponding to this level player. * @param newAnims Animations * @param startX Starting position x-coordinate * @param startY Starting position y-coordinate * @param flockSize The number of birds accompanying the player */ JJ1LevelPlayer::JJ1LevelPlayer (Player* parent, Anim** newAnims, unsigned char startX, unsigned char startY, int flockSize) { const int offsets[15] = {PCO_GREY, PCO_SGREEN, PCO_BLUE, PCO_RED, PCO_LGREEN, PCO_LEVEL1, PCO_YELLOW, PCO_LEVEL2, PCO_ORANGE, PCO_LEVEL3, PCO_LEVEL4, PCO_SANIM, PCO_LANIM, PCO_LEVEL5, 256}; int count, start, length; player = parent; memcpy(anims, newAnims, JJ1PANIMS * sizeof(Anim *)); birds = NULL; for (count = 0; count < flockSize; count++) birds = new JJ1Bird(birds, this, startX, startY - 2); reset(startX, startY); // Create the player's palette for (count = 0; count < MAX_PALETTE_COLORS; count++) palette[count].r = palette[count].g = palette[count].b = count; // Fur colours start = offsets[player->cols[0]]; length = offsets[player->cols[0] + 1] - start; for (count = 0; count < 16; count++) palette[count + 48].r = palette[count + 48].g = palette[count + 48].b = (count * length / 16) + start; // Bandana colours start = offsets[player->cols[1]]; length = offsets[player->cols[1] + 1] - start; for (count = 0; count < 16; count++) palette[count + 32].r = palette[count + 32].g = palette[count + 32].b = (count * length / 16) + start; // Gun colours start = offsets[player->cols[2]]; length = offsets[player->cols[2] + 1] - start; for (count = 0; count < 9; count++) palette[count + 23].r = palette[count + 23].g = palette[count + 23].b = (count * length / 9) + start; // Wristband colours start = offsets[player->cols[3]]; length = offsets[player->cols[3] + 1] - start; for (count = 0; count < 8; count++) palette[count + 88].r = palette[count + 88].g = palette[count + 88].b = (count * length / 8) + start; } /** * Delete the JJ1 level player. */ JJ1LevelPlayer::~JJ1LevelPlayer () { if (birds) delete birds; } /** * Reset the player's position, energy etc. * * @param startX New x-coordinate * @param startY New y-coordinate */ void JJ1LevelPlayer::reset (int startX, int startY) { x = TTOF(startX); y = TTOF(startY); dx = 0; dy = 0; eventType = JJ1PE_NONE; energy = 4; flying = false; facing = true; udx = 0; animType = PA_RSTAND; reaction = PR_NONE; reactionTime = 0; jumpHeight = PYO_JUMP; targetY = TTOF(LH); fastFeetTime = warpTime = fireTime = fireAnimTime = 0; eventX = eventY = warpX = warpY = lookTime = 0; shield = 0; enemies = items = 0; gem = false; } /** * Add to the player's item tally. */ void JJ1LevelPlayer::addItem () { items++; } /** * If the player is tied to the event from the given tile, untie it. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile */ void JJ1LevelPlayer::clearEvent (unsigned char gridX, unsigned char gridY) { // If the location matches, clear the event if ((gridX == eventX) && (gridY == eventY)) eventType = JJ1PE_NONE; } /** * Determine the player's current animation. * * @return The current animation */ Anim* JJ1LevelPlayer::getAnim () { return anims[animType]; } /** * Determine the number of enemies the player has killed. * * @return Number of enemies killed */ int JJ1LevelPlayer::getEnemies () { return enemies; } /** * Determine the player's current energy level. * * @return Energy level */ int JJ1LevelPlayer::getEnergy () { return energy; } /** * Determine the direction the player is facing. * * @return True if the player is facing right */ bool JJ1LevelPlayer::getFacing () { return facing; } /** * Get the horizontal speed of the player. * * @return The player's horizontal speed */ fixed JJ1LevelPlayer::getXSpeed () { return dx; } /** * Determine the number of items the player has collected. * * @return Number of items collected */ int JJ1LevelPlayer::getItems () { return items; } /** * Determine whether or not the player is being accompanied by a bird. * * @return Whether or not the player is being accompanied by a bird */ int JJ1LevelPlayer::countBirds () { if (birds) return birds->getFlockSize(); return 0; } /** * Determine whether or not the player has collected a gem. * * @return Whether or not the player has collected a gem */ bool JJ1LevelPlayer::hasGem () { return gem; } /** * Deal with bullet collisions. * * @param source Player that fired the bullet (NULL if an event) * @param ticks Time * * @return Whether or not the hit was successful */ bool JJ1LevelPlayer::hit (Player *source, unsigned int ticks) { // Invulnerable if reacting to e.g. having been hit if (reaction != PR_NONE) return false; // Hits from the same team have no effect if (source && (source->getTeam() == player->team)) return false; // Hits to the shield only affect the shield if (shield) { if (shield == 2) shield = 0; else shield--; reaction = PR_SHIELDED; reactionTime = ticks + PRT_SHIELDED; return true; } // Hits only cause damage in applicable game modes if (player->hit(source)) { // Hits don't cause damage with a bird, but do scare the bird away if (birds) birds->hit(); else energy--; } playSound(SE::UPLOOP); if (energy) { reaction = PR_HURT; reactionTime = ticks + PRT_HURT; if (dx < 0) dx = PXS_RUN; else dx = -PXS_RUN; dy = PYS_JUMP; } else { kill(source, ticks); } return true; } /** * Kill the player. * * @param source Player responsible for the kill (NULL if due to an event or time) * @param ticks time */ void JJ1LevelPlayer::kill (Player *source, unsigned int ticks) { if (reaction != PR_NONE) return; if (player->kill(source)) { energy = 0; player->lives--; reaction = PR_KILLED; reactionTime = ticks + PRT_KILLED; } } /** * Determine whether or not the player is overlapping the given area. * * @param left The x-coordinate of the left of the area * @param top The y-coordinate of the top of the area * @param width The width of the area * @param height The height of the area * * @return Whether or not there is an overlap */ bool JJ1LevelPlayer::overlap (fixed left, fixed top, fixed width, fixed height) { return (x + PXO_R >= left) && (x + PXO_L < left + width) && (y >= top) && (y + PYO_TOP < top + height); } /** * Handle the player's reaction. * * @param ticks Time * * @return The reaction the player has just finished */ JJ1PlayerReaction JJ1LevelPlayer::reacted (unsigned int ticks) { if ((reaction != PR_NONE) && (reactionTime < ticks)) { JJ1PlayerReaction oldReaction = reaction; reaction = PR_NONE; return oldReaction; } return PR_NONE; } /** * Tie the player to the platform from the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param shiftX Change in horizontal position * @param newY New vertical position */ void JJ1LevelPlayer::setPlatform (unsigned char gridX, unsigned char gridY, fixed shiftX, fixed newY) { eventType = JJ1PE_PLATFORM; eventX = gridX; eventY = gridY; if (((shiftX < 0) && !level->checkMaskUp(x + PXO_L + shiftX, y + PYO_MID)) || ((shiftX > 0) && !level->checkMaskUp(x + PXO_R + shiftX, y + PYO_MID))) { x += shiftX; } y = newY; } /** * Take the event from the given tile. * * @param event Event type * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param ticks Time * * @return Whether or not the event should be destroyed. */ bool JJ1LevelPlayer::takeEvent (JJ1EventType* event, unsigned char gridX, unsigned char gridY, unsigned int ticks) { switch (event->modifier) { case 41: // Bonus level if (energy) level->setNext(event->multiA, event->multiB); // FALLTHROUGH case 8: // Boss case 27: // End of level if (!energy) return false; if (!player->endOfLevel(gridX, gridY)) return false; level->setStage(LS_END); break; case 0: // Enemy break; case 1: // Invincibility if (!energy) return false; reaction = PR_INVINCIBLE; reactionTime = ticks + PRT_INVINCIBLE; break; case 2: case 3: // Health if ((energy == 4) && setup.leaveUnneeded) return false; if (energy < 4) energy++; break; case 4: // Extra life player->addLife(); break; case 5: // High-jump feet jumpHeight += F16; break; case 7: // Used with destructible blocks break; case 9: // Sand timer level->addTimer(2 * 60); break; case 10: // Checkpoint player->setCheckpoint(gridX, gridY); break; case 11: // Item break; case 12: // Rapid fire player->fireSpeed++; fireTime = 0; break; case 15: // Ammo player->addAmmo(0, 15); break; case 16: // Ammo player->addAmmo(1, 15); break; case 17: // Ammo player->addAmmo(2, 15); break; case 18: // Ammo player->addAmmo(0, 2); break; case 19: // Ammo player->addAmmo(1, 2); break; case 20: // Ammo player->addAmmo(2, 2); break; case 26: // Fast feet box fastFeetTime = ticks + T_FASTFEET; // Sky blue flash level->flash(135, 206, 235, 320); // Speed up music setMusicTempo(MusicTempo::FAST); break; case 30: // TNT player->addAmmo(4, 1); break; case 31: // Water level level->setWaterLevel(gridY + 1); break; case 33: // 1-hit shield if ((shield >= 1) && setup.leaveUnneeded) return false; else shield = 1; break; case 34: // Bird if (birds && !setup.manyBirds) return false; birds = new JJ1Bird(birds, this, gridX, gridY); break; case 35: // Airboard, etc. if (flying && setup.leaveUnneeded) return false; flying = true; break; case 36: // 4-hit shield if ((shield == 5) && setup.leaveUnneeded) return false; shield = 5; break; case 37: // Diamond if (gem && setup.leaveUnneeded) return false; gem = true; // Yellow flash level->flash(255, 255, 0, 320); break; case 39: // Ammo player->addAmmo(3, 15); break; case 40: // Ammo player->addAmmo(3, 2); break; default: return false; } player->addScore(event->points * 10); // Add to player's enemy/item tally // If the event hurts and can be killed, it is an enemy // Anything else that scores is an item if ((event->modifier == 0) && event->strength) enemies++; else if (event->points) items++; return true; } /** * Called when the player has touched the event from the given tile. * * @param event Event type * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param ticks Time * * @return Whether or not the event should be destroyed. */ bool JJ1LevelPlayer::touchEvent (JJ1EventType* event, unsigned char gridX, unsigned char gridY, unsigned int ticks) { if ((event->movement == 37) || (event->movement == 38)) { // Repel if (event->multiB) { if (event->multiA > 0) { udx = (event->magnitude < 0 ? -ITOF(240): ITOF(240)); eventType = JJ1PE_REPELUP; targetY = TTOF(gridY) - ITOF(event->multiA * 3); } else { eventType = JJ1PE_REPELDOWN; } eventX = gridX; eventY = gridY; dy = event->multiA * -F24; } else { eventType = JJ1PE_REPELH; eventX = gridX; eventY = gridY; } } switch (event->modifier) { case 0: // Hurt case 8: // Boss if ((event->movement < 37) || (event->movement > 44)) { if (reaction != PR_SHIELDED) hit(NULL, ticks); if (reaction == PR_SHIELDED) { level->hitEvent(gridX, gridY, 255, this, ticks); return true; } } break; case 7: // Used with destructible blocks, but should not destroy on contact break; case 13: // Warp if (!warpTime) { warpX = event->multiA; warpY = event->multiB; warpTime = ticks + T_WARP; // White flash level->flash(255, 255, 255, T_WARP); } break; case 28: // Belt if (event->magnitude < 0) { if (!level->checkMaskDown(x + PXO_L + (event->magnitude * 64), y + PYO_MID)) x += event->magnitude * 64; } else { if (!level->checkMaskDown(x + PXO_R + (event->magnitude * 64), y + PYO_MID)) x += event->magnitude * 64; } break; case 29: // Upwards spring eventType = JJ1PE_SPRING; eventX = gridX; eventY = gridY; targetY = TTOF(gridY) + (event->magnitude * ITOF(21)); playSound(event->sound); break; case 31: // Water level level->setWaterLevel(gridY + 1); break; case 32: // Float up / sideways if (event->multiB) { eventType = JJ1PE_FLOAT; eventX = gridX; eventY = gridY; targetY = TTOF(gridY) - (event->multiA * ITOF(17)); } else { eventType = JJ1PE_FLOATH; eventX = gridX; eventY = gridY; } break; case 38: // Airboard, etc. off flying = false; break; default: if (!event->strength) return takeEvent(event, gridX, gridY, ticks); break; } return false; } /** * Fill a buffer with player data. * * @param buffer The buffer */ void JJ1LevelPlayer::send (unsigned char *buffer) { // Copy data to be sent to clients/server buffer[9] = countBirds(); buffer[23] = energy; buffer[25] = shield; buffer[26] = flying; buffer[27] = getFacing(); buffer[29] = jumpHeight >> 24; buffer[30] = (jumpHeight >> 16) & 255; buffer[31] = (jumpHeight >> 8) & 255; buffer[32] = jumpHeight & 255; buffer[33] = targetY >> 24; buffer[34] = (targetY >> 16) & 255; buffer[35] = (targetY >> 8) & 255; buffer[36] = targetY & 255; buffer[37] = x >> 24; buffer[38] = (x >> 16) & 255; buffer[39] = (x >> 8) & 255; buffer[40] = x & 255; buffer[41] = y >> 24; buffer[42] = (y >> 16) & 255; buffer[43] = (y >> 8) & 255; buffer[44] = y & 255; } /** * Adjust player data based on the contents of a given buffer. * * @param buffer The buffer */ void JJ1LevelPlayer::receive (unsigned char *buffer) { int count; // Interpret data received from client/server switch (buffer[1]) { case MT_P_ANIMS: for (count = 0; count < JJ1PANIMS; count++) anims[count] = level->getAnim(buffer[MTL_P_ANIMS + count]); break; case MT_P_TEMP: if ((buffer[9] > 0) && (birds == NULL)) { birds = new JJ1Bird(birds, this, FTOT(x), FTOT(y) - 2); } if (birds) { birds = birds->setFlockSize(buffer[9]); } energy = buffer[23]; shield = buffer[25]; flying = buffer[26]; facing = buffer[27]; jumpHeight = (buffer[29] << 24) + (buffer[30] << 16) + (buffer[31] << 8) + buffer[32]; targetY = (buffer[33] << 24) + (buffer[34] << 16) + (buffer[35] << 8) + buffer[36]; x = (buffer[37] << 24) + (buffer[38] << 16) + (buffer[39] << 8) + buffer[40]; y = (buffer[41] << 24) + (buffer[42] << 16) + (buffer[43] << 8) + buffer[44]; break; } } openjazz-20240919/src/jj1/level/jj1levelplayer.h000066400000000000000000000144321467303304200213200ustar00rootroot00000000000000 /** * * @file jj1levelplayer.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created player.h from parts of OpenJazz.h * - 24th June 2010: Created levelplayer.h from parts of player.h * - 29th June 2010: Created jj2levelplayer.h from parts of levelplayer.h * - 1st August 2012: Renamed levelplayer.h to jj1levelplayer.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ /* "Tile" is a flexible term. Here it is used to refer specifically to the individual elements of the tile set. "Tiles" in the context of level units are referred to as grid elements. */ #ifndef _JJ1LEVELPLAYER_H #define _JJ1LEVELPLAYER_H #include "jj1level.h" #include "level/levelplayer.h" #include "player/player.h" #include "OpenJazz.h" // Constants // Colour offsets #define PCO_GREY 0 #define PCO_SGREEN 16 #define PCO_BLUE 23 #define PCO_RED 32 #define PCO_LGREEN 48 #define PCO_LEVEL1 64 #define PCO_YELLOW 75 #define PCO_LEVEL2 80 #define PCO_ORANGE 88 #define PCO_LEVEL3 96 #define PCO_LEVEL4 104 #define PCO_SANIM 112 #define PCO_LANIM 116 #define PCO_LEVEL5 124 // Player reaction times #define PRT_HURT 1000 #define PRT_HURTANIM 200 #define PRT_KILLED 2000 #define PRT_INVINCIBLE 10000 #define PRT_SHIELDED 100 // Other time periods #define T_FASTFEET 25000 #define T_WARP 1000 #define T_FIRING 150 // Player offsets #define PXO_MID F16 #define PXO_L (PXO_MID - F12) #define PXO_ML (PXO_MID - F8) #define PXO_MR (PXO_MID + F8) #define PXO_R (PXO_MID + F12) #define PYO_TOP (-F20) #define PYO_MID (-F10) #define PYO_JUMP ITOF(84) // Player speeds #define PXS_WALK ITOF(150) #define PXS_RUN ITOF(325) #define PYS_FALL ITOF(350) #define PYS_SINK ITOF(150) #define PYS_JUMP -ITOF(350) // Player accelerations #define PXA_REVERSE ITOF(28) #define PXA_STOP ITOF(16) #define PXA_WALK ITOF(16) #define PXA_RUN ITOF(16) #define PYA_GRAVITY ITOF(36) #define PYA_SINK ITOF(16) // Enums /// JJ1 player event types enum JJ1PlayerEvent { JJ1PE_NONE, ///< No event JJ1PE_SPRING, ///< Spring JJ1PE_FLOAT, ///< Float up JJ1PE_FLOATH, ///< Float horizontally JJ1PE_REPELH, ///< Repel horizontally only JJ1PE_REPELUP, ///< Repel upwards JJ1PE_REPELDOWN, ///< Repel downwards JJ1PE_PLATFORM ///< Moving platform }; /// JJ1 player reaction type enum JJ1PlayerReaction { PR_NONE, ///< Not reacting PR_HURT, ///< Hurt PR_KILLED, ///< Killed PR_INVINCIBLE, ///< Invincibility PR_SHIELDED ///< Hit to shield }; // Classes class Anim; class JJ1Bird; /// JJ1 level player class JJ1LevelPlayer final : public LevelPlayer { private: JJ1Bird* birds; ///< Bird companion(s) Anim* anims[JJ1PANIMS]; ///< Animations int energy; ///< 0 = dead, 4 = maximum int shield; ///< 0 = none, 1 = yellow, 2 = 1 orange, 3 = 2 orange, 4 = 3 orange, 5 = 4 orange bool flying; ///< false = normal, true = boarding/bird/etc. bool facing; ///< false = left, true = right fixed udx; ///< Unobstructed horizontal speed unsigned char animType; ///< Current animation unsigned char eventX; ///< X-coordinate (in tiles) of an event (spring, platform, bridge) unsigned char eventY; ///< Y-coordinate (in tiles) of an event (spring, platform, bridge) JJ1PlayerEvent eventType; ///< Event type int lookTime; ///< Negative if looking up, positive if looking down, 0 if neither JJ1PlayerReaction reaction; ///< Reaction type unsigned int reactionTime; ///< Time the reaction will end unsigned int fireTime; ///< The next time the player can fire unsigned int fireAnimTime; ///< Time the firing animation will end fixed jumpHeight; ///< The height the player can reach when jumping fixed targetY; ///< Having been propelled, the y-coordinate the player could reach unsigned int fastFeetTime; ///< Time fast feet will expire unsigned char warpX; ///< X-coordinate (in tiles) player will warp to unsigned char warpY; ///< Y-coordinate (in tiles) player will warp to unsigned int warpTime; ///< Time the warp will happen int enemies; ///< Number of enemies killed int items; ///< Number of items collected bool gem; ///< Bonus level gem collected JJ1LevelPlayer(const JJ1LevelPlayer&); // non construction-copyable JJ1LevelPlayer& operator=(const JJ1LevelPlayer&); // non copyable bool checkMaskDown (fixed yOffset); bool checkMaskUp (fixed yOffset); void ground (); public: JJ1LevelPlayer (Player* parent, Anim** newAnims, unsigned char startX, unsigned char startY, int flockSize); ~JJ1LevelPlayer (); void reset (int startX, int startY) override; void addItem (); void clearEvent (unsigned char gridX, unsigned char gridY); int countBirds () override; Anim* getAnim (); int getEnemies (); int getEnergy (); bool getFacing (); int getXSpeed (); int getItems (); bool hasGem (); bool hit (Player* source, unsigned int ticks); void kill (Player* source, unsigned int ticks); bool overlap (fixed left, fixed top, fixed width, fixed height); JJ1PlayerReaction reacted (unsigned int ticks); void setPlatform (unsigned char gridX, unsigned char gridY, fixed shiftX, fixed newY); bool takeEvent (JJ1EventType* set, unsigned char gridX, unsigned char gridY, unsigned int ticks); bool touchEvent (JJ1EventType* set, unsigned char gridX, unsigned char gridY, unsigned int ticks); void send (unsigned char* buffer) override; void receive (unsigned char* buffer) override; void changeAmmo (int type, bool fallback = false); void control (unsigned int ticks); void move (unsigned int ticks); void view (unsigned int ticks, int mspf, int change); void draw (unsigned int ticks, int change); }; #endif openjazz-20240919/src/jj1/level/jj1levelplayerframe.cpp000066400000000000000000000476421467303304200226770ustar00rootroot00000000000000 /** * * @file jj1levelplayerframe.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 18th July 2009: Created playerframe.cpp from parts of player.cpp * - 24th June 2010: Renamed playerframe.cpp to levelplayerframe.cpp * - 29th June 2010: Created jj2levelplayerframe.cpp from parts of * levelplayerframe.cpp * - 1st August 2012: Renamed levelplayerframe.cpp to jj1levelplayerframe.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the once-per-frame functions of players in levels. * */ #include "jj1bullet.h" #include "event/jj1event.h" #include "jj1level.h" #include "jj1bird.h" #include "jj1levelplayer.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" /** * Determine whether or not the area below the player is solid when travelling * downwards. * * @param yOffset Vertical offset of the mask values to check * * @return Solidity */ bool JJ1LevelPlayer::checkMaskDown (fixed yOffset) { return level->checkMaskDown(x + PXO_ML + F1, y + yOffset) || level->checkMaskDown(x + PXO_MID, y + yOffset) || level->checkMaskDown(x + PXO_MR - F1, y + yOffset); } /** * Determine whether or not the area above the player is solid when travelling * upwards. * * @param yOffset Vertical offset of the mask values to check * * @return Solidity */ bool JJ1LevelPlayer::checkMaskUp (fixed yOffset) { return level->checkMaskUp(x + PXO_ML + F1, y + yOffset) || level->checkMaskUp(x + PXO_MID, y + yOffset) || level->checkMaskUp(x + PXO_MR - F1, y + yOffset); } /** * Move the player to the ground's surface. */ void JJ1LevelPlayer::ground () { // If on an uphill slope, push the player upwards if (checkMaskUp(0) && !checkMaskUp(-F4)) y -= F4; // If on a downhill slope, push the player downwards if (!checkMaskUp(F4) && checkMaskUp(F8)) y += F4; } /** * Tries to switch ammo to specific type, with fallback * * @param type Type * @param fallback Whether to go backwards in weapon list */ void JJ1LevelPlayer::changeAmmo (int type, bool fallback) { // Type 3 is unknown, skip it and use TNT instead if (type == 3) player->ammoType = 4; else player->ammoType = type; // If there is no ammo of this type, go to the next type that has ammo while ((player->ammoType > -1) && !player->ammo[player->ammoType]) { if (fallback) player->ammoType--; else player->ammoType = ((player->ammoType + 2) % 6) - 1; } } /** * Respond to controls, unless the player has been killed. * * @param ticks Time */ void JJ1LevelPlayer::control (unsigned int ticks) { fixed speed; bool platform; int count; // If the player has been killed, drop but otherwise do not move if (!energy) { dx = 0; udx = 0; if (flying) dy = 0; else { if (dy < 0) dy += PYA_GRAVITY; else dy = PYS_FALL; } return; } if ((eventType == JJ1PE_FLOATH) || (eventType == JJ1PE_REPELH)) { speed = level->getEvent(eventX, eventY)->magnitude * ((eventType == JJ1PE_FLOATH)? F10: F40); if (speed < -PXS_WALK || speed > PXS_WALK) { udx = speed; } else if ((speed < 0) && player->pcontrols[C_RIGHT]) { // Walk right, against the flow if (udx < speed) udx += PXA_REVERSE; else if (udx < speed + PXS_WALK) udx += PXA_WALK; if (udx > speed + PXS_WALK) udx = speed + PXS_WALK; facing = true; } else if ((speed > 0) && player->pcontrols[C_LEFT]) { // Walk left, against the flow if (udx > 0) udx -= PXA_REVERSE; else if (udx > speed - PXS_WALK) udx -= PXA_WALK; if (udx < speed - PXS_WALK) udx = speed - PXS_WALK; facing = false; } else { udx = speed; } eventType = JJ1PE_NONE; } else if (player->pcontrols[C_RIGHT]) { // Walk/run right if (udx < 0) udx = dx; if (udx < 0) udx += PXA_REVERSE; else if (udx < PXS_WALK) udx += PXA_WALK; else if (udx < PXS_RUN) udx += PXA_RUN; if (udx > PXS_RUN) udx = PXS_RUN; facing = true; } else if (player->pcontrols[C_LEFT]) { // Walk/run left if (udx > 0) udx = dx; if (udx > 0) udx -= PXA_REVERSE; else if (udx > -PXS_WALK) udx -= PXA_WALK; else if (udx > -PXS_RUN) udx -= PXA_RUN; if (udx < -PXS_RUN) udx = -PXS_RUN; facing = false; } else { // Slow down if (udx > 0) { if (udx < PXA_STOP) udx = 0; else udx -= PXA_STOP; } else if (udx < 0) { if (udx > -PXA_STOP) udx = 0; else udx += PXA_STOP; } } // Check for platform event, bridge or level mask below player platform = (eventType == JJ1PE_PLATFORM) || checkMaskDown(F4) || ((dx > 0) && level->checkMaskDown(x + PXO_ML, y + F8)) || ((dx < 0) && level->checkMaskDown(x + PXO_MR, y + F8)); if (flying) { if (player->pcontrols[C_UP]) { // Fly upwards if (dy > 0) dy -= PXA_REVERSE; else if (dy > -PXS_WALK) dy -= PXA_WALK; else if (dy > -PXS_RUN) dy -= PXA_RUN; } else if (player->pcontrols[C_DOWN]) { // Fly downwards if (dy < 0) dy += PXA_REVERSE; else if (dy < PXS_WALK) dy += PXA_WALK; else if (dy < PXS_RUN) dy += PXA_RUN; } else { // Slow down if (dy > 0) { if (dy < PXA_STOP) dy = 0; else dy -= PXA_STOP; } else if (dy < 0) { if (dy > -PXA_STOP) dy = 0; else dy += PXA_STOP; } } if (eventType == JJ1PE_SPRING) dy = level->getEvent(eventX, eventY)->multiA * -F20; else if (eventType == JJ1PE_FLOAT) dy = PYS_JUMP; if (dy < -PXS_RUN) dy = -PXS_RUN; else if (dy > PXS_RUN) dy = PXS_RUN; } else if (y + PYO_MID > level->getWaterLevel() + F8) { if (player->pcontrols[C_SWIM]) { // Swim upwards if (dy > 0) dy -= PXA_REVERSE; else if (dy > -PXS_WALK) dy -= PXA_WALK; else if (dy > -PXS_RUN) dy -= PXA_RUN; // Prepare to jump upon leaving the water if (!level->checkMaskUp(x + PXO_MID, y - F36)) { targetY = y - jumpHeight; if (dx < 0) targetY += dx >> 4; else if (dx > 0) targetY -= dx >> 4; eventType = JJ1PE_NONE; } } else if (player->pcontrols[C_DOWN]) { // Swim downwards if (dy < 0) dy += PXA_REVERSE; else if (dy < PXS_WALK) dy += PXA_WALK; else if (dy < PXS_RUN) dy += PXA_RUN; } else { // Sink dy += PYA_SINK; if (dy > PYS_SINK) dy = PYS_SINK; } if (dy < -PXS_RUN) dy = -PXS_RUN; else if (dy > PXS_RUN) dy = PXS_RUN; } else { if (platform && player->pcontrols[C_JUMP] && !level->checkMaskUp(x + PXO_MID, y - F36)) { // Jump targetY = y - jumpHeight; // Increase jump height if walking/running if (dx < 0) targetY += dx >> 3; else if (dx > 0) targetY -= dx >> 3; eventType = JJ1PE_NONE; playSound(SE::PHOTON); } else if (((eventType == JJ1PE_NONE) || (eventType == JJ1PE_PLATFORM)) && !player->pcontrols[C_JUMP]) { // Stop jumping targetY = TTOF(LH); } if ((eventType == JJ1PE_FLOAT) && player->pcontrols[C_DOWN]) { // Prevent floating dy = PYS_FALL; } else if (y >= targetY) { if ((eventType == JJ1PE_FLOAT) && (dy > PYS_JUMP)) { dy -= F16 + F8; } else { dy = (targetY - y - F64) * 4; } // Spring/float up speed limit if ((eventType == JJ1PE_SPRING) || (eventType == JJ1PE_REPELUP)) { speed = level->getEvent(eventX, eventY)->multiA * -F20; if (speed >= 0) speed = PYS_JUMP; if (dy < speed) dy = speed; } // Avoid jumping too fast, unless caused by an event if ((eventType == JJ1PE_NONE) && (dy < PYS_JUMP)) dy = PYS_JUMP; } else if (eventType != JJ1PE_PLATFORM) { // Fall under gravity if (dy < 0) dy += PYA_GRAVITY; else dy = PYS_FALL; } // Don't descend through platforms if ((dy > 0) && (eventType == JJ1PE_PLATFORM)) dy = 0; if (platform && !lookTime) { // If requested, look up or down if (player->pcontrols[C_UP]) lookTime = -ticks; else if (player->pcontrols[C_DOWN]) lookTime = ticks; } // Stop looking if there is no platform or the control has been released if (!platform || (!player->pcontrols[C_UP] && (lookTime < 0)) || (!player->pcontrols[C_DOWN] && (lookTime > 0))) lookTime = 0; } // If there is an obstacle above, stop rising if ((targetY < y) && checkMaskUp(PYO_TOP - F4)) { targetY = TTOF(LH); if (dy < 0) dy = 0; if (eventType != JJ1PE_PLATFORM) eventType = JJ1PE_NONE; } // Handle firing if (player->pcontrols[C_FIRE]) { if (ticks > fireTime) { if (player->ammoType == 4) { JJ1Event* event; // TNT event = level->getEvents(); while (event) { // If the event is within range, hit it if (event->overlap(x - F160, y - F100, 2 * F160, 2 * F100)) { event->hit(this, 2, ticks); } event = event->getNext(); } // Red flash level->flash(255, 0, 0, T_TNT); } else { // Horizontal bullet position is taken from the shooting animation animType = facing? PA_RSHOOT: PA_LSHOOT; level->createBullet(this, 0, 0, x + anims[animType]->getShootX(), y - ITOF(lookTime? 5: 10), player->getAmmoType() + 1, facing, ticks); } // Set when the next bullet can be fired and length of shoot animation if (player->fireSpeed) { fireTime = ticks + (1000 / player->fireSpeed); fireAnimTime = ticks + 1000; } else { fireTime = 0x7FFFFFFF; fireAnimTime = ticks + T_FIRING; } // Remove the bullet from the arsenal if (player->ammoType != -1) player->ammo[player->ammoType]--; /* If the current ammo type has been exhausted or TNT has been used, use the previous non-exhausted ammo type */ while (((player->ammoType > -1) && !player->ammo[player->ammoType]) || (player->ammoType == 4)) player->ammoType--; } } else fireTime = 0; // Check for a change in ammo if (player->pcontrols[C_CHANGE]) { if (player == localPlayer) controls.release(C_CHANGE); changeAmmo(((player->ammoType + 2) % 6) - 1); } for (count = 0; count < 5; count++) { if (controls.getState(count + C_BLASTER)) { if (player == localPlayer) controls.release(count + C_BLASTER); changeAmmo(count - 1, true); } } // Deal with the bird if (birds) birds = birds->step(ticks); // Replay sound effect before invincibility wears off if ((reaction == PR_INVINCIBLE) && (reactionTime < ticks + 2200)) if (!isSoundPlaying(SE::INVULN)) playSound(SE::INVULN); } /** * Move the player. * * @param ticks Time */ void JJ1LevelPlayer::move (unsigned int ticks) { fixed pdx, pdy; bool grounded = false; int count; if (warpTime && (ticks > warpTime)) { x = TTOF(warpX); y = TTOF(warpY + 1); warpTime = 0; } // Apply as much of the trajectory as possible, without going into the // scenery if (fastFeetTime > ticks) { pdx = (udx * 3) >> 7; pdy = (dy * 3) >> 7; // reset music speed before running shoes wear off if (fastFeetTime - ticks < T_FASTFEET >> 2) setMusicTempo(MusicTempo::NORMAL); } else { pdx = udx >> 6; pdy = dy >> 6; } // First for the vertical component of the trajectory if (pdy < 0) { // Moving up count = (-pdy) >> 12; while (count > 0) { if (checkMaskUp(PYO_TOP - F4)) { y &= ~4095; dy = 0; break; } y -= F4; count--; } pdy = (-pdy) & 4095; if (checkMaskUp(PYO_TOP - pdy)) { y &= ~4095; dy = 0; } else y -= pdy; } else { if (pdy > 0) { // Moving down count = pdy >> 12; while (count > 0) { if (checkMaskDown(F4)) { y |= 4095; dy = 0; break; } y += F4; count--; } pdy &= 4095; if (checkMaskDown(pdy)) { y |= 4095; dy = 0; } else y += pdy; } if (!flying) { if (checkMaskDown(0)) { // In the ground, so move up if (y >= 4096) y = (y - 4096) | 4095; grounded = true; } else if (checkMaskDown(1)) { // On the ground grounded = true; } } } // Then for the horizontal component of the trajectory dx = udx; if (pdx < 0) { // Moving left count = (-pdx) >> 12; while (count > 0) { // If there is an obstacle, stop if (level->checkMaskUp(x + PXO_L - F4, y + PYO_MID)) { x &= ~4095; dx = 0; if (udx < -PXS_RUN) udx = -PXS_RUN; break; } x -= F4; count--; if (grounded) ground(); } pdx = (-pdx) & 4095; if (level->checkMaskUp(x + PXO_L - pdx, y + PYO_MID)) { x &= ~4095; dx = 0; if (udx < -PXS_RUN) udx = -PXS_RUN; } else x -= pdx; if (grounded) ground(); } else if (pdx > 0) { // Moving right count = pdx >> 12; while (count > 0) { // If there is an obstacle, stop if (level->checkMaskUp(x + PXO_R + F4, y + PYO_MID)) { x |= 4095; dx = 0; if (udx > PXS_RUN) udx = PXS_RUN; break; } x += F4; count--; if (grounded) ground(); } pdx &= 4095; if (level->checkMaskUp(x + PXO_R + pdx, y + PYO_MID)) { x |= 4095; dx = 0; if (udx > PXS_RUN) udx = PXS_RUN; } else x += pdx; if (grounded) ground(); } // If the target has been reached, stop rising if (y <= targetY) { targetY = TTOF(LH); if ((eventType != JJ1PE_PLATFORM) && (eventType != JJ1PE_FLOATH) && (eventType != JJ1PE_REPELH)) eventType = JJ1PE_NONE; } if (level->getStage() != LS_END) { // If the player has hit the bottom of the level, kill if (y + F4 > TTOF(LH)) kill(NULL, ticks); // Handle spikes if (level->checkSpikes(x + PXO_MID, y + PYO_TOP - F4) || level->checkSpikes(x + PXO_MID, y + F4) || level->checkSpikes(x + PXO_L - F4, y + PYO_MID) || level->checkSpikes(x + PXO_R + F4, y + PYO_MID)) hit(NULL, ticks); } // Choose animation if (!energy) animType = facing? PA_RDIE: PA_LDIE; else if ((reaction == PR_HURT) && (reactionTime - ticks > PRT_HURT - PRT_HURTANIM)) animType = facing? PA_RHURT: PA_LHURT; else if (y + PYO_MID > level->getWaterLevel() + F8) animType = facing? PA_RSWIM: PA_LSWIM; else if (flying) animType = facing? PA_RBOARD: PA_LBOARD; else if (dy < 0) { if (eventType == JJ1PE_SPRING) animType = facing? PA_RSPRING: PA_LSPRING; else animType = facing? PA_RJUMP: PA_LJUMP; } else if (checkMaskDown(F4) || (eventType == JJ1PE_PLATFORM)) { if (udx) { if (udx <= -PXS_RUN) animType = PA_LRUN; else if (udx >= PXS_RUN) animType = PA_RRUN; else if ((udx < 0) && facing) animType = PA_LSTOP; else if ((udx > 0) && !facing) animType = PA_RSTOP; else animType = facing? PA_RWALK: PA_LWALK; } else if ((player->pcontrols[C_FIRE]) && (ticks < fireAnimTime)) animType = facing? PA_RSHOOT: PA_LSHOOT; else if ((lookTime < 0) && ((int)ticks > 1000 - lookTime)) animType = PA_LOOKUP; else if (lookTime > 0) { if ((int)ticks < 1000 + lookTime) animType = facing? PA_RCROUCH: PA_LCROUCH; else animType = PA_LOOKDOWN; } else if (!level->checkMaskDown(x + PXO_ML, y + F20) && !level->checkMaskDown(x + PXO_L, y + F2) && (eventType != JJ1PE_PLATFORM)) animType = facing? PA_RSTAND: PA_LEDGE; else if (!level->checkMaskDown(x + PXO_MR, y + F20) && !level->checkMaskDown(x + PXO_R, y + F2) && (eventType != JJ1PE_PLATFORM)) animType = facing? PA_REDGE: PA_LSTAND; else animType = facing? PA_RSTAND: PA_LSTAND; } else animType = facing? PA_RFALL: PA_LFALL; } /** * Calculate viewport. * * @param ticks Time * @param mspf Ticks per frame * @param change Time since last step */ void JJ1LevelPlayer::view (unsigned int ticks, int mspf, int change) { int oldViewX, oldViewY, speed; // Record old viewport position for applying lag oldViewX = viewX; oldViewY = viewY; // Find new position viewX = x + ((dx * change) >> 10) + F8 - (canvasW << 9); viewY = y + ((dy * change) >> 10) - F24 - ((canvasH - 33) << 9); if ((lookTime > 0) && ((int)ticks > 1000 + lookTime)) { // Look down if ((int)ticks < 2000 + lookTime) viewY += 64 * (ticks - (1000 + lookTime)); else viewY += F64; } else if ((lookTime < 0) && ((int)ticks > 1000 - lookTime)) { // Look up if ((int)ticks < 2000 - lookTime) viewY -= 64 * (ticks - (1000 - lookTime)); else viewY -= F64; } // Apply lag proportional to player "speed" speed = ((dx >= 0? dx: -dx) + (dy >= 0? dy: -dy)) >> 14; if (speed && (mspf < speed)) { viewX = ((oldViewX * (speed - mspf)) + (viewX * mspf)) / speed; viewY = ((oldViewY * (speed - mspf)) + (viewY * mspf)) / speed; } } /** * Draw the player. * * @param ticks Time * @param change Time since last step */ void JJ1LevelPlayer::draw (unsigned int ticks, int change) { Anim *an; int frame; fixed drawX, drawY; fixed xOffset, yOffset; fixed angle; // The current frame for animations if (reaction == PR_KILLED) frame = (ticks + PRT_KILLED - reactionTime) / 75; else frame = ticks / 75; // Get position drawX = getDrawX(change); drawY = getDrawY(change); // Choose sprite an = anims[animType]; an->setFrame(frame, reaction != PR_KILLED); // Show the player // Flash red if hurt, otherwise use player colour if ((reaction == PR_HURT) && (!((ticks / 30) & 3))) an->flashPalette(36); else { an->setPalette(palette, 23, 41); an->setPalette(palette, 88, 8); } // Draw "motion blur" if (fastFeetTime > ticks) an->draw(drawX - (dx >> 6), drawY); // Draw player an->draw(drawX, drawY); // Remove red flash or player colour from sprite an->restorePalette(); // Uncomment the following to see the area of the player /*drawRect(FTOI(drawX + PXO_L), FTOI(drawY + PYO_TOP), FTOI(PXO_R - PXO_L), FTOI(-PYO_TOP), 89); drawRect(FTOI(drawX + PXO_ML), FTOI(drawY + PYO_TOP), FTOI(PXO_MR - PXO_ML), FTOI(-PYO_TOP), 88);*/ // Uncomment the following to show the tile containing the player's base //drawRect(FTOI(TTOF(FTOT(x + PXO_MID)) - viewX), FTOI(TTOF(FTOT(y)) - viewY), 32, 32, 48); // Uncomment the following to show the player's event tile // if (eventType != JJ1PE_NONE) drawRect(FTOI(TTOF(eventX) - viewX), FTOI(TTOF(eventY) - viewY), 32, 32, 89); if (flying) { an = level->getMiscAnim(facing? MA_RBOARD: MA_LBOARD); an->setFrame(ticks >> 4, true); an->draw(drawX, drawY + F10); } if (reaction == PR_INVINCIBLE) { // Show invincibility stars xOffset = fSin(ticks * 2) * 12; yOffset = fCos(ticks * 2) * 12; an = level->getMiscAnim(MA_SPARKLE); an->setFrame(frame, true); an->draw(drawX + PXO_MID + xOffset, drawY + PYO_MID + yOffset); an->setFrame(frame + 1, true); an->draw(drawX + PXO_MID - xOffset, drawY + PYO_MID - yOffset); an->setFrame(frame + 2, true); an->draw(drawX + PXO_MID + yOffset, drawY + PYO_MID + xOffset); an->setFrame(frame + 3, true); an->draw(drawX + PXO_MID - yOffset, drawY + PYO_MID - xOffset); } else if (shield > 1) { // Show the 4-hit shield an = level->getMiscAnim(MA_4SHIELD); if (shield == 4) { // triangle based for (int i = 0; i < 3; i++) { angle = -(i * 341 + ticks); xOffset = fSin(angle) * 20; yOffset = fCos(angle) * 20; if (!facing) yOffset = -yOffset; an->draw(drawX + xOffset, drawY + PYO_TOP + yOffset); } } else { // rectangle based xOffset = fCos(ticks) * 20; yOffset = fSin(ticks) * 20; if (!facing) yOffset = -yOffset; an->draw(drawX + xOffset, drawY + PYO_TOP + yOffset); if (shield > 2) an->draw(drawX - xOffset, drawY + PYO_TOP - yOffset); if (shield > 4) { an->draw(drawX + yOffset, drawY + PYO_TOP - xOffset); an->draw(drawX - yOffset, drawY + PYO_TOP + xOffset); } } } else if (shield) { // Show the 1-hit shield xOffset = fCos(ticks) * 20; yOffset = fSin(ticks) * 20; an = level->getMiscAnim(MA_1SHIELD); if (!facing) yOffset = -yOffset; an->draw(drawX + xOffset, drawY + PYO_TOP + yOffset); an->draw(drawX - xOffset, drawY + PYO_TOP - yOffset); } // Show the bird if (birds) birds->draw(ticks, change); // Show the player's name if (nPlayers > 1) panelBigFont->showString(player->name, FTOI(drawX + PXO_MID) - (panelBigFont->getStringWidth(player->name) >> 1), FTOI(drawY - F32 - F16)); } openjazz-20240919/src/jj1/planet/000077500000000000000000000000001467303304200163665ustar00rootroot00000000000000openjazz-20240919/src/jj1/planet/jj1planet.cpp000066400000000000000000000053451467303304200207710ustar00rootroot00000000000000 /** * * @file jj1planet.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created planet.c * - 3rd February 2009: Renamed planet.c to planet.cpp * - 1st August 2012: Renamed planet.cpp to jj1planet.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading, displaying and freeing of the planet landing * sequence. * */ #include "jj1planet.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "loop.h" #include "util.h" #include /** * Create a JJ1 planet approach sequence. * * @param fileName Name of the file containing the planet data * @param previous The ID of the last planet approach sequence */ JJ1Planet::JJ1Planet (char * fileName, int previous) { File *file; unsigned char *pixels; int count; try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { throw; } id = file->loadShort(); if (id == previous) { // Not approaching a planet if already there delete file; throw E_NONE; } // Load planet name name = file->loadString(); // Lower-case the name for (count = 0; name[count]; count++) { if ((name[count] >= 65) && (name[count] <= 90)) name[count] += 32; } // Load the palette file->loadPalette(palette, false); // Load the planet image pixels = file->loadBlock(64 * 55); sprite.setPixels(pixels, 64, 55, 0); delete[] pixels; delete file; } /** * Delete the JJ1 planet approach sequence. */ JJ1Planet::~JJ1Planet () { delete[] name; } /** * Get the ID of the planet approach squence. * * @return The ID */ int JJ1Planet::getId () { return id; } /** * Run the JJ1 planet approach sequence. * * @return Error code */ int JJ1Planet::play () { unsigned int tickOffset; tickOffset = globalTicks; stopMusic(); video.setPalette(palette); while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE) || controls.wasCursorReleased()) return E_NONE; SDL_Delay(T_MENU_FRAME); video.clearScreen(0); if (globalTicks - tickOffset < F2) sprite.drawScaled(canvasW >> 1, canvasH >> 1, globalTicks - tickOffset); else if (globalTicks - tickOffset < F4) sprite.drawScaled(canvasW >> 1, canvasH >> 1, F2); else if (globalTicks - tickOffset < F4 + FQ) sprite.drawScaled(canvasW >> 1, canvasH >> 1, (globalTicks - tickOffset - F4) * 32 + F2); else return E_NONE; fontmn1->showString("now approaching", (canvasW - 288) >> 1, 0); fontmn1->showString(name, (canvasW - fontmn1->getStringWidth(name)) >> 1, canvasH - 24); } return E_NONE; } openjazz-20240919/src/jj1/planet/jj1planet.h000066400000000000000000000013661467303304200204350ustar00rootroot00000000000000 /** * * @file jj1planet.h * * Part of the OpenJazz project * * @par History: * - 3rd February 2009: Created planet.h * - 1st August 2012: Renamed planet.h to jj1planet.h * * @par Licence: * Copyright (c) 2009-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _PLANET_H #define _PLANET_H #include "io/gfx/sprite.h" // Class /// Planet approach sequence class JJ1Planet { private: SDL_Color palette[MAX_PALETTE_COLORS]; /// Palette Sprite sprite; /// Planet image char* name; /// Planet name int id; /// World number public: JJ1Planet (char * fileName, int previous); ~JJ1Planet (); int getId (); int play (); }; #endif openjazz-20240919/src/jj1/save/000077500000000000000000000000001467303304200160415ustar00rootroot00000000000000openjazz-20240919/src/jj1/save/jj1save.cpp000066400000000000000000000067751467303304200201270ustar00rootroot00000000000000 /** * * @file jj1save.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2024 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with savegames. * */ #include "jj1save.h" #include "util.h" #include "io/log.h" #include /* This algorithm has been found out by CYBERDEViL by analysing the Assembly in * DoxBox-X. For more information see: https://codeberg.org/CYBERDEV/JJSave/ */ namespace { constexpr int multiplicatorField = 256; // 0x100 } static void keyIterate(unsigned short &key1, unsigned short &key2) { // multiplicators constexpr int m1 = 33797; // 0x8405 constexpr int m2 = 2056; // 0x808 constexpr int m3 = 5; constexpr int m4 = 4; //LOG_TRACE("FROM key1, key2: 0x%04X, 0x%04X\n", key1, key2); // save value unsigned short oldKey2 = key2; // calculate keys unsigned int keyBuffer = key1 * m1; key2 = keyBuffer + 1; // use lower bits unsigned short part1 = keyBuffer >> 16; // use upper bits unsigned short part2 = key1 * m2; unsigned short part3 = oldKey2 * m3; unsigned short val1 = part1 + part2 + part3; part1 = val1 >> 8; // use upper bit part2 = (unsigned char)oldKey2 + (oldKey2 << m3); unsigned short val2 = part1 + part2 * m4; key1 = val2 * multiplicatorField | (unsigned char)val1; // add lower bit //LOG_TRACE("TO key1, key2: 0x%04X, 0x%04X\n", key1, key2); } static unsigned short dataOffset(unsigned short key, bool skipGarbage = false) { // multiplicators constexpr int mData = 2000; // 0x7D0 unsigned short offset; if(skipGarbage) { // return offset to data offset = (key * mData) >> 16; } else { // return next data field offset = (key * multiplicatorField) >> 16; } //LOG_TRACE("SKIP offset: 0x%04X\n", offset); return offset; } JJ1Save::JJ1Save (const char* fileName) : valid(false) { std::unique_ptr file; LOG_TRACE("Save: %s", fileName); try { file = std::make_unique(fileName, PATH_TYPE_GAME); } catch (int e) { name = createString("empty"); return; } // First load player's name name = file->loadString(16); lowercaseString(name); // Use termination marker as magic if (file->loadChar() != 0x1A) { LOG_WARN("Wrong save magic."); delete[] name; name = createString("invalid"); return; } // Read creation time for initial key generation int m = file->loadChar(); int h = file->loadChar(); int t = file->loadChar(); int s = file->loadChar(); LOG_MAX("Save creation time: %02d:%02d:%02d:%02d.", h, m, s, t); unsigned short key1 = m | (h << 8); unsigned short key2 = t | (s << 8); // Calculate offset from keys keyIterate(key1, key2); // Seek to data offset file->seek(dataOffset(key1, true)); // Read keys for data entries key1 = file->loadShort(); key2 = file->loadShort(); // Read XOR'ed values auto readValue = [&]() -> unsigned short { keyIterate(key1, key2); unsigned short bit = dataOffset(key1); unsigned short val = file->loadShort(); // Switch keys auto tmp = key2; key2 = key1; key1 = tmp; return val ^ bit; }; level = readValue(); planet = readValue(); difficulty = readValue(); LOG_TRACE("Saved planet = %d, level = %d, difficulty = %d", planet, level, difficulty); // TODO: Find out about the next 6 unknown fields for(int i = 0; i < 6; i++) unknown[i] = readValue(); LOG_MAX("Unknown save fields: %d %d %d %d %d %d", unknown[0], unknown[1], unknown[2], unknown[3], unknown[4], unknown[5]); valid = true; } JJ1Save::~JJ1Save () { delete[] name; } openjazz-20240919/src/jj1/save/jj1save.h000066400000000000000000000011561467303304200175600ustar00rootroot00000000000000 /** * * @file jj1save.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2024 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef OJ_JJ1SAVE_H #define OJ_JJ1SAVE_H #include "io/file.h" #include "types.h" class JJ1Save { private: JJ1Save(const JJ1Save&); // non construction-copyable JJ1Save& operator=(const JJ1Save&); // non copyable public: explicit JJ1Save (const char* fileName); ~JJ1Save (); bool valid; char* name; int planet; int level; int difficulty; int unknown[6]; }; #endif openjazz-20240919/src/jj1/scene/000077500000000000000000000000001467303304200162005ustar00rootroot00000000000000openjazz-20240919/src/jj1/scene/jj1scene.cpp000066400000000000000000000272461467303304200204210ustar00rootroot00000000000000 /** * * @file jj1scene.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created scene.c * - 3rd February 2009: Created scene.h from parts of scene.c * - 3rd February 2009: Renamed scene.c to scene.cpp * - 27th March 2010: Created sceneload.cpp from parts of scene.cpp * - 1st August 2012: Renamed scene.cpp to jj1scene.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the displaying and freeing of the cutscenes. * */ #include "jj1scene.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/paletteeffects.h" #include "io/gfx/video.h" #include "io/sound.h" #include "loop.h" #include "util.h" #include "io/log.h" #include /** * Create a JJ1 cutscne frame. * * @param newFrameType The type of the frame * @param newFrameData The frame's data buffer * @param newFrameSize The size of the frame's data buffer */ JJ1SceneFrame::JJ1SceneFrame(int newFrameType, unsigned char* newFrameData, int newFrameSize) { soundId = SE::NONE; frameData = newFrameData; frameType = newFrameType; frameSize = newFrameSize; prev = NULL; next = NULL; } /** * Delete the JJ1 cutscene frame. */ JJ1SceneFrame::~JJ1SceneFrame() { delete [] frameData; } /** * Add a frame to the JJ1 cutscene animation. * * @param frameType The type of the frame * @param frameData The frame's data buffer * @param frameSize The size of the frame's data buffer */ void JJ1SceneAnimation::addFrame(int frameType, unsigned char* frameData, int frameSize) { JJ1SceneFrame* frame = new JJ1SceneFrame(frameType, frameData, frameSize); if(sceneFrames == NULL) { sceneFrames = frame; } else { frame->prev = lastFrame; lastFrame->next = frame; } lastFrame = frame; frames++; } /** * Create a JJ1 cutscene animation. * * @param newNext The next animation */ JJ1SceneAnimation::JJ1SceneAnimation (JJ1SceneAnimation* newNext) { next = newNext; background = NULL; lastFrame = sceneFrames = NULL; frames = reverseAnimation = 0; id = -1; } /** * Delete the JJ1 cutscene animation. */ JJ1SceneAnimation::~JJ1SceneAnimation () { if (next) delete next; if(sceneFrames) { JJ1SceneFrame* frame = sceneFrames; JJ1SceneFrame* nextFrame = NULL; while(frame) { nextFrame = frame->next; delete frame; frame = NULL; frame = nextFrame; } } if (background) SDL_FreeSurface(background); } /** * Create a JJ1 cutscene image. * * @param newNext The next image */ JJ1SceneImage::JJ1SceneImage (JJ1SceneImage *newNext) { next = newNext; image = NULL; id = -1; } /** * Delete the JJ1 cutscene image. */ JJ1SceneImage::~JJ1SceneImage () { if (next) delete next; if (image) SDL_FreeSurface(image); } /** * Create a JJ1 cutscene palette. * * @param newNext The next palette */ JJ1ScenePalette::JJ1ScenePalette (JJ1ScenePalette *newNext) { next = newNext; id = -1; } /** * Delete the JJ1 cutscene palette. */ JJ1ScenePalette::~JJ1ScenePalette () { if (next) delete next; } /** * Create a JJ1 cutscene text object. */ JJ1SceneText::JJ1SceneText() { x = -1; y = -1; textRect.x = -1; textRect.y = -1; extraLineHeight = -1; text = NULL; shadowColour = 0; alignment = 0; fontId = -1; } /** * Delete the JJ1 cutscene text object. */ JJ1SceneText::~JJ1SceneText() { if (text) delete[] text; } /** * Create a JJ1 cutscene page. */ JJ1ScenePage::JJ1ScenePage() { pageTime = 0; nTexts = 0; backgrounds = 0; musicFile = NULL; paletteIndex = 0; askForYesNo = 0; stopMusic = 0; animIndex = -1; // no anim animLoops = 0; animSpeed = 0; nextPageAfterAnim = 0; backgroundFade = 255; } /** * Delete the JJ1 cutscene page. */ JJ1ScenePage::~JJ1ScenePage() { if (musicFile) delete[] musicFile; } /** * Create a JJ1 cutscene. * * @param fileName Name of the file containing the cutscene data */ JJ1Scene::JJ1Scene (const char * fileName) { File *file; nFonts = 0; LOG_TRACE("Scene: %s", fileName); try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { throw; } images = NULL; palettes = NULL; animations = NULL; file->seek(0x13, true); // Skip Digital Dimensions header signed long int dataOffset = file->loadInt(); //get offset pointer to first data block scriptItems = file->loadShort(); // Get number of script items scriptStarts = new signed long int[scriptItems]; pages = new JJ1ScenePage[scriptItems]; LOG_TRACE("Scene: Script items: %d", scriptItems); for (int i = 0; i < scriptItems; i++) { scriptStarts[i] = file->loadInt();// Load offset to script LOG_TRACE("scriptStart: %ld", scriptStarts[i]); } // Seek to datastart now file->seek(dataOffset, true); // Seek to data offsets dataItems = file->loadShort() + 1; // Get number of data items LOG_TRACE("Scene: Data items %d", dataItems); dataOffsets = new signed long int[dataItems]; for (int i = 0; i < dataItems; i++) { dataOffsets[i] = file->loadInt();// Load offset to script LOG_TRACE("dataOffsets: %ld", dataOffsets[i]); } loadData(file); loadScripts(file); delete[] scriptStarts; delete[] dataOffsets; delete file; } /** * Delete the JJ1 cutscene. */ JJ1Scene::~JJ1Scene () { delete[] pages; if (images) delete images; if (palettes) delete palettes; if (animations) delete animations; } /** * Play the JJ1 cutscene. * * @return Error code */ int JJ1Scene::play () { SDL_Rect dst; unsigned int sceneIndex = 0; JJ1SceneImage *image; JJ1SceneAnimation* animation = NULL; JJ1SceneFrame* currentFrame = NULL; PaletteEffect* paletteEffect = NULL; int frameDelay = 0; int prevFrame = 0; int continueToNextPage = 0; unsigned int pageTime = pages[sceneIndex].pageTime; unsigned int lastTicks = globalTicks; int newpage = true; SDL_Rect textRect = {0, 0, SW, SH}; video.clearScreen(0); while (true) { bool upOrLeft = false; bool downOrRight = false; int x, y; if (loop(NORMAL_LOOP, paletteEffect) == E_QUIT) { if (paletteEffect) delete paletteEffect; return E_QUIT; } controls.getCursor(x, y); x -= (canvasW - SW) >> 1; y -= (canvasH - SH) >> 1; downOrRight = controls.wasCursorReleased(); if (controls.release(C_ESCAPE) || (controls.release(C_NO) && pages[sceneIndex].askForYesNo) || (downOrRight && (x >= 0) && (x < 100) && (y >= SH - 12) && (y < SH))) { if (paletteEffect) delete paletteEffect; return E_NONE; } SDL_Delay(T_MENU_FRAME); if(pages[sceneIndex].askForYesNo) { downOrRight |= controls.release(C_ENTER) || controls.release(C_YES); } else { upOrLeft = (controls.release(C_UP) || controls.release(C_LEFT)); downOrRight |= (controls.release(C_RIGHT) || controls.release(C_DOWN) || controls.release(C_ENTER)); } if ((sceneIndex > 0 && upOrLeft) || downOrRight || continueToNextPage || ((globalTicks-lastTicks) >= pageTime * 1000 && pageTime != 256 && pageTime != 0)) { if(pages[sceneIndex].stopMusic) { stopMusic(); } if (upOrLeft) sceneIndex--; else sceneIndex++; if (sceneIndex == scriptItems) { if (paletteEffect) delete paletteEffect; return E_NONE; } lastTicks = globalTicks; // Get bg for this page newpage = true; pageTime = pages[sceneIndex].pageTime; continueToNextPage = 0; } if (newpage) { //if (paletteEffect) delete paletteEffect; //paletteEffect = new FadeOutPaletteEffect(250, NULL); textRect.x = 0; textRect.y = 0; textRect.w = SW; textRect.h = SH; JJ1ScenePalette *palette = palettes; while (palette && (palette->id != pages[sceneIndex].paletteIndex)) palette = palette->next; if (palette) { video.setPalette(palette->palette); // Fade in from black if (paletteEffect) delete paletteEffect; paletteEffect = new FadeInPaletteEffect(250, NULL); } if(pages[sceneIndex].musicFile) { playMusic(pages[sceneIndex].musicFile); } newpage = 0; } // First draw the backgrounds associated with this page if (pages[sceneIndex].backgrounds > 0) { for (int bg = 0; bg < pages[sceneIndex].backgrounds; bg++) { image = images; while (image && (image->id != pages[sceneIndex].bgIndex[bg])) image = image->next; if (image) { dst.x = pages[sceneIndex].bgX[bg] + ((canvasW - SW) >> 1); dst.y = pages[sceneIndex].bgY[bg] + ((canvasH - SH) >> 1); SDL_BlitSurface(image->image, NULL, canvas, &dst); } } } else if (pages[sceneIndex].animIndex != -1) { if (currentFrame == NULL) { animation = animations; while (animation && (animation->id != pages[sceneIndex].animIndex)) animation = animation->next; if (animation && animation->background) { dst.x = (canvasW - SW) >> 1; dst.y = (canvasH - SH) >> 1; frameDelay = 1000 / (pages[sceneIndex].animSpeed >> 8); SDL_BlitSurface(animation->background, NULL, canvas, &dst); currentFrame = animation->sceneFrames; SDL_Delay(frameDelay); } } else { // Upload pixel data to the surface if (SDL_MUSTLOCK(animation->background)) SDL_LockSurface(animation->background); switch (currentFrame->frameType) { case ESquareAniHeader: loadCompactedMem(currentFrame->frameSize, currentFrame->frameData, static_cast(animation->background->pixels)); break; case EFFAniHeader: loadFFMem(currentFrame->frameSize, currentFrame->frameData, static_cast(animation->background->pixels)); break; default: LOG_DEBUG("Scene::Play unknown type: %d", currentFrame->frameType); break; } if (SDL_MUSTLOCK(animation->background)) SDL_UnlockSurface(animation->background); dst.x = (canvasW - SW) >> 1; dst.y = (canvasH - SH) >> 1; SDL_BlitSurface(animation->background, NULL, canvas, &dst); playSound(currentFrame->soundId); if (prevFrame) currentFrame = currentFrame->prev; else currentFrame = currentFrame->next; SDL_Delay(frameDelay); if (currentFrame == NULL && animation->reverseAnimation) { //prevFrame = 1 - prevFrame; /*if(prevFrame) currentFrame = lastFrame->prev; else currentFrame = lastFrame->next;*/ currentFrame = NULL;//animation->sceneFrames; } else if (currentFrame == NULL && !pageTime && !pages[sceneIndex].askForYesNo && pages[sceneIndex].nextPageAfterAnim) { continueToNextPage = 1; } } } else video.clearScreen(0); // Draw the texts associated with this page x = 0; y = 0; int extraLineHeight = 0; for (int count = 0; count < pages[sceneIndex].nTexts; count++) { JJ1SceneText *text = pages[sceneIndex].texts + count; Font *font = NULL; int xOffset, yOffset; for (int index = 0; index < nFonts; index++) { if (text->fontId == fonts[index].id) { font = fonts[index].font; continue; } } if (text->x != -1) { x = text->x; y = text->y; } if (text->textRect.x != -1) { textRect = text->textRect; x = 0; y = 0; } if (text->extraLineHeight != -1) { extraLineHeight = text->extraLineHeight; } xOffset = ((canvasW - SW) >> 1) + textRect.x + x; yOffset = ((canvasH - SH) >> 1) + textRect.y + y; switch (text->alignment) { case 0: // left break; case 1: // right xOffset += textRect.w - font->getSceneStringWidth(text->text); break; case 2: // center xOffset += (textRect.w - font->getSceneStringWidth(text->text)) >> 1; break; } // Drop shadow font->mapPalette(0, MAX_PALETTE_COLORS, 0, 1); font->showSceneString(text->text, xOffset + 1, yOffset + 1); font->restorePalette(); // Text itself font->showSceneString(text->text, xOffset, yOffset); y += extraLineHeight + font->getHeight() / 2; } } return E_NONE; } openjazz-20240919/src/jj1/scene/jj1scene.h000066400000000000000000000115701467303304200200570ustar00rootroot00000000000000 /** * * @file jj1scene.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created scene.c * - 3rd February 2009: Created scene.h from parts of scene.c * - 1st August 2012: Renamed scene.h to jj1scene.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _SCENE_H #define _SCENE_H #include "io/file.h" #include "types.h" // Enums /** * Cutscene file animation headers 11 1L /0/0 PB FF RN RB RC RL RR ][ PL AN _E MX ST SL */ enum ANIHeaders { E11AniHeader = 0x3131, ///< Background/start image E1LAniHeader = 0x4c31, EPBAniHeader = 0x4250, EFFAniHeader = 0x4646, ///< Floodfill? or full frame? ERNAniHeader = 0x4e52, ERBAniHeader = 0x4252, ERCAniHeader = 0x4352, ERLAniHeader = 0x4c52, ERRAniHeader = 0x5252, E_EHeader = 0x455F, ///< ANI End ESquareAniHeader = 0x5b5d, EMXAniHeader = 0x584d, ESTAniHeader = 0x5453, ///< Sound tag ESoundListAniHeader = 0x4C53, EPlayListAniHeader = 0x4C50 }; /// Cutscene script types - these are the known types enum { ESceneYesNo = 0x23, ESceneMusic = 0x2A, ESceneStopMusic = 0x2D, ESceneFadeType = 0x3F, ESceneTextBlock = 0x40, ESceneTextColour = 0x41, ESceneFontFun = 0x45, ESceneFontIndex = 0x46, ESceneTextPosition = 0x47, ESceneTextAlign = 0x4A, ESceneTextAlign2 = 0x4B, ESceneBackground = 0x4c, ESceneBreaker = 0x50, ESceneSomethingElse = 0x51, ESceneTextRect = 0x57, ESceneFontDefine = 0x58, ESceneTime = 0x5d, ESceneTextLine = 0x5e, ESceneTextVAdjust = 0x5f, ESceneAnimationPlayAndContinue = 0xA7, ESceneAnimation = 0xA6, ESceneBackgroundFade = 0xb1, ESceneTextSomething = 0xd9, ESceneTextShadow = 0xdb }; // Classes class Font; /// Cutscene page text class JJ1SceneText { public: unsigned char* text; int alignment; int fontId; int x; int y; SDL_Rect textRect; int extraLineHeight; int shadowColour; JJ1SceneText (); ~JJ1SceneText (); }; /// Cutscene page class JJ1ScenePage { public: int backgrounds; int bgIndex[30]; unsigned short int bgX[30]; unsigned short int bgY[30]; int animLoops; int animSpeed; int animIndex; int nextPageAfterAnim; /// Length of the scene in seconds, or if zero = anim complete, or 256 = user interaction int pageTime; JJ1SceneText texts[100]; int nTexts; char* musicFile; int paletteIndex; int askForYesNo; int stopMusic; int backgroundFade; JJ1ScenePage (); ~JJ1ScenePage (); }; /// Cutscene background image class JJ1SceneImage { public: JJ1SceneImage* next; SDL_Surface* image; int id; explicit JJ1SceneImage (JJ1SceneImage* newNext); ~JJ1SceneImage (); }; /// Cutscene palette class JJ1ScenePalette { public: JJ1ScenePalette* next; SDL_Color palette[MAX_PALETTE_COLORS]; int id; explicit JJ1ScenePalette (JJ1ScenePalette* newNext); ~JJ1ScenePalette (); }; /// Cutscene font class JJ1SceneFont { public: Font *font; int id; }; /// Cutscene animation frame class JJ1SceneFrame { public: JJ1SceneFrame* next; JJ1SceneFrame* prev; unsigned char* frameData; int frameSize; unsigned int frameType; SE::Type soundId; JJ1SceneFrame (int frameType, unsigned char* frameData, int frameSize); ~JJ1SceneFrame (); }; /// Cutscene animation class JJ1SceneAnimation { public: JJ1SceneAnimation* next; JJ1SceneFrame* sceneFrames; JJ1SceneFrame* lastFrame; SDL_Surface* background; int id; int frames; int reverseAnimation; explicit JJ1SceneAnimation (JJ1SceneAnimation* newNext); ~JJ1SceneAnimation (); void addFrame (int frameType, unsigned char* frameData, int frameSize); }; /// Cutscene class JJ1Scene { private: JJ1SceneAnimation* animations; JJ1SceneImage* images; JJ1ScenePalette* palettes; JJ1SceneFont fonts[5]; int nFonts; unsigned short int scriptItems; unsigned short int dataItems; signed long int* scriptStarts; signed long int* dataOffsets; /// Scripts all information needed to render script pages, text etc JJ1ScenePage* pages; JJ1Scene(const JJ1Scene&); // non construction-copyable JJ1Scene& operator=(const JJ1Scene&); // non copyable void loadScripts (File* f); void loadData (File* f); void loadAni (File* f, int dataIndex); void loadCompactedMem (int size, unsigned char* frameData, unsigned char* pixdata); void loadFFMem (int size, unsigned char* frameData, unsigned char* pixdata); unsigned short int loadShortMem (unsigned char **data); public: explicit JJ1Scene (const char* fileName); ~JJ1Scene (); int play (); }; #endif openjazz-20240919/src/jj1/scene/jj1sceneload.cpp000066400000000000000000000444531467303304200212600ustar00rootroot00000000000000 /** * * @file jj1sceneload.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created scene.c * - 3rd February 2009: Created scene.h from parts of scene.c * - 3rd February 2009: Renamed scene.c to scene.cpp * - 27th March 2010: Created sceneload.cpp from parts of scene.cpp * - 1st August 2012: Renamed sceneload.cpp to jj1sceneload.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading of cutscene data. * */ #include "jj1scene.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" #include "io/log.h" #include /** * Load a short from a buffer and advance the pointer past it. * * @param data Pointer to data in a buffer * * @return The loaded short */ unsigned short int JJ1Scene::loadShortMem (unsigned char** data) { unsigned short int val; val = **data; (*data)++; val += ((unsigned short)(**data)) << 8; (*data)++; return val; } /** * Decompress JJ1 cutscene graphical data. * * @param size The size of the compressed data * @param frameData The compressed data * @param pixels Buffer to contain the decompressed data */ void JJ1Scene::loadFFMem (int size, unsigned char* frameData, unsigned char* pixels) { unsigned char* nextPixel = pixels; unsigned char* nextData = frameData; int fillWidth = 0; bool trans = true; /*FILE* out = fopen("c:\\output.dat", "wb"); fwrite(frameData, size, 1, out); fclose(out);*/ while ((nextData < frameData + size) && (nextPixel < pixels + (SW * SH))) { unsigned char header = *nextData; nextData++; LOG_MAX("PL FF frame header: %x", header); if ((header & 0x7F) == 0x7F) { fillWidth = loadShortMem(&nextData); if (trans) fillWidth += 255; LOG_MAX("PL FF 0x7f skip: %d", fillWidth); } else if (header) { if(trans) { fillWidth = header; LOG_MAX("PL FF SKIP bytes: %d", fillWidth); } else { fillWidth = header & 0x1F; switch (header & 0x60) { default: break; case 0x00: LOG_MAX("PL FF 0x00 Copy bytes: %x", header); memcpy(nextPixel, nextData, fillWidth); nextData += fillWidth; break; case 0x20: LOG_MAX("PL FF 0x20 copy previous line op: %d", fillWidth); if (nextPixel - 320 >= pixels) memcpy(nextPixel, nextPixel - 320, fillWidth); break; case 0x40: LOG_MAX("PL FF 0x40 fillWidth: %d", fillWidth); memset(nextPixel, *nextData, fillWidth); nextData++; break; case 0x60: LOG_MAX("PL FF 0x60 header: %x", header); fillWidth = header&0x3F; memset(nextPixel, *nextData, fillWidth); nextData++; break; } } } else { LOG_MAX("PL FF FAULTY END OF STREAM: %d", size); return; } nextPixel += fillWidth; if (header & 0x80) trans = false; else trans = !trans; } LOG_MAX("PL FF pixels: %d", (unsigned int)(nextPixel - pixels)); } /* * $0x $... Next x + 1 bytes are 'literals'; each byte colors 1 column (Max val $3F) * $4x $yy Next x + 1 columns drawn in color yy (Max value $7E) * $7F $xxxx $yy Next xxxx columns colored with color yy * $8x Next x + 1 pixels are skipped, they're already the right color (Max val $FE) * $FF $xxxx Skip next xxxx pixels of picture, they're already the right color */ /** * Decompress JJ1 cutscene graphical data. * * @param size The size of the compressed data * @param frameData The compressed data * @param pixels Buffer to contain the decompressed data */ void JJ1Scene::loadCompactedMem (int size, unsigned char* frameData, unsigned char* pixels) { unsigned char* nextPixel = pixels; unsigned char* endpixdata = pixels + (SW * SH); unsigned char* fillstart = NULL; int fillWidth; while (size > 0) { unsigned char header = *frameData; frameData++; if (header == 0x7F) { fillWidth = loadShortMem(&frameData); unsigned char fillColor = *frameData; frameData++; fillstart = nextPixel; while (fillstart + fillWidth <= endpixdata) { memset(fillstart, fillColor, fillWidth); fillstart += SW; } size -= 3; } else if (header == 0xFF) { fillWidth = loadShortMem(&frameData); size -= 2; } else if (header & 0x80) { fillWidth = (header - 0x80) + 1; } else if (header & 0x40) { unsigned char fillColor = *frameData; frameData++; fillWidth = (header - 0x40) + 1; fillstart = nextPixel; while (fillstart + fillWidth <= endpixdata) { memset(fillstart, fillColor, fillWidth); fillstart += SW; } size--; } else { fillWidth = (header & 0x3F) + 1; for (int col = 0; col < fillWidth; col++) { unsigned char color = *frameData; frameData++; if (color != 0xFF) { fillstart = nextPixel + col; while (fillstart < endpixdata) { *fillstart = color; fillstart += SW; } } size--; } } nextPixel += fillWidth; size--; } LOG_MAX("PL Compacts pixels: %d", (unsigned int)(nextPixel - pixels)); } /** * Load JJ1 cutscene animation. * * @param f File from which to load animation * @param dataIndex Index */ void JJ1Scene::loadAni (File *f, int dataIndex) { LOG_MAX("ParseAni DataLen: %x", f->loadShort()); // should be 0x02 LOG_MAX("ParseAni Frames(?): %d", f->loadShort()); // unknown, number of frames? unsigned short int type = 0; int loop; while (type != EPlayListAniHeader) { type = f->loadShort(); if (type == ESoundListAniHeader) { // SL /*unsigned short int offset =*/ f->loadShort(); unsigned char nSounds = f->loadChar(); for(loop = 0; loop < nSounds; loop++) { char* soundName = f->loadString(); LOG_MAX("Soundname: %s", soundName); resampleSound(loop, soundName, 11025); delete[] soundName; } } else if (type == EPlayListAniHeader) {// PL int nextPos = f->tell(); LOG_MAX("PL Read position: %d", nextPos); f->loadShort(); // Length palettes = new JJ1ScenePalette(palettes); f->loadPalette(palettes->palette, false); palettes->id = dataIndex; int items = 0; int validValue = true; LOG_MAX("PL Read position start: %d", f->tell()); while (validValue) { unsigned short int value = f->loadShort(); LOG_MAX("PL Read block start tag: %x", value); int size = f->loadShort(); LOG_MAX("PL Anim block size: %d", size); nextPos = f->tell(); // next pos is intial position + size and four bytes header nextPos += size; switch (value) { case E_EHeader: // END MARKER validValue = false; break; case E11AniHeader: //11 // Skip back size header, this is read by the surface reader LOG_MAX("PL 11 Background Type: 0"); f->seek(-2, false); animations->background = f->loadSurface(SW, SH); // Use the most recently loaded palette video.setPalette(palettes->palette); break; case E1LAniHeader: { LOG_MAX("PL 1L Background Type: 0"); unsigned char* pixels; pixels = new unsigned char[SW* SH]; memset(pixels, 0, SW*SH); unsigned char* frameData; frameData = f->loadBlock(size); loadCompactedMem(size, frameData, pixels); delete[] frameData; animations->background = createSurface(pixels, SW, SH); delete[] pixels; // Use the most recently loaded palette video.setPalette(palettes->palette); } break; case EFFAniHeader: { unsigned char* blockData = f->loadBlock(size); animations->addFrame(EFFAniHeader, blockData, size); } break; case ERNAniHeader: case ERBAniHeader: case ERLAniHeader: case EMXAniHeader: break; case ERRAniHeader: // Reverse animation when end found animations->reverseAnimation = 1; break; case ERCAniHeader: { unsigned char* blockData = f->loadBlock(size); animations->addFrame(ERCAniHeader, blockData, size); }break; case ESquareAniHeader: // Full screen animation frame, that does n't clear the screen first. { unsigned char* blockData = f->loadBlock(size); animations->addFrame(ESquareAniHeader, blockData, size); } break; case ESTAniHeader: // Sound item { auto se = static_cast(f->loadChar()); if (!isValidSoundIndex(se)) { LOG_WARN("PL Audio tag with invalid index: %d", se); animations->lastFrame->soundId = SE::NONE; } else { LOG_MAX("PL Audio tag with index: %d", se); animations->lastFrame->soundId = se; } LOG_MAX("PL Audio tag play at: %x", f->loadChar()); LOG_MAX("PL Audio tag play offset: %x", f->loadChar()); } break; case 0: { int longvalue = f->loadInt(); while (longvalue == 0) { longvalue = f->loadInt(); nextPos += 4; } f->seek(-4, false); value = longvalue; LOG_MAX("PL Read Long: %x", value); } break; default: LOG_MAX("PL Read Unknown type: %x", value); validValue = false; break; } LOG_MAX("PL Read position after block should be: %d", nextPos); f->seek(nextPos, true); if(validValue) items++; } LOG_MAX("PL Parsed through number of items skipping 0 items: %d", items); LOG_MAX("PL Read position after parsing anim blocks: %d", f->tell()); } } } /** * Load JJ1 cutscene data. * * @param f File from which to load the data */ void JJ1Scene::loadData (File *f) { int loop; for (loop = 0; loop < dataItems; loop++) { f->seek(dataOffsets[loop], true); // Seek to data start unsigned short int dataLen = f->loadShort(); // Get get the length of the datablock LOG_MAX("Data dataLen: %d", dataLen); // AN if (dataLen == 0x4e41) { LOG_MAX("Data Type: ANI"); animations = new JJ1SceneAnimation(animations); animations->id = loop; loadAni(f, loop); } else { unsigned char type = f->loadChar(); LOG_MAX("Data Type: %d", type); switch (type) { case 3: case 4: // image case 5: case 6: { LOG_MAX("Data Type: Image"); LOG_MAX("Data Type Image index: %d", loop); unsigned short int width = f->loadShort(SW); // get width unsigned short int height; if (type == 3) height = f->loadChar(); // Get height else height = f->loadShort(SH); // Get height f->seek(-2, false); images = new JJ1SceneImage(images); images->image = f->loadSurface(width, height); images->id = loop; } break; default: LOG_MAX("Data Type: Palette"); LOG_MAX("Data Type Palette index: %d", loop); f->seek(-3, false); palettes = new JJ1ScenePalette(palettes); f->loadPalette(palettes->palette); palettes->id = loop; break; } } } } /** * Load JJ1 cutscene scripts. * * @param f File from which to load the scripts */ void JJ1Scene::loadScripts (File *f) { int loop; /*int bgIndex = 0;*/ int textAlignment = 0; int textFont = 0; int textShadow = -1; for(loop = 0; loop < scriptItems; loop++) { LOG_MAX("Parse Script: %d", loop); SDL_Rect textRect = { 0,0,0,0 }; f->seek(scriptStarts[loop], true); // Seek to data start if (f->loadChar() == 0x50) { // Script tag LOG_MAX("Script id: %x", f->loadShort()); int palette = f->loadShort(); LOG_MAX("Script default palette: %x", palette); pages[loop].paletteIndex = palette; bool breakloop = false; int pos = f->tell(); int textPosX = -1; int textPosY = -1; int extraheight = -1; bool textRectValid = false; while(!breakloop && pos < dataOffsets[0]) { unsigned char type = f->loadChar(); switch(type) { case ESceneYesNo: { pages[loop].askForYesNo = 1; LOG_MAX("ESceneYesNo"); }break; case ESceneStopMusic: { pages[loop].stopMusic = 1; LOG_MAX("ESceneStopMusic"); }break; case ESceneAnimation: { pages[loop].animLoops = f->loadInt(); pages[loop].animSpeed = f->loadShort(); pages[loop].animIndex = f->loadShort(); LOG_MAX("ESceneAnimation - loops: %d", pages[loop].animLoops); LOG_MAX("ESceneAnimation - speed: %d", pages[loop].animSpeed); LOG_MAX("ESceneAnimation - anim num: %d", pages[loop].animIndex); } break; case ESceneAnimationPlayAndContinue: { pages[loop].nextPageAfterAnim = f->loadChar(); LOG_MAX("ESceneAnimationPlayAndContinue: %d", pages[loop].nextPageAfterAnim); } break; case ESceneFadeType: { LOG_MAX("ESceneFadeType: %x", f->loadChar()); } break; case ESceneBackground: pages[loop].bgX[pages[loop].backgrounds] = f->loadShort(); pages[loop].bgY[pages[loop].backgrounds] = f->loadShort(); pages[loop].bgIndex[pages[loop].backgrounds] = f->loadShort(); LOG_MAX("ESceneBackground - xpos: %d", pages[loop].bgX[pages[loop].backgrounds]); LOG_MAX("ESceneBackground - ypos: %d", pages[loop].bgY[pages[loop].backgrounds]); LOG_MAX("ESceneBackground - index: %d", pages[loop].bgIndex[pages[loop].backgrounds]); pages[loop].backgrounds++; break; case ESceneMusic: // Music file name pages[loop].musicFile = f->loadString(); LOG_MAX("ESceneMusic: %s", pages[loop].musicFile); break; case ESceneSomethingElse: { LOG_MAX("ESceneSomethingElse"); } break; case ESceneTextRect: // String textRect.x = f->loadShort(); textRect.y = f->loadShort(); textRect.w = f->loadShort() - textRect.x; textRect.h = f->loadShort() - textRect.y; textRectValid = true; LOG_MAX("Text rectangle (x, y, w, h): %d, %d, %d, %d", textRect.x, textRect.y, textRect.w, textRect.h); break; case ESceneFontDefine: // Font defnition if (nFonts < 5) { fonts[nFonts].id = f->loadShort(); char *fontname = f->loadString(); LOG_MAX("ESceneFontDefine: %s", fontname); LOG_MAX("ESceneFontDefine with id: %d", fonts[nFonts].id); if (strcmp(fontname, "FONT2") == 0) fonts[nFonts].font = font2; else if (strcmp(fontname, "FONTBIG") == 0) fonts[nFonts].font = fontbig; else if (strcmp(fontname, "FONTTINY") == 0) fonts[nFonts].font = fontiny; else if (strcmp(fontname, "FONTMN1") == 0) fonts[nFonts].font = fontmn1; else if (strcmp(fontname, "FONTMN2") == 0) fonts[nFonts].font = fontmn2; else fonts[nFonts].font = font2; nFonts++; delete[] fontname; } break; case ESceneTextPosition: textPosX = f->loadShort(); textPosY = f->loadShort(); LOG_MAX("TextPosition (x, y): %d, %d", textPosX, textPosY); break; case ESceneTextColour: { LOG_MAX("ESceneTextColour: %x", f->loadShort()); } break; case ESceneFontFun: { LOG_MAX("ESceneFontFun len: %d", f->loadShort()); /*while (len) { unsigned char data = f->loadChar(); len--; }*/ } break; case ESceneFontIndex: textFont = f->loadShort(); LOG_MAX("ESceneFontIndex: %d", textFont); break; case ESceneTextVAdjust: extraheight = f->loadShort(); LOG_MAX("ESceneTextVAdjust: %d", extraheight); break; case ESceneBackgroundFade: { pages[loop].backgroundFade = f->loadShort(); LOG_MAX("ESceneBackgroundFade: %d", pages[loop].backgroundFade); } break; case ESceneTextShadow: { char enableShadow = f->loadChar(); if(enableShadow) { textShadow = f->loadChar(); } else { f->loadChar(); // Skip this value since shadows are turned off textShadow = -1; // Turn off shadow , -1 means no shadow colour } LOG_MAX("ESceneTextShadow: %d", textShadow); } break; case ESceneTextAlign: textAlignment = f->loadChar(); LOG_MAX("ESceneTextAlign: %d", textAlignment); break; case ESceneTextAlign2: { char a = f->loadChar(); int b = f->loadShort(); LOG_MAX("ESceneTextAlign2 (a, b): %x, %x", a, b); } break; case ESceneTextSomething: { char a = f->loadChar(); int b = f->loadShort(); LOG_MAX("ESceneTextSomething (a, b): %x, %x", a, b); } break; case ESceneTextLine: case ESceneTextBlock: { unsigned char datalen = f->loadChar(); LOG_MAX("Text len: %d", datalen); JJ1SceneText *text = pages[loop].texts + pages[loop].nTexts; if (datalen > 0) { text->text = f->loadBlock(datalen + 1); f->seek(-1, false); // Convert number placeholders for (int textPos = 1; textPos < datalen; textPos++) { if (text->text[textPos] == 0x8B) { if (loop >= 9) text->text[textPos - 1] = ((loop + 1) / 10) + 53; text->text[textPos] = ((loop + 1) % 10) + 53; } else if (text->text[textPos] == 0x8A) { if (scriptItems >= 10) text->text[textPos - 1] = (scriptItems / 10) + 53; text->text[textPos] = (scriptItems % 10) + 53; } } text->text[datalen] = 0; } else { text->text = new unsigned char[1]; text->text[0] = 0; } text->alignment = textAlignment; text->fontId = textFont; text->shadowColour = textShadow; if(textPosX != -1) { text->x = textPosX; text->y = textPosY; textPosX = -1; textPosY = -1; } if(textRectValid) { text->textRect = textRect; textRectValid = false; } if(extraheight != -1) { text->extraLineHeight = extraheight; extraheight = -1; } pages[loop].nTexts++; } break; case ESceneTime: pages[loop].pageTime = (f->loadShort()); LOG_MAX("Scene time: %d", pages[loop].pageTime); pages[loop].pageTime&=255; break; case ESceneBreaker: case 0x3e: pos = f->tell(); LOG_MAX("Parse script end at position: %d, with: %x", pos, type); breakloop = true; f->loadChar(); break; default: pos = f->tell(); LOG_MAX("Parse script end at position: %d, breaker: %x", pos, type); breakloop = true; break; } pos = f->tell(); } } } } openjazz-20240919/src/jj2/000077500000000000000000000000001467303304200151045ustar00rootroot00000000000000openjazz-20240919/src/jj2/level/000077500000000000000000000000001467303304200162135ustar00rootroot00000000000000openjazz-20240919/src/jj2/level/event/000077500000000000000000000000001467303304200173345ustar00rootroot00000000000000openjazz-20240919/src/jj2/level/event/jj2event.cpp000066400000000000000000000111661467303304200215740ustar00rootroot00000000000000 /** * * @file jj2event.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Added parts of levelload.cpp to level.cpp * - 29th June 2010: Created jj2level.cpp from parts of level.cpp * - 2nd July 2010: Created jj2event.cpp from parts of jj2level.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creating and freeing of JJ2 events. * */ #include "jj2event.h" #include "level/level.h" /** * Create event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param newProperties Event properties */ JJ2Event::JJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, int newProperties) { x = TTOF(gridX); y = TTOF(gridY); dx = 0; dy = 0; next = newNext; type = newType; properties = newProperties; endTime = 0; flipped = false; } /** * Delete all events */ JJ2Event::~JJ2Event () { if (next) delete next; } /** * Create pickup event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param TSF Whether or not the level uses TSF animations * @param newProperties Event properties */ PickupJJ2Event::PickupJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF, int newProperties) : JJ2Event(newNext, gridX, gridY, newType, newProperties) { floating = true; animSet = TSF? 71: 67; } /** * Delete pickup event */ PickupJJ2Event::~PickupJJ2Event () { } /** * Create ammo pickup event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param TSF Whether or not the level uses TSF animations */ AmmoJJ2Event::AmmoJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF) : PickupJJ2Event(newNext, gridX, gridY, newType, TSF, 0) { } /** * Delete ammo pickup event */ AmmoJJ2Event::~AmmoJJ2Event () { } /** * Create coin/gem pickup event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param TSF Whether or not the level uses TSF animations */ CoinGemJJ2Event::CoinGemJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF) : PickupJJ2Event(newNext, gridX, gridY, newType, TSF, 0) { } /** * Delete coin/gem pickup event */ CoinGemJJ2Event::~CoinGemJJ2Event () { } /** * Create food pickup event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param TSF Whether or not the level uses TSF animations */ FoodJJ2Event::FoodJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF) : PickupJJ2Event(newNext, gridX, gridY, newType, TSF, 0) { } /** * Delete food pickup event */ FoodJJ2Event::~FoodJJ2Event () { } /** * Create spring event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param TSF Whether or not the level uses TSF animations * @param newProperties Event properties */ SpringJJ2Event::SpringJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF, int newProperties) : JJ2Event(newNext, gridX, gridY, newType, newProperties) { animSet = TSF? 96: 92; } /** * Delete spring event */ SpringJJ2Event::~SpringJJ2Event () { } /** * Create placeholder event * * @param newNext Next event * @param gridX X-coordinate * @param gridY Y-coordinate * @param newType Event type * @param TSF Whether or not the level uses TSF animations * @param newProperties Event properties */ OtherJJ2Event::OtherJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF, int newProperties) : JJ2Event(newNext, gridX, gridY, newType, newProperties) { animSet = TSF? 71: 67; } /** * Delete placeholder event */ OtherJJ2Event::~OtherJJ2Event () { } /** * Initiate the destruction of the event * * @param ticks Time */ void JJ2Event::destroy (unsigned int ticks) { endTime = ticks + 500; } /** * Get the event's type * * @return Event type */ unsigned char JJ2Event::getType () { return type; } /** * Delete this event * * @return The next event */ JJ2Event* JJ2Event::remove () { JJ2Event *oldNext; oldNext = next; next = NULL; delete this; return oldNext; } openjazz-20240919/src/jj2/level/event/jj2event.h000066400000000000000000000063011467303304200212340ustar00rootroot00000000000000 /** * * @file jj2event.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 29th June 2010: Created jj2level.h from parts of level.h * - 2nd July 2010: Created jj2event.h from parts of jj2level.h * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _JJ2EVENT_H #define _JJ2EVENT_H #include "level/movable.h" // Classes class Anim; /// JJ2 level "movable" event class JJ2Event : public Movable { private: JJ2Event* next; protected: unsigned char type; int properties; ///< Event-specific options unsigned int endTime; ///< Point at which the event will terminate bool flipped; ///< Whether or not the sprite image should be flipped JJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, int newProperties); void destroy (unsigned int ticks); bool prepareStep (unsigned int ticks, int msps); bool prepareDraw (unsigned int ticks, int change); JJ2Event* remove (); public: virtual ~JJ2Event (); unsigned char getType (); virtual JJ2Event* step (unsigned int ticks, int msps) = 0; virtual void draw (unsigned int ticks, int change) = 0; }; /// JJ2 level pickup event class PickupJJ2Event : public JJ2Event { private: bool floating; protected: unsigned char animSet; PickupJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF, int newProperties); virtual ~PickupJJ2Event (); JJ2Event* step (unsigned int ticks, int msps); }; /// JJ2 level ammo class AmmoJJ2Event : public PickupJJ2Event { public: AmmoJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF); ~AmmoJJ2Event (); void draw (unsigned int ticks, int change); }; /// JJ2 level gold/silver coin class CoinGemJJ2Event : public PickupJJ2Event { private: void mapPalette (Anim* anim, int start); public: CoinGemJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF); ~CoinGemJJ2Event (); void draw (unsigned int ticks, int change); }; /// JJ2 level food class FoodJJ2Event : public PickupJJ2Event { public: FoodJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF); ~FoodJJ2Event (); void draw (unsigned int ticks, int change); }; /// JJ2 level spring class SpringJJ2Event : public JJ2Event { private: unsigned char animSet; public: SpringJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF, int newProperties); ~SpringJJ2Event (); JJ2Event* step (unsigned int ticks, int msps); void draw (unsigned int ticks, int change); }; /// Unimplemented JJ2 level event class OtherJJ2Event : public JJ2Event { private: unsigned char animSet; public: OtherJJ2Event (JJ2Event* newNext, int gridX, int gridY, unsigned char newType, bool TSF, int newProperties); ~OtherJJ2Event (); JJ2Event* step (unsigned int ticks, int msps); void draw (unsigned int ticks, int change); }; #endif openjazz-20240919/src/jj2/level/event/jj2eventframe.cpp000066400000000000000000000246121467303304200226070ustar00rootroot00000000000000 /** * * @file jj2eventframe.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Added parts of levelload.cpp to level.cpp * - 29th June 2010: Created jj2level.cpp from parts of level.cpp * - 2nd July 2010: Created jj2eventframe.cpp from parts of jj2level.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the playing of JJ2 levels. * */ #include "jj2event.h" #include "../jj2level.h" #include "../jj2levelplayer.h" #include "io/gfx/anim.h" #include "io/gfx/font.h" #include "io/gfx/video.h" /// Look-up table for ammo animations (in animSet 0) const unsigned char ammoAnims[] = { 29, // Ice 25, // Bouncer 34, // Seeker 49, // RF 57, // Toaster 59, // TNT 62, // Pellets 69 // Sparks }; /// Look-up table for food etc. animations (in animSet 67/71) const unsigned char pickupAnims[] = { 0, // 0 0, // 1 0, // 2 0, // 3 0, // 4 0, // 5 0, // 6 0, // 7 0, // 8 0, // 9 0, // 10 0, // 11 0, // 12 0, // 13 0, // 14 0, // 15 0, // 16 0, // 17 0, // 18 0, // 19 0, // 20 0, // 21 0, // 22 0, // 23 0, // 24 0, // 25 0, // 26 0, // 27 0, // 28 0, // 29 0, // 30 0, // 31 0, // 32 0, // 33 0, // 34 0, // 35 0, // 36 0, // 37 0, // 38 0, // 39 0, // 40 0, // 41 0, // 42 0, // 43 84, // Silver coin 37, // Gold coin 0, // 46 0, // 47 0, // 48 0, // 49 0, // 50 0, // 51 0, // 52 55, // Ice crate 54, // Bouncer crate 56, // Seeker crate 57, // RF crate 58, // Toaster crate 90, // Armed TNT 36, // Board 0, // 60 29, // Rapid fire (can also be 30) 0, // 62 35, // Red gem 35, // Green gem 35, // Blue gem 35, // Purple gem 34, // Large red gem 0, // 68 3, // Ammo barrel 0, // 70 0, // 71 82, // Energy 72, // Full energy 31, // Fire shield 10, // Bubble shield 51, // Plasma shield 0, // 77 0, // 78 33, // High jump 0, // 1-up 28, // exit signpost 0, // 82 14, // Checkpoint 0, // 84 0, // 85 0, // 86 0, // 87 0, // 88 87, // Extra time 42, // Freeze 0, // 91 0, // 92 0, // 93 0, // 94 52, // Trigger crate 0, // 96 0, // 97 0, // 98 0, // 99 0, // 100 0, // 101 0, // 102 0, // 103 0, // 104 0, // 105 0, // 106 0, // 107 0, // 108 0, // 109 0, // 110 0, // 111 0, // 112 0, // 113 0, // 114 0, // 115 0, // 116 0, // 117 0, // 118 0, // 119 0, // 120 0, // 121 0, // 122 0, // 123 0, // 124 0, // 125 0, // 126 0, // 127 0, // 128 0, // 129 0, // 130 60, // Blaster PU (can also be 83) 61, // Bouncer PU 62, // Ice PU 63, // Seeker PU 64, // RF PU 65, // Toaster PU 0, // 137 0, // 138 0, // 139 0, // 140 1, // Apple 2, // Banana 16, // Cherry 71, // Orange 74, // Pear 79, // Pretzel 81, // Strawberry 0, // 148 0, // 149 0, // 150 0, // 151 0, // 152 0, // 153 48, // Lemon 50, // Lime 89, // Thing 92, // Watermelon 73, // Peach 38, // Grapes 49, // Lettuce 26, // Aubergine 23, // Cucumber 75, // Jazzade 20, // Cola 53, // Milk 76, // Tart 12, // Cake 25, // Doughnut 24, // Cupcake 18, // Crisps 13, // Sweet 19, // Chocolate 43, // Ice cream 11, // Burger 77, // Pizza 32, // Chips 17, // Chicken drumstick 80, // Sandwich 88, // Taco 91, // Hot dog 39, // Ham 15, // Cheese 0, // 183 0, // 184 0, // 185 0, // 186 0, // 187 0, // 188 0, // 189 0, // 190 0, // 191 0, // 192 0, // 193 0, // 194 0, // 195 0, // 196 0, // 197 0, // 198 0, // 199 0, // 200 0, // 201 0, // 202 0, // 203 0, // 204 0, // 205 0, // 206 0, // 207 0, // 208 0, // 209 0, // 210 0, // 211 0, // 212 0, // 213 0, // 214 0, // 215 0, // 216 0, // 217 0, // 218 0, // 219 66, // Pellet PU 67, // Sparks PU }; /** * Functionality required by all event types on each iteration * * @param ticks Time * @param msps Ticks per step * * @return Whether or not the event should be deleted */ bool JJ2Event::prepareStep (unsigned int ticks, int msps) { int count; // Process next event(s) if (next) next = next->step(ticks, msps); // If the reaction time has expired if (endTime && (ticks > endTime)) { return true; } if (endTime) return false; // Handle contact with player for (count = 0; count < nPlayers; count++) { JJ2LevelPlayer *levelPlayer = players[count].getJJ2LevelPlayer(); // Check if the player is touching the event if (levelPlayer->overlap(x, y, F32, F32)) { // If the player picks up the event, destroy it if (levelPlayer->touchEvent(this, ticks, msps)) destroy(ticks); } } return false; } /** * Functionality required by all event types on each draw * * @param ticks Time * @param change Time since last iteration * * @return Whether or not the event shouldn't be drawn */ bool JJ2Event::prepareDraw (unsigned int ticks, int change) { // Draw next event(s) if (next) next->draw(ticks, change); // Don't draw if too far off-screen if ((x < viewX - F64) || (y < viewY - F64) || (x > viewX + ITOF(canvasW) + F64) || (y > viewY + ITOF(canvasH) + F64)) return true; return false; } /** * Pickup event iteration. * * @param ticks Time * @param msps Ticks per step * * @return Remaining event */ JJ2Event* PickupJJ2Event::step (unsigned int ticks, int msps) { if (prepareStep(ticks, msps)) return remove(); if (!floating) { if (jj2Level->checkMaskDown(x, y + F4, false)) { dy += 1000 / msps; } else { dy = 0; } } else { /// @todo Check for bullet overlap // floating = false; } return this; } /** * Draw ammo pickup event. * * @param ticks Time * @param change Time since last iteration */ void AmmoJJ2Event::draw (unsigned int ticks, int change) { Anim* an; int drawX, drawY; if (prepareDraw(ticks, change)) return; drawX = getDrawX(change); drawY = getDrawY(change); /// @todo Check if ammo is powered up if (!endTime) an = jj2Level->getAnim(0, ammoAnims[type - 33], flipped); else an = jj2Level->getAnim(animSet, 86, flipped); an->setFrame((int)ticks / 60, true); an->draw(drawX + F16, drawY + F16 + F32); } /** * Map gem animation's palette. * * @param anim Gem animation * @param start Index of first colour */ void CoinGemJJ2Event::mapPalette (Anim* anim, int start) { SDL_Color palette[MAX_PALETTE_COLORS]; int count; for (count = 0; count < 112; count++) palette[count + 128].r = palette[count + 128].g = palette[count + 128].b = start + 6 - (count >> 4); for (; count < 128; count++) palette[count + 128].r = palette[count + 128].g = palette[count + 128].b = 255; anim->setPalette(palette, 128, 128); } /** * Draw coin/gem pickup event. * * @param ticks Time * @param change Time since last iteration */ void CoinGemJJ2Event::draw (unsigned int ticks, int change) { Anim* an; int drawX, drawY; if (prepareDraw(ticks, change)) return; drawX = getDrawX(change); drawY = getDrawY(change); if (endTime) { an = jj2Level->getAnim(animSet, 86, flipped); an->setFrame((int)ticks / 60, true); an->draw(drawX + F16, drawY + F16 + F32); } if (type == 44) an = jj2Level->getAnim(animSet, 84, flipped); // Silver coin else if (type == 45) an = jj2Level->getAnim(animSet, 37, flipped); // Gold coin else an = jj2Level->getAnim(animSet, 35, flipped); // Gem an->setFrame((int)ticks / 60, true); switch (type) { case 63: // Red gem mapPalette(an, 48); break; case 64: // Green gem mapPalette(an, 80); break; case 65: // Blue gem mapPalette(an, 32); break; case 66: // Purple gem mapPalette(an, 88); break; } if (endTime) { drawX += (ticks + 500 - endTime) << 8; drawY += (ticks + 500 - endTime) << 10; } an->draw(drawX + F16, drawY + F16 + F32); } /** * Draw food pickup event. * * @param ticks Time * @param change Time since last iteration */ void FoodJJ2Event::draw (unsigned int ticks, int change) { Anim* an; int drawX, drawY; if (prepareDraw(ticks, change)) return; drawX = getDrawX(change); drawY = getDrawY(change); // Use look-up table if (!endTime) an = jj2Level->getAnim(animSet, pickupAnims[type], flipped); else an = jj2Level->getAnim(animSet, 86, flipped); an->setFrame((int)ticks / 60, true); an->draw(drawX + F16, drawY + F16 + F32); } /** * Spring event iteration. * * @param ticks Time * @param msps Ticks per step * * @return Remaining event */ JJ2Event* SpringJJ2Event::step (unsigned int ticks, int msps) { if (prepareStep(ticks, msps)) return remove(); if (!jj2Level->checkMaskDown(x, y + F1, true)) y += F1; return this; } /** * Draw spring event. * * @param ticks Time * @param change Time since last iteration */ void SpringJJ2Event::draw (unsigned int ticks, int change) { Anim* an; int drawX, drawY; if (prepareDraw(ticks, change)) return; drawX = getDrawX(change); drawY = getDrawY(change); switch (type) { case 60: // Frozen green spring an = jj2Level->getAnim(animSet, 5, flipped); break; case 62: // Spring crate an = jj2Level->getAnim(animSet, 0, flipped); break; case 85: // Red spring an = jj2Level->getAnim(animSet, 7, flipped); break; case 86: // Green spring an = jj2Level->getAnim(animSet, 5, flipped); break; case 87: // Blue spring an = jj2Level->getAnim(animSet, 0, flipped); break; default: return; } an->setFrame(0, true); an->draw(drawX + F16, drawY + F16); } /** * Placeholder event iteration. * * @param ticks Time * @param msps Ticks per step * * @return Remaining event */ JJ2Event* OtherJJ2Event::step (unsigned int ticks, int msps) { if (prepareStep(ticks, msps)) return remove(); return this; } /** * Draw placeholder event. * * @param ticks Time * @param change Time since last iteration */ void OtherJJ2Event::draw (unsigned int ticks, int change) { Anim* an; int drawX, drawY; if (prepareDraw(ticks, change)) return; drawX = getDrawX(change); drawY = getDrawY(change); switch (type) { default: if ((type <= 221) && pickupAnims[type]) { an = jj2Level->getAnim(animSet, pickupAnims[type], flipped); an->setFrame((int)ticks / 60, true); an->draw(drawX + F16, drawY + F16 + F32); } else if (!endTime) { drawRect(FTOI(drawX) + 8, FTOI(drawY) + 8, 16, 16, type); } panelBigFont->showNumber(type, FTOI(drawX) + 24, FTOI(drawY) + 12); return; } an->draw(drawX + F16, drawY + F16); } openjazz-20240919/src/jj2/level/jj2layer.cpp000066400000000000000000000110741467303304200204440ustar00rootroot00000000000000 /** * * @file jj2layer.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Created levelframe.cpp from parts of level.cpp * - 29th June 2010: Created jj2levelframe.cpp from parts of levelframe.cpp * - 30th June 2010: Created jj2layer.cpp from parts of jj2levelframe.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Handles JJ2 level layers. * */ #include "jj2level.h" #include "io/gfx/video.h" /** * Create a blank 1-by-1 layer. */ JJ2Layer::JJ2Layer () { width = height = 1; tileX = tileY = 0; limit = true; warp = false; xSpeed = ySpeed = 0; grid = new JJ2Tile *[1]; *grid = new JJ2Tile[1]; (*grid)->tile = 0; } /** * Create a blank layer. * * @param newWidth The width of the layer (in tiles) * @param newHeight The height of the layer (in tiles) * @param newXSpeed The relative horizontal speed of the layer * @param newYSpeed The relative vertical speed of the layer * @param flags Layer flags */ JJ2Layer::JJ2Layer (int flags, int newWidth, int newHeight, fixed newXSpeed, fixed newYSpeed) { int row; width = newWidth; height = newHeight; grid = new JJ2Tile *[height]; *grid = new JJ2Tile[width * height]; for (row = 0; row < height; row++) grid[row] = *grid + (row * width); tileX = flags & 1; tileY = flags & 2; limit = flags & 4; warp = flags & 8; xSpeed = newXSpeed; ySpeed = newYSpeed; } /** * Delete the layer. */ JJ2Layer::~JJ2Layer () { delete[] *grid; delete[] grid; } /** * Get flipped. We aim to offend! * * @param x The x-coordinate of the tile (in tiles) * @param y The y-coordinate of the tile (in tiles) * * @return Whether or not the tile is flipped horizontally */ bool JJ2Layer::getFlipped (int x, int y) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return false; return grid[y][x].flipped; } /** * Get the height of the layer. * * @return The height of the layer (in tiles) */ int JJ2Layer::getHeight () { return height; } /** * Get the tile for the given co-ordinates. * * @param x The x-coordinate of the tile (in tiles) * @param y The y-coordinate of the tile (in tiles) * * @return The number of the tile */ int JJ2Layer::getTile (int x, int y) { if ((x < 0) || (y < 0)) return 0; if ((x >= width) && !tileX) return 0; if ((y >= height) && !tileY) return 0; return grid[y % height][x % width].tile; } /** * Get the width of the layer. * * @return The width of the layer (in tiles) */ int JJ2Layer::getWidth () { return width; } /** * Set the frame of the tile at the given co-ordinates. * * @param x The x-coordinate of the tile (in tiles) * @param y The y-coordinate of the tile (in tiles) * @param frame The new frame */ void JJ2Layer::setFrame (int x, int y, unsigned char frame) { grid[y][x].frame = frame; } /** * Set the tile at the given co-ordinates. * * @param x The x-coordinate of the tile (in tiles) * @param y The y-coordinate of the tile (in tiles) * @param tile The number of the tile * @param TSF Whether or not this is a TSF tile * @param tiles The total number of tiles */ void JJ2Layer::setTile (int x, int y, unsigned short int tile, bool TSF, int tiles) { JJ2Tile* ge; ge = grid[y] + x; if (TSF) { ge->flipped = tile & 0x1000; ge->tile = tile & 0xFFF; } else { ge->flipped = tile & 0x400; ge->tile = tile & 0x3FF; } if (ge->tile > tiles) ge->tile = 0; ge->frame = 0; } /** * Draw the layer. * * @param tileSet The tile set to use for non-flipped tiles * @param flippedTileSet The tile set to use for flipped tiles */ void JJ2Layer::draw (SDL_Surface* tileSet, SDL_Surface* flippedTileSet) { SDL_Rect src, dst; int vX, vY; int x, y; // Set tile drawing dimensions src.w = TTOI(1); src.h = TTOI(1); src.x = 0; // Calculate the layer view vX = FTOI(FTOI(viewX) * xSpeed); vY = FTOI(FTOI(viewY) * ySpeed); if (limit) { if (!tileX) { if (vX + canvasW > TTOI(width)) vX = TTOI(width) - canvasW; } if (!tileY) { vY -= canvasH - SH; if (vY + canvasH > TTOI(height)) vY = TTOI(height) - canvasH; } } for (y = 0; y <= ITOT(canvasH - 1) + 1; y++) { for (x = 0; x <= ITOT(canvasW - 1) + 1; x++) { dst.x = TTOI(x) - (vX & 31); dst.y = TTOI(y) - (vY & 31); src.y = TTOI(getTile(x + ITOT(vX), y + ITOT(vY))); if (src.y) SDL_BlitSurface(getFlipped(x + ITOT(vX), y + ITOT(vY))? flippedTileSet: tileSet, &src, canvas, &dst); } } } openjazz-20240919/src/jj2/level/jj2level.cpp000066400000000000000000000226301467303304200204370ustar00rootroot00000000000000 /** * * @file jj2level.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Added parts of levelload.cpp to level.cpp * - 29th June 2010: Created jj2level.cpp from parts of level.cpp * - 2nd July 2010: Created jj2event.cpp from parts of jj2level.cpp * - 2nd July 2010: Created jj2eventframe.cpp from parts of jj2level.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creating, playing and freeing of JJ2 levels. * */ #include "event/jj2event.h" #include "jj2level.h" #include "jj2levelplayer.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" #include /** * Create a JJ2 level. * * @param owner The current game * @param fileName Name of the file containing the level data. * @param checkpoint Whether or not the player(s) will start at a checkpoint * @param multi Whether or not the level will be multi-player */ JJ2Level::JJ2Level (Game* owner, char* fileName, bool checkpoint, bool multi) : Level(owner) { int ret; // Load level data ret = load(fileName, checkpoint); if (ret < 0) throw ret; multiplayer = multi; } /** * Delete the JJ2 level. */ JJ2Level::~JJ2Level () { int count; if (events) delete events; delete[] *mods; delete[] mods; for (count = 0; count < LAYERS; count++) delete layers[count]; delete[] flippedMask; delete[] mask; delete[] musicFile; delete[] nextLevel; for (count = 0; count < nAnimSets; count++) { if (animSets[count]) delete[] animSets[count]; } delete[] animSets; delete[] spriteSet; SDL_FreeSurface(flippedTileSet); SDL_FreeSurface(tileSet); delete font; // Restore panel font palette panelBigFont->restorePalette(); panelSmallFont->restorePalette(); } /** * Determine whether or not the given point is solid when travelling upwards. * * @param x X-coordinate * @param y Y-coordinate * * @return Solidity */ bool JJ2Level::checkMaskUp (fixed x, fixed y) { int tX, tY; tX = FTOT(x); tY = FTOT(y); // Anything off the edge of the map is solid if ((x < 0) || (y < 0) || (tX >= layer->getWidth()) || (tY >= layer->getHeight())) return true; // Event 1 is one-way // Event 3 is vine // Event 4 is hook if ((mods[tY][tX].type == 1) || (mods[tY][tX].type == 3) || (mods[tY][tX].type == 4)) return false; // Check the mask in the tile in question return (layer->getFlipped(tX, tY)? flippedMask: mask)[(layer->getTile(tX, tY) << 10) + ((y >> 5) & 992) + ((x >> 10) & 31)]; } /** * Determine whether or not the given point is solid when travelling downwards. * * @param x X-coordinate * @param y Y-coordinate * @param drop Whether or not the player is dropping * * @return Solidity */ bool JJ2Level::checkMaskDown (fixed x, fixed y, bool drop) { int tX, tY; tX = FTOT(x); tY = FTOT(y); // Anything off the edge of the map is solid if ((x < 0) || (y < 0) || (tX >= layer->getWidth()) || (tY >= layer->getHeight())) return true; // Event 3 is vine // Event 4 is hook if (drop && ((mods[tY][tX].type == 3) || (mods[tY][tX].type == 4))) return false; // Check the mask in the tile in question return (layer->getFlipped(tX, tY)? flippedMask: mask)[(layer->getTile(tX, tY) << 10) + ((y >> 5) & 992) + ((x >> 10) & 31)]; } /** * Set which level will come next. * * @param fileName Next level's file name */ void JJ2Level::setNext (char* fileName) { delete[] nextLevel; nextLevel = createString(fileName); if (multiplayer) { unsigned char buffer[MTL_L_PROP]; buffer[0] = MTL_L_PROP; buffer[1] = MT_L_PROP; buffer[2] = 0; // set next level buffer[3] = 0; buffer[4] = 0; game->send(buffer); } } /** * Set the frame of the animated tile at the given location. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * @param frame The new frame */ void JJ2Level::setFrame (int gridX, int gridY, unsigned char frame) { layer->setFrame(gridX, gridY, frame); if (multiplayer) { unsigned char buffer[MTL_L_GRID]; buffer[0] = MTL_L_GRID; buffer[1] = MT_L_GRID; buffer[2] = gridX & 0xFF; buffer[3] = gridY & 0xFF; buffer[4] = 0; // tile variable buffer[5] = frame; buffer[6] = (gridX >> 8) & 0xFF; buffer[7] = (gridY >> 8) & 0xFF; game->send(buffer); } } /** * Get the modifier event for the given tile. * * @param gridX X-coordinate of the tile * @param gridY Y-coordinate of the tile * * @return Modifier event */ JJ2Modifier* JJ2Level::getModifier (int gridX, int gridY) { return mods[gridY] + gridX; } /** * Get a sprite. * * @param sprite Sprite number * * @return Sprite */ Sprite* JJ2Level::getSprite (unsigned char sprite) { return spriteSet + sprite; } /** * Get an animation. * * @param set Animation set number * @param anim Animation number * @param flipped Whether or not the animation should be flipped horizontally * * @return Animation */ Anim* JJ2Level::getAnim (int set, int anim, bool flipped) { return (flipped? flippedAnimSets: animSets)[set] + anim; } /** * Get a player animation. * * @param character Character * @param anim Animation number * @param flipped Whether or not the animation should be flipped horizontally * * @return Animation */ Anim* JJ2Level::getPlayerAnim (int character, int anim, bool flipped) { int set; if (TSF) { if (character == 1) set = 61; else if (character == 2) set = 89; else set = 55; } else { if (character == 1) set = 85; else set = 54; } return getAnim(set, playerAnims[anim], flipped); } /** * Set the water level. * * @param gridY New water level y-coordinate * @param instant Whether or not the change is instant */ void JJ2Level::setWaterLevel (int gridY, bool instant) { waterLevelTarget = TTOF(gridY); if (instant) waterLevel = waterLevelTarget - F8; if (multiplayer) { unsigned char buffer[MTL_L_PROP]; buffer[0] = MTL_L_PROP; buffer[1] = MT_L_PROP; buffer[2] = 1; // set water level buffer[3] = gridY & 0xFF; buffer[4] = (gridY >> 8) & 0xFF; game->send(buffer); } } /** * Determine the water level. * * @return The y-coordinate of the water level */ fixed JJ2Level::getWaterLevel () { return waterLevel; } /** * Move a player to a warp target. * * @param player The player to move * @param id The warp target ID */ void JJ2Level::warp (JJ2LevelPlayer *player, int id) { int x, y; for (y = 0; y < layer->getHeight(); y++) { for (x = 0; x < layer->getWidth(); x++) { if ((mods[y][x].type == 240) && ((mods[y][x].properties & 255) == id)) { player->setPosition(TTOF(x), TTOF(y)); return; } } } } /** * Interpret data received from client/server * * @param buffer Received data */ void JJ2Level::receive (unsigned char* buffer) { switch (buffer[1]) { case MT_L_PROP: if (buffer[2] == 1) { waterLevelTarget = TTOF(buffer[3] + (buffer[4] << 8)); } else if (buffer[2] == 2) { if (stage == LS_NORMAL) endTime += buffer[3] * 1000; } break; case MT_L_GRID: if (buffer[4] == 0) layer->setFrame(buffer[2] + (buffer[6] << 8), buffer[3] + (buffer[7] << 8), buffer[5]); break; case MT_L_STAGE: stage = LevelStage(buffer[2]); break; } } /** * Play the level. * * @return Error code */ int JJ2Level::play () { JJ2LevelPlayer* jj2LevelPlayer; bool pmessage, pmenu; int option; unsigned int returnTime; int count; jj2LevelPlayer = localPlayer->getJJ2LevelPlayer(); tickOffset = globalTicks; ticks = T_STEP; steps = 0; pmessage = pmenu = false; option = 0; returnTime = 0; video.setPalette(palette); playMusic(musicFile); while (true) { int ret = loop(pmenu, option, pmessage); if (ret < 0) return ret; // Check if level has been won if (game && returnTime && (ticks > returnTime)) { ret = game->setLevel(nextLevel); if (ret < 0) return ret; return WON; } // Process frame-by-frame activity while (getTimeChange() >= T_STEP) { // Apply controls to local player for (count = 0; count < PCONTROLS; count++) localPlayer->setControl(count, controls.getState(count)); ret = step(); steps++; if (ret) return ret; } // Draw the graphics draw(); // If paused, draw "PAUSE" if (pmessage && !pmenu) font->showString("pause", (canvasW >> 1) - 44, 32); // If paused, silence music pauseMusic(pmessage && !pmenu); if (stage == LS_END) { // The level is over, so draw gem counts if (!returnTime) { returnTime = ticks + 3000; playSound(SE::UPLOOP); } // Display statistics font->showString("red gems", (canvasW >> 1) - 152, (canvasH >> 1) - 60); font->showNumber(jj2LevelPlayer->getGems(0), (canvasW >> 1) + 124, (canvasH >> 1) - 60); font->showString("green gems", (canvasW >> 1) - 152, (canvasH >> 1) - 40); font->showNumber(jj2LevelPlayer->getGems(1), (canvasW >> 1) + 124, (canvasH >> 1) - 40); font->showString("blue gems", (canvasW >> 1) - 152, (canvasH >> 1) - 20); font->showNumber(jj2LevelPlayer->getGems(2), (canvasW >> 1) + 124, (canvasH >> 1) - 20); } // Draw statistics, menu etc. drawOverlay(JJ2_BLACK, pmenu, option, 71, 31, -8); } return E_NONE; } openjazz-20240919/src/jj2/level/jj2level.h000066400000000000000000000157701467303304200201130ustar00rootroot00000000000000 /** * * @file jj2level.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 29th June 2010: Created jj2level.h from parts of level.h * - 2nd July 2010: Created jj2event.h from parts of jj2level.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ /* "Tile" is a flexible term. Here it is used to refer specifically to the individual elements of the tile set. "Tiles" in the context of level units are referred to as grid elements. */ #ifndef _JJ2LEVEL_H #define _JJ2LEVEL_H #include "level/level.h" #include "io/gfx/anim.h" #include "OpenJazz.h" // Constants // Number of layers #define LAYERS 8 // Player animations #define JJ2PA_BOARD 0 #define JJ2PA_BOARDSW 1 #define JJ2PA_STOMP 2 #define JJ2PA_DEAD 3 #define JJ2PA_DIE 4 #define JJ2PA_CROUCH1 5 #define JJ2PA_CROUCHED 6 #define JJ2PA_CROUCHSHOOT 7 #define JJ2PA_CROUCH2 8 #define JJ2PA_VINE 9 #define JJ2PA_EXIT1 10 #define JJ2PA_FALL 11 #define JJ2PA_STOMPING 12 #define JJ2PA_LAND 13 #define JJ2PA_STANDSHOOT 14 #define JJ2PA_STANDSHOOTUP 15 #define JJ2PA_WHIP1 16 #define JJ2PA_UNFROG 17 #define JJ2PA_HOOKWHIP 18 #define JJ2PA_HOOKDIAG 19 #define JJ2PA_HOOKSHOOTUP 20 #define JJ2PA_HOOK1 21 #define JJ2PA_HOOK2 22 #define JJ2PA_HOOKWHIPUP 23 #define JJ2PA_HOOKSHOOT 24 #define JJ2PA_HELI 25 #define JJ2PA_HELIWHIP 26 #define JJ2PA_HELISHOOT 27 #define JJ2PA_HPOLE 28 #define JJ2PA_HURT 29 #define JJ2PA_WAIT1 30 #define JJ2PA_WAIT2 31 #define JJ2PA_WAIT3 32 #define JJ2PA_WAIT4 33 #define JJ2PA_WAIT5 34 #define JJ2PA_FALLWHIP 35 #define JJ2PA_FALLSHOOT 36 #define JJ2PA_FLOAT1 37 #define JJ2PA_FLOAT2 38 #define JJ2PA_UP1 39 #define JJ2PA_EDGE 40 #define JJ2PA_CARRY 41 #define JJ2PA_UNLOAD 42 #define JJ2PA_LOAD 43 #define JJ2PA_LOOKUP 44 #define JJ2PA_WOOZYWALK 45 #define JJ2PA_PUSH 46 #define JJ2PA_WHIP2 47 #define JJ2PA_EXIT2 48 #define JJ2PA_SPEED1 49 #define JJ2PA_SPEED2 50 #define JJ2PA_FALLMOVE 51 #define JJ2PA_JUMP 52 #define JJ2PA_BALL 53 #define JJ2PA_WALKSHOOT 54 #define JJ2PA_RUN 55 #define JJ2PA_SPEEDRUN 56 #define JJ2PA_STOP1 57 #define JJ2PA_MYSTERY 58 #define JJ2PA_STOP2 59 #define JJ2PA_UP2 60 #define JJ2PA_STAND 61 #define JJ2PA_POWER 62 #define JJ2PA_POWEREND 63 #define JJ2PA_POWERSTART 64 #define JJ2PA_WOOZYSTAND 65 #define JJ2PA_SWIMDOWN 66 #define JJ2PA_SWIM 67 #define JJ2PA_SWIMDIAGDOWN 68 #define JJ2PA_SWIMDIAGUP 69 #define JJ2PA_SWIMUP 70 #define JJ2PA_VINESDIAG 71 #define JJ2PA_WARPOUT 72 #define JJ2PA_WARPFALLIN 73 #define JJ2PA_WARPFALL 74 #define JJ2PA_WARPFALLOUT 75 #define JJ2PA_WARPIN 76 #define JJ2PA_VPOLE 77 #define JJ2PANIMS 78 /* Number of player animations. */ // Black palette index #define JJ2_BLACK 0 // Datatypes /// JJ2 level tile typedef struct { unsigned short int tile; ///< Indexes the tile set unsigned char frame; ///< Current frame being used (for animated tiles) bool flipped; ///< Whether or not the tile image and mask are flipped } JJ2Tile; /// JJ2 level tile modifier event typedef struct { unsigned char type; int properties; } JJ2Modifier; // Classes class Font; ///< JJ2 level parallaxing layer class JJ2Layer { private: JJ2Tile** grid; ///< Layer tiles int width; ///< Width (in tiles) int height; ///< Height (in tiles) bool tileX; ///< Repeat horizontally bool tileY; ///< Repeat vertically bool limit; ///< Do not view beyond edges bool warp; ///< Warp effect fixed xSpeed; ///< Relative horizontal speed fixed ySpeed; ///< Relative vertical speed JJ2Layer(const JJ2Layer&); // non construction-copyable JJ2Layer& operator=(const JJ2Layer&); // non copyable public: JJ2Layer (); JJ2Layer (int flags, int newWidth, int newHeight, fixed newXSpeed, fixed newYSpeed); ~JJ2Layer (); bool getFlipped (int x, int y); int getHeight (); int getTile (int x, int y); int getWidth (); void setFrame (int x, int y, unsigned char frame); void setTile (int x, int y, unsigned short int tile, bool TSF, int tiles); void draw (SDL_Surface* tileSet, SDL_Surface* flippedTileSet); }; class JJ2Event; class JJ2LevelPlayer; /// JJ2 level class JJ2Level : public Level { private: SDL_Surface* tileSet; ///< Tile images SDL_Surface* flippedTileSet; ///< Tile images (flipped) JJ2Event* events; ///< "Movable" events Font* font; ///< On-screen message font char* mask; ///< Tile masks char* flippedMask; ///< Tile masks (flipped) char* musicFile; ///< Music file name char* nextLevel; ///< Next level file name Sprite* spriteSet; ///< Sprite images Sprite* flippedSpriteSet; ///< Sprite images (flipped) Anim** animSets; ///< Animation sets Anim** flippedAnimSets; ///< Animation sets (flipped) char playerAnims[JJ2PANIMS]; ///< Player animations JJ2Layer* layers[LAYERS]; ///< All layers JJ2Layer* layer; ///< Layer 4 JJ2Modifier** mods; ///< Modifier events for each tile in layer 4 int nAnimSets; ///< Number of animation sets bool TSF; ///< 1.24 level fixed waterLevel; ///< Height of water fixed waterLevelTarget; ///< Future height of water fixed waterLevelSpeed; ///< Rate of water level change void createEvent (int x, int y, const unsigned char* data); int load (char* fileName, bool checkpoint); void loadSprite (unsigned char* parameters, unsigned char* compressedPixels, Sprite* sprite, Sprite* flippedSprite); int loadSprites (); int loadTiles (char* fileName); int step (); void draw (); public: JJ2Level (Game* owner, char* fileName, bool checkpoint, bool multi); ~JJ2Level (); bool checkMaskDown (fixed x, fixed y, bool drop); bool checkMaskUp (fixed x, fixed y); Anim* getAnim (int set, int anim, bool flipped); Anim* getPlayerAnim (int character, int anim, bool flipped); JJ2Modifier* getModifier (int gridX, int gridY); Sprite* getSprite (unsigned char sprite); fixed getWaterLevel (); void setFrame (int gridX, int gridY, unsigned char frame); void setNext (char* fileName); void setWaterLevel (int gridY, bool instant); void warp (JJ2LevelPlayer *player, int id); void receive (unsigned char* buffer); int play (); }; // Variable EXTERN JJ2Level* jj2Level; //< JJ2 level #endif openjazz-20240919/src/jj2/level/jj2levelframe.cpp000066400000000000000000000103731467303304200214530ustar00rootroot00000000000000 /** * * @file jj2levelframe.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Created levelframe.cpp from parts of level.cpp * - 29th June 2010: Created jj2levelframe.cpp from parts of levelframe.cpp * - 30th June 2010: Created jj2layer.cpp from parts of jj2levelframe.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the once-per-frame functions for levels. * */ #include "event/jj2event.h" #include "jj2level.h" #include "jj2levelplayer.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "util.h" /** * JJ2 level iteration. * * @return Error code */ int JJ2Level::step () { int x; int msps; // Milliseconds per step msps = T_STEP; // Determine the players' trajectories for (x = 0; x < nPlayers; x++) players[x].getJJ2LevelPlayer()->control(ticks, msps); // Process events if (events) events = events->step(ticks, msps); // Apply as much of those trajectories as possible, without going into the // scenery for (x = 0; x < nPlayers; x++) players[x].getJJ2LevelPlayer()->move(ticks, msps); // Handle change in water level if (waterLevel < waterLevelTarget) waterLevelSpeed += 100 * msps; else waterLevelSpeed -= 100 * msps; if (waterLevelSpeed > 40000) waterLevelSpeed = 40000; if (waterLevelSpeed < -40000) waterLevelSpeed = -40000; waterLevel += (waterLevelSpeed * msps) >> 10; // Handle player reactions for (x = 0; x < nPlayers; x++) { if (players[x].getJJ2LevelPlayer()->reacted(ticks) == JJ2PR_KILLED) { if (!multiplayer) return LOST; game->resetPlayer(players + x); } } return E_NONE; } /** * Draw the JJ2 level. */ void JJ2Level::draw () { int width, height; int x, y; unsigned int change; width = layer->getWidth(); height = layer->getHeight(); // Calculate change since last step change = getTimeChange(); // Calculate viewport if (game && (stage == LS_END)) game->view(paused? 0: ((ticks - prevTicks) * 160)); else localPlayer->getJJ2LevelPlayer()->view(ticks, paused? 0: (ticks - prevTicks), change); // Ensure the new viewport is within the level if (FTOI(viewX) + canvasW >= TTOI(width)) viewX = ITOF(TTOI(width) - canvasW); if (viewX < 0) viewX = 0; if (FTOI(viewY) + canvasH >= TTOI(height)) viewY = ITOF(TTOI(height) - canvasH); if (viewY < 0) viewY = 0; // Show background layers for (x = 7; x >= 3; x--) layers[x]->draw(tileSet, flippedTileSet); // Show the events if (events) events->draw(ticks, change); // Show the players for (x = 0; x < nPlayers; x++) players[x].getJJ2LevelPlayer()->draw(ticks, change); // Show foreground layers for (x = 2; x >= 0; x--) layers[x]->draw(tileSet, flippedTileSet); // Temporary lines showing the water level drawRect(0, FTOI(waterLevel - viewY), canvasW, 2, 72); drawRect(0, FTOI(waterLevel - viewY) + 3, canvasW, 1, 72); drawRect(0, FTOI(waterLevel - viewY) + 6, canvasW, 1, 72); drawRect(0, FTOI(waterLevel - viewY) + 10, canvasW, 1, 72); // Black-out areas outside the level (for high resolutions) if (TTOI(layers[3]->getWidth()) - FTOI(viewX) < canvasW) drawRect(TTOI(layers[3]->getWidth()) - FTOI(viewX), 0, canvasW, canvasH, JJ2_BLACK); if (TTOI(layers[3]->getHeight()) - FTOI(viewY) < canvasH) drawRect(0, TTOI(layers[3]->getHeight()) - FTOI(viewY), TTOI(layers[3]->getWidth()) - FTOI(viewX), canvasH, JJ2_BLACK); // Show "panel" data // Show score if (multiplayer) game->getMode()->drawScore(font); else panelSmallFont->showNumber(localPlayer->getScore(), 64, 8); // Draw hearts x = localPlayer->getJJ2LevelPlayer()->getEnergy(); for (y = 1; y <= x; y++) { drawRect(canvasW - (y * 12), 4, 8, 8, 48); } // Show lives panelSmallFont->showNumber(localPlayer->getLives(), 16, canvasH - 16); // Show ammo if (localPlayer->getAmmoType() == -1) { panelSmallFont->showString(":", canvasW - 24, canvasH - 16); panelSmallFont->showString(";", canvasW - 16, canvasH - 16); } else panelSmallFont->showNumber(localPlayer->getAmmo(), canvasW - 8, canvasH - 16); } openjazz-20240919/src/jj2/level/jj2levelload.cpp000066400000000000000000000457651467303304200213150ustar00rootroot00000000000000 /** * * @file jj2levelload.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 22nd July 2008: Created levelload.c from parts of level.c * - 3rd February 2009: Renamed levelload.c to levelload.cpp * - 28th June 2010: Created levelloadjj2.cpp from parts of levelload.cpp * - 29th June 2010: Renamed levelloadjj2.cpp to jj2levelload.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the loading of JJ2 level data. * */ #include "event/jj2event.h" #include "jj2level.h" #include "jj2levelplayer.h" #include "game/game.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "loop.h" #include "util.h" #include #define SKEY 254 /* Sprite colour key */ /** * Load a sprite. * * @param parameters Sprite parameters * @param compressedPixels Compressed data from which to obtain the sprite data * @param sprite Sprite that will receive the loaded data * @param flippedSprite Sprite that will receive the flipped loaded data */ void JJ2Level::loadSprite (unsigned char* parameters, unsigned char* compressedPixels, Sprite* sprite, Sprite* flippedSprite) { unsigned char* pixels; int width, height; int srcPos, dstPos, rle; int x, y; // Load dimensions width = createShort(parameters); height = createShort(parameters + 2); if ((width == 0) || (height == 0)) { sprite->clearPixels(); return; } // Decompress pixels pixels = new unsigned char[width * height]; memset(pixels, 0, width * height); srcPos = createInt(parameters + 16); dstPos = 0; while (dstPos < width * height) { rle = compressedPixels[srcPos]; srcPos++; if (rle > 128) { rle &= 127; if (dstPos + rle < width * height) memcpy(pixels + dstPos, compressedPixels + srcPos, rle); srcPos += rle; dstPos += rle; } else if (rle == 128) { rle = dstPos % width; if (rle) dstPos = dstPos + width - rle; } else { dstPos += rle; } } // Set sprite data sprite->setOffset(createShort(parameters + 8), createShort(parameters + 10)); sprite->setPixels(pixels, width, height, 0); // Flip sprite for (y = 0; y < height; y++) { for (x = 0; x < (width >> 1); x++) { rle = pixels[(y * width) + x]; pixels[(y * width) + x] = pixels[(y * width) + (width - 1) - x]; pixels[(y * width) + (width - 1) - x] = rle; } } // Set flipped sprite data flippedSprite->setOffset(-createShort(parameters + 8) - width, createShort(parameters + 10)); flippedSprite->setPixels(pixels, width, height, 0); delete[] pixels; } /** * Load sprites. * * @return Error code */ int JJ2Level::loadSprites () { File* file; int* setOffsets; int set; // Thanks to Neobeo for working out the .j2a format try { file = new File("anims.j2a", PATH_TYPE_GAME); } catch (int e) { return e; } file->seek(24, true); nAnimSets = file->loadInt(); setOffsets = new int[nAnimSets]; for (set = 0; set < nAnimSets; set++) setOffsets[set] = file->loadInt(); // Count number of sprites int nSprites = 0; for (set = 0; set < nAnimSets; set++) { file->seek(setOffsets[set] + 6, true); nSprites += file->loadShort(); } spriteSet = new Sprite[nSprites]; flippedSpriteSet = new Sprite[nSprites]; animSets = new Anim *[nAnimSets]; flippedAnimSets = new Anim *[nAnimSets]; // Load animations and sprites nSprites = 0; for (set = 0; set < nAnimSets; set++) { file->seek(setOffsets[set] + 4, true); int setAnims = file->loadChar(); if (setAnims) { animSets[set] = new Anim[setAnims]; flippedAnimSets[set] = new Anim[setAnims]; } else { animSets[set] = NULL; flippedAnimSets[set] = NULL; } file->seek(7, false); int aCLength = file->loadInt(); int aLength = file->loadInt(); int bCLength = file->loadInt(); int bLength = file->loadInt(); int cCLength = file->loadInt(); int cLength = file->loadInt(); file->loadInt(); // Don't need this compressed block length file->loadInt(); // Don't need this block length unsigned char* aBuffer = file->loadLZ(aCLength, aLength); unsigned char* bBuffer = file->loadLZ(bCLength, bLength); unsigned char* cBuffer = file->loadLZ(cCLength, cLength); int setSprite = 0; for (int anim = 0; anim < setAnims; anim++) { int animSprites = createShort(aBuffer + (anim * 8)); // Fonts are loaded separately if (animSprites == 224) animSprites = 1; animSets[set][anim].setData(animSprites, 0, 0, 0, 0, 0, 0); flippedAnimSets[set][anim].setData(animSprites, 0, 0, 0, 0, 0, 0); for (int sprite = 0; sprite < animSprites; sprite++) { loadSprite(bBuffer + (setSprite * 24), cBuffer, spriteSet + nSprites, flippedSpriteSet + nSprites); animSets[set][anim].setFrame(sprite, false); animSets[set][anim].setFrameData(spriteSet + nSprites, 0, 0); flippedAnimSets[set][anim].setFrame(sprite, false); flippedAnimSets[set][anim].setFrameData(flippedSpriteSet + nSprites, 0, 0); setSprite++; nSprites++; } } delete[] cBuffer; delete[] bBuffer; delete[] aBuffer; } delete[] setOffsets; delete file; return E_NONE; } /** * Load the tileset. * * @param fileName Name of the file containing the tileset * * @return The number of tiles loaded and the maximum possible number of tiles */ int JJ2Level::loadTiles (char* fileName) { File* file; unsigned char* aBuffer; unsigned char* bBuffer; unsigned char* dBuffer; unsigned char* tileBuffer; int aCLength, bCLength, cCLength, dCLength; int aLength, bLength, dLength; int count, x, y; int maxTiles; int tiles; // Thanks to Neobeo for working out the most of the .j2t format try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { return e; } // Skip to version indicator file->seek(220, true); maxTiles = file->loadShort(); if (maxTiles == 0x201) maxTiles = 4096; else maxTiles = 1024; // Skip to compressed block lengths file->seek(8, false); aCLength = file->loadInt(); aLength = file->loadInt(); bCLength = file->loadInt(); bLength = file->loadInt(); cCLength = file->loadInt(); file->loadInt(); // Don't need this block length dCLength = file->loadInt(); dLength = file->loadInt(); aBuffer = file->loadLZ(aCLength, aLength); bBuffer = file->loadLZ(bCLength, bLength); file->seek(cCLength, false); // Don't need this block dBuffer = file->loadLZ(dCLength, dLength); delete file; // Load the palette for (count = 0; count < MAX_PALETTE_COLORS; count++) { palette[count].r = aBuffer[count << 2]; palette[count].g = aBuffer[(count << 2) + 1]; palette[count].b = aBuffer[(count << 2) + 2]; } // Load tiles tiles = createShort(aBuffer + 1024); tileBuffer = new unsigned char[tiles << 10]; for (count = 0; count < tiles; count++) { memcpy(tileBuffer + (count << 10), bBuffer + createInt(aBuffer + 1028 + (maxTiles << 1) + (count << 2)), 1024); } tileSet = createSurface(tileBuffer, TTOI(1), TTOI(tiles)); enableColorKey(tileSet, 0); // Flip tiles for (count = 0; count < TTOI(tiles); count++) { for (x = 0; x < 16; x++) { y = tileBuffer[(count * 32) + x]; tileBuffer[(count * 32) + x] = tileBuffer[(count * 32) + 31 - x]; tileBuffer[(count * 32) + 31 - x] = y; } } flippedTileSet = createSurface(tileBuffer, TTOI(1), TTOI(tiles)); enableColorKey(flippedTileSet, 0); delete[] tileBuffer; // Load mask mask = new char[tiles << 10]; // Unpack bits for (count = 0; count < tiles; count++) { for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) mask[(count << 10) + (y << 5) + x] = (dBuffer[createInt(aBuffer + 1028 + (maxTiles * 18) + (count << 2)) + (y << 2) + (x >> 3)] >> (x & 7)) & 1; } } flippedMask = new char[tiles << 10]; // Unpack bits for (count = 0; count < tiles; count++) { for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) flippedMask[(count << 10) + (y << 5) + x] = (dBuffer[createInt(aBuffer + 1028 + (maxTiles * 22) + (count << 2)) + (y << 2) + (x >> 3)] >> (x & 7)) & 1; } } delete[] dBuffer; delete[] bBuffer; delete[] aBuffer; /* Uncomment the code below if you want to see the mask instead of the tile graphics during gameplay */ /*if (SDL_MUSTLOCK(tileSet)) SDL_LockSurface(tileSet); if (SDL_MUSTLOCK(flippedTileSet)) SDL_LockSurface(flippedTileSet); for (count = 0; count < tiles; count++) { for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) { if (mask[(count << 10) + (y << 5) + x] == 1) ((char *)(tileSet->pixels))[(count << 10) + (y << 5) + x] = 43; if (flippedMask[(count << 10) + (y << 5) + x] == 1) ((char *)(flippedTileSet->pixels))[(count << 10) + (y << 5) + x] = 88; } } } if (SDL_MUSTLOCK(tileSet)) SDL_UnlockSurface(tileSet); if (SDL_MUSTLOCK(flippedTileSet)) SDL_UnlockSurface(flippedTileSet);*/ return tiles | (maxTiles << 16); } /** * Create an event. * * @param x X-coordinate of the new event * @param y Y-coordinate of the new event * @param data Event parameters */ void JJ2Level::createEvent (int x, int y, const unsigned char* data) { unsigned char type; int properties; type = *data; properties = (data[1] >> 4) + (data[2] << 4) + (data[3] << 12); if ((type < 33) || ((type >= 206) && (type <= 208)) || (type == 230) || (type == 240) || (type == 245)) { mods[y][x].type = type; mods[y][x].properties = properties; return; } mods[y][x].type = 0; if (type <= 40) { events = new AmmoJJ2Event(events, x, y, type, TSF); } else if ((type >= 44) && (type <= 45)) { events = new CoinGemJJ2Event(events, x, y, type, TSF); } else if (type == 60) { events = new SpringJJ2Event(events, x, y, type, TSF, properties); } else if (type == 62) { events = new SpringJJ2Event(events, x, y, type, TSF, properties); } else if ((type >= 63) && (type <= 66)) { events = new CoinGemJJ2Event(events, x, y, type, TSF); } else if ((type >= 72) && (type <= 73)) { events = new FoodJJ2Event(events, x, y, type, TSF); } else if (type == 80) { events = new FoodJJ2Event(events, x, y, type, TSF); } else if ((type >= 85) && (type <= 87)) { events = new SpringJJ2Event(events, x, y, type, TSF, properties); } else if ((type >= 141) && (type <= 147)) { events = new FoodJJ2Event(events, x, y, type, TSF); } else if ((type >= 154) && (type <= 182)) { events = new FoodJJ2Event(events, x, y, type, TSF); } else { events = new OtherJJ2Event(events, x, y, type, TSF, properties); } } /** * Load the level. * * @param fileName Name of the file containing the level data * @param checkpoint Whether or not the player(s) will start at a checkpoint * * @return Error code */ int JJ2Level::load (char *fileName, bool checkpoint) { Anim* pAnims[JJ2PANIMS]; Anim* pFlippedAnims[JJ2PANIMS]; File *file; char *string; unsigned char* aBuffer; unsigned char* bBuffer; unsigned char* cBuffer; unsigned char* dBuffer; int aCLength, bCLength, cCLength, dCLength; int aLength, bLength, cLength, dLength; int tiles; int count, x, y, ret; unsigned char tileQuad[8]; short int* quadRefs; int width, height; unsigned char startX, startY; // Thanks to Neobeo for working out the most of the .j2l format try { font = new Font(false); } catch (int e) { throw; } // Open JJ2 level file try { file = new File(fileName, PATH_TYPE_GAME); } catch (int e) { delete font; return e; } // Load level name file->seek(188, true); string = reinterpret_cast(file->loadBlock(32)); // Show loading screen video.setPalette(menuPalette); video.clearScreen(0); x = (canvasW >> 1) - ((strlen(string) + 6) << 2); x = fontmn2->showString("LOADING ", x - 60, (canvasH >> 1) - 16); fontmn2->showString(string, x, (canvasH >> 1) - 16); delete[] string; if (::loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; // Skip to compressed block lengths file->seek(230, true); aCLength = file->loadInt(); aLength = file->loadInt(); bCLength = file->loadInt(); bLength = file->loadInt(); cCLength = file->loadInt(); cLength = file->loadInt(); dCLength = file->loadInt(); dLength = file->loadInt(); aBuffer = file->loadLZ(aCLength, aLength); bBuffer = file->loadLZ(bCLength, bLength); cBuffer = file->loadLZ(cCLength, cLength); dBuffer = file->loadLZ(dCLength, dLength); delete file; // Load tile set from given file ret = loadTiles(reinterpret_cast(aBuffer + 51)); if (ret < 0) { delete[] dBuffer; delete[] cBuffer; delete[] bBuffer; delete[] aBuffer; delete font; return ret; } TSF = ret >> 28; tiles = ret & 0xFFFF; // Next level string = reinterpret_cast(aBuffer + 115); if (fileExists(string, PATH_TYPE_GAME)) nextLevel = createString(string); else nextLevel = createString(string, ".j2l"); // Music file string = reinterpret_cast(aBuffer + 179); if (fileExists(string, PATH_TYPE_GAME)) musicFile = createString(string); else musicFile = createString(string, ".j2b"); // Create layers quadRefs = reinterpret_cast(dBuffer); for (count = 0; count < LAYERS; count++) { int flags = aBuffer[8403 + (count << 2)]; width = createInt(aBuffer + 8403 + 48 + (count << 2)); int pitch = createInt(aBuffer + 8403 + 80 + (count << 2)); height = createInt(aBuffer + 8403 + 112 + (count << 2)); fixed xSpeed = createInt(aBuffer + 8403 + 248 + (count << 2)) >> 6; fixed ySpeed = createInt(aBuffer + 8403 + 280 + (count << 2)) >> 6; pitch = (pitch + 3) >> 2; if (aBuffer[8403 + 40 + count]) { layers[count] = new JJ2Layer(flags, width, height, xSpeed, ySpeed); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if ((x & 3) == 0) memcpy(tileQuad, cBuffer + (quadRefs[x >> 2] << 3), 8); layers[count]->setTile(x, y, createShort(tileQuad + ((x & 3) << 1)), TSF, tiles); } quadRefs += pitch; } } else { // No tile data layers[count] = new JJ2Layer(); } } layer = layers[3]; width = layer->getWidth(); height = layer->getHeight(); // Load events startX = 1; startY = 1; mods = new JJ2Modifier *[height]; *mods = new JJ2Modifier[width * height]; events = NULL; for (y = 0; y < height; y++) { mods[y] = *mods + (y * width); for (x = 0; x < width; x++) { // Create event or assign modifier createEvent(x, y, bBuffer + (((y * width) + x) << 2)); if (mods[y][x].type == 29) { // Jazz start pos startX = x; startY = y; } } } delete[] dBuffer; delete[] cBuffer; delete[] bBuffer; delete[] aBuffer; // Load anims from anims.j2a ret = loadSprites(); if (ret < 0) { if (events) delete events; delete[] *mods; delete[] mods; for (x = 0; x < LAYERS; x++) delete layers[x]; delete[] flippedMask; delete[] mask; delete[] musicFile; delete[] nextLevel; SDL_FreeSurface(flippedTileSet); SDL_FreeSurface(tileSet); delete font; return ret; } // Set initial water level waterLevelTarget = TTOF(layer->getHeight() + 1); waterLevel = waterLevelTarget - F8; waterLevelSpeed = 0; // Generate player's animation set references if (multiplayer) { string = new char[MTL_P_ANIMS + 1]; string[0] = MTL_P_ANIMS + 1; string[1] = MT_P_ANIMS; string[2] = 0; string[3] = 0; game->send(reinterpret_cast(string)); delete[] string; } // Set the players' initial values if (TSF) { for (count = 0; count < JJ2PANIMS; count++) { playerAnims[count] = count; pAnims[count] = animSets[55] + count; pFlippedAnims[count] = flippedAnimSets[55] + count; } } else { playerAnims[JJ2PA_BOARD] = 1; playerAnims[JJ2PA_BOARDSW] = 2; playerAnims[JJ2PA_STOMP] = 3; playerAnims[JJ2PA_DEAD] = 4; playerAnims[JJ2PA_DIE] = 5; playerAnims[JJ2PA_CROUCH1] = 6; playerAnims[JJ2PA_CROUCHED] = 7; playerAnims[JJ2PA_CROUCHSHOOT] = 8; playerAnims[JJ2PA_CROUCH2] = 9; playerAnims[JJ2PA_VINE] = 11; playerAnims[JJ2PA_EXIT1] = 12; playerAnims[JJ2PA_FALL] = 13; playerAnims[JJ2PA_STOMPING] = 14; playerAnims[JJ2PA_LAND] = 15; playerAnims[JJ2PA_STANDSHOOT] = 16; playerAnims[JJ2PA_STANDSHOOTUP] = 17; playerAnims[JJ2PA_WHIP1] = 18; playerAnims[JJ2PA_UNFROG] = 19; playerAnims[JJ2PA_HOOKWHIP] = 21; playerAnims[JJ2PA_HOOKDIAG] = 22; playerAnims[JJ2PA_HOOKSHOOTUP] = 23; playerAnims[JJ2PA_HOOK1] = 24; playerAnims[JJ2PA_HOOK2] = 25; playerAnims[JJ2PA_HOOKWHIPUP] = 26; playerAnims[JJ2PA_HOOKSHOOT] = 27; playerAnims[JJ2PA_HELI] = 28; playerAnims[JJ2PA_HELIWHIP] = 29; playerAnims[JJ2PA_HELISHOOT] = 30; playerAnims[JJ2PA_HPOLE] = 31; playerAnims[JJ2PA_HURT] = 32; playerAnims[JJ2PA_WAIT1] = 33; playerAnims[JJ2PA_WAIT2] = 34; playerAnims[JJ2PA_WAIT3] = 35; playerAnims[JJ2PA_WAIT4] = 36; playerAnims[JJ2PA_WAIT5] = 37; playerAnims[JJ2PA_FALLWHIP] = 38; playerAnims[JJ2PA_FALLSHOOT] = 39; playerAnims[JJ2PA_FLOAT1] = 40; playerAnims[JJ2PA_FLOAT2] = 41; playerAnims[JJ2PA_UP1] = 42; playerAnims[JJ2PA_EDGE] = 43; playerAnims[JJ2PA_CARRY] = 44; playerAnims[JJ2PA_UNLOAD] = 45; playerAnims[JJ2PA_LOAD] = 46; playerAnims[JJ2PA_LOOKUP] = 47; playerAnims[JJ2PA_WOOZYWALK] = 55; playerAnims[JJ2PA_PUSH] = 56; playerAnims[JJ2PA_WHIP2] = 57; playerAnims[JJ2PA_EXIT2] = 58; playerAnims[JJ2PA_SPEED1] = 59; playerAnims[JJ2PA_SPEED2] = 60; playerAnims[JJ2PA_FALLMOVE] = 61; playerAnims[JJ2PA_JUMP] = 63; playerAnims[JJ2PA_BALL] = 67; playerAnims[JJ2PA_WALKSHOOT] = 68; playerAnims[JJ2PA_RUN] = 70; playerAnims[JJ2PA_SPEEDRUN] = 71; playerAnims[JJ2PA_STOP1] = 72; playerAnims[JJ2PA_MYSTERY] = 73; playerAnims[JJ2PA_STOP2] = 74; playerAnims[JJ2PA_UP2] = 75; playerAnims[JJ2PA_STAND] = 76; playerAnims[JJ2PA_POWER] = 77; playerAnims[JJ2PA_POWEREND] = 78; playerAnims[JJ2PA_POWERSTART] = 79; playerAnims[JJ2PA_WOOZYSTAND] = 80; playerAnims[JJ2PA_SWIMDOWN] = 81; playerAnims[JJ2PA_SWIM] = 82; playerAnims[JJ2PA_SWIMDIAGDOWN] = 83; playerAnims[JJ2PA_SWIMDIAGUP] = 84; playerAnims[JJ2PA_SWIMUP] = 85; playerAnims[JJ2PA_VINESDIAG] = 86; playerAnims[JJ2PA_WARPOUT] = 87; playerAnims[JJ2PA_WARPFALLIN] = 88; playerAnims[JJ2PA_WARPFALL] = 89; playerAnims[JJ2PA_WARPFALLOUT] = 90; playerAnims[JJ2PA_WARPIN] = 91; playerAnims[JJ2PA_VPOLE] = 92; for (count = 0; count < JJ2PANIMS; count++) { pAnims[count] = animSets[54] + playerAnims[count]; pFlippedAnims[count] = flippedAnimSets[54] + playerAnims[count]; } } createLevelPlayers(LT_JJ2, pAnims, pFlippedAnims, checkpoint, startX, startY); // And that's us done! // Set the tick at which the level will end, though this is not used endTime = (5 - game->getDifficulty()) * 2 * 60 * 1000; // Adjust panel fonts to use bonus level palette panelBigFont->mapPalette(0, 32, 64, 8); panelSmallFont->mapPalette(75, 5, 64, 8); return E_NONE; } openjazz-20240919/src/jj2/level/jj2levelplayer.cpp000066400000000000000000000305261467303304200216570ustar00rootroot00000000000000 /** * * @file jj2levelplayer.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 24th June 2010: Created levelplayer.cpp from parts of player.cpp * - 29th June 2010: Created jj2levelplayer.cpp from parts of levelplayer.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creation and destruction of players in levels, and their * interactions with other level objects. * */ #include "event/jj2event.h" #include "jj2level.h" #include "jj2levelplayer.h" #include "game/game.h" #include "game/gamemode.h" #include "io/sound.h" #include /** * Create a JJ2 level player. * * @param parent The game player corresponding to this level player. * @param newAnims Animations * @param newFlippedAnims Flipped animations * @param startX Starting position x-coordinate * @param startY Starting position y-coordinate * @param flockSize The number of birds accompanying the player */ JJ2LevelPlayer::JJ2LevelPlayer (Player* parent, Anim** newAnims, Anim** newFlippedAnims, unsigned char startX, unsigned char startY, int flockSize) { const int offsets[14] = {JJ2PCO_GREY, JJ2PCO_SGREEN, JJ2PCO_BLUE, JJ2PCO_RED, JJ2PCO_LGREEN, JJ2PCO_LEVEL1, JJ2PCO_YELLOW, JJ2PCO_LEVEL2, JJ2PCO_ORANGE, JJ2PCO_LEVEL3, JJ2PCO_LEVEL4, JJ2PCO_SANIM, JJ2PCO_LANIM, JJ2PCO_LEVEL5}; const int lengths[14] = {JJ2PCL_GREY, JJ2PCL_SGREEN, JJ2PCL_BLUE, JJ2PCL_RED, JJ2PCL_LGREEN, JJ2PCL_LEVEL1, JJ2PCL_YELLOW, JJ2PCL_LEVEL2, JJ2PCL_ORANGE, JJ2PCL_LEVEL3, JJ2PCL_LEVEL4, JJ2PCL_SANIM, JJ2PCL_LANIM, JJ2PCL_LEVEL5}; int count, start, length; player = parent; memcpy(anims, newAnims, JJ2PANIMS * sizeof(Anim *)); memcpy(flippedAnims, newFlippedAnims, JJ2PANIMS * sizeof(Anim *)); birds = flockSize; reset(startX, startY); // Create the player's palette for (count = 0; count < MAX_PALETTE_COLORS; count++) palette[count].r = palette[count].g = palette[count].b = count; // Fur colours start = offsets[player->cols[0]]; length = lengths[player->cols[0]]; for (count = 0; count < 16; count++) palette[count + 16].r = palette[count + 16].g = palette[count + 16].b = (count * length / 8) + start; // Bandana colours start = offsets[player->cols[1]]; length = lengths[player->cols[1]]; for (count = 0; count < 16; count++) palette[count + 24].r = palette[count + 24].g = palette[count + 24].b = (count * length / 8) + start; // Gun colours start = offsets[player->cols[2]]; length = lengths[player->cols[2]]; for (count = 0; count < 9; count++) palette[count + 32].r = palette[count + 32].g = palette[count + 32].b = (count * length / 8) + start; // Wristband colours start = offsets[player->cols[3]]; length = lengths[player->cols[3]]; for (count = 0; count < 8; count++) palette[count + 40].r = palette[count + 40].g = palette[count + 40].b = (count * length / 8) + start; } /** * Delete the JJ2 level player. */ JJ2LevelPlayer::~JJ2LevelPlayer () { } /** * Reset the player's position, energy etc. * * @param startX New x-coordinate * @param startY New y-coordinate */ void JJ2LevelPlayer::reset (int startX, int startY) { mod = NULL; energy = 5; floating = false; facing = true; animType = JJ2PA_STAND; event = JJ2PE_NONE; reaction = JJ2PR_NONE; reactionTime = 0; jumpHeight = JJ2PYO_JUMP; throwY = TTOF(256); throwX = 0; fastFeetTime = 0; stopTime = 0; lookTime = 0; fireTime = 0; dx = 0; dy = 0; x = TTOF(startX); y = TTOF(startY); gems[0] = gems[1] = gems[2] = gems[3] = 0; coins = 0; shield = JJ2S_NONE; } /** * Add to the player's tally of gems of a certain colour. * * @param colour The colour of the gem */ void JJ2LevelPlayer::addGem (int colour) { gems[colour]++; } /** * Centre the player horizontally on the nearest tile. */ void JJ2LevelPlayer::centreX () { x = ((x + JJ2PXO_MID) & ~32767) + F16 - JJ2PXO_MID; } /** * Centre the player vertically on the nearest tile. */ void JJ2LevelPlayer::centreY () { y = ((y + JJ2PYO_MID) & ~32767) + F16 - JJ2PYO_MID; } /** * Determine the player's current animation. * * @return The current animation */ Anim* JJ2LevelPlayer::getAnim () { return (facing? anims: flippedAnims)[animType]; } /** * Determine the player's current energy level. * * @return Energy level */ int JJ2LevelPlayer::getEnergy () { return energy; } /** * Determine the direction the player is facing. * * @return True if the player is facing right */ bool JJ2LevelPlayer::getFacing () { return facing; } /** * Determine the number of gems, of a certain colour, the player has collected. * * @param colour The colour of the gems * * @return Number of gems collected */ int JJ2LevelPlayer::getGems (int colour) { return gems[colour]; } /** * Determine whether or not the player is being accompanied by a bird. * * @return Whether or not the player is being accompanied by a bird */ int JJ2LevelPlayer::countBirds () { return birds; } /** * Deal with bullet collisions. * * @param source Player that fired the bullet (NULL if an event) * @param ticks Time * * @return Whether or not the hit was successful */ bool JJ2LevelPlayer::hit (Player *source, unsigned int ticks) { // Invulnerable if reacting to e.g. having been hit if (reaction != JJ2PR_NONE) return false; // Hits from the same team have no effect if (source && (source->getTeam() == player->team)) return false; if (player->hit(source)) { energy--; //if (bird) bird->hit(); playSound(SE::OW); } if (energy) { reaction = JJ2PR_HURT; reactionTime = ticks + JJ2PRT_HURT; if (dx < 0) dx = JJ2PXS_RUN; else dx = -JJ2PXS_RUN; dy = JJ2PYS_JUMP; } else { kill(source, ticks); } return true; } /** * Kill the player. * * @param source Player responsible for the kill (NULL if due to an event or time) * @param ticks time */ void JJ2LevelPlayer::kill (Player *source, unsigned int ticks) { if (reaction != JJ2PR_NONE) return; if (player->kill(source)) { energy = 0; player->lives--; reaction = JJ2PR_KILLED; reactionTime = ticks + JJ2PRT_KILLED; } } /** * Determine whether or not the player is overlapping the given area. * * @param left The x-coordinate of the left of the area * @param top The y-coordinate of the top of the area * @param width The width of the area * @param height The height of the area * * @return Whether or not there is an overlap */ bool JJ2LevelPlayer::overlap (fixed left, fixed top, fixed width, fixed height) { return (x + JJ2PXO_R >= left) && (x + JJ2PXO_L < left + width) && (y >= top) && (y + JJ2PYO_TOP < top + height); } /** * Handle the player's reaction. * * @param ticks Time * * @return The reaction the player has just finished */ JJ2PlayerReaction JJ2LevelPlayer::reacted (unsigned int ticks) { if ((reaction != JJ2PR_NONE) && (reactionTime < ticks)) { JJ2PlayerReaction oldReaction = reaction; reaction = JJ2PR_NONE; return oldReaction; } return JJ2PR_NONE; } /** * Set the player's position. * * @param newX New x-coordinate * @param newY New y-coordinate */ void JJ2LevelPlayer::setPosition (fixed newX, fixed newY) { x = newX; y = newY; } /** * Set the player's speed. * * @param newDx New x-speed * @param newDy New y-speed */ void JJ2LevelPlayer::setSpeed (fixed newDx, fixed newDy) { dx = newDx; if (newDy) dy = newDy; } /** * Called when the player has touched an event. * * @param touched The event that was touched * @param ticks Time * @param msps Ticks per step * * @return Whether or not the event should be destroyed. */ bool JJ2LevelPlayer::touchEvent (JJ2Event* touched, unsigned int ticks, int msps) { (void)msps; unsigned char type; bool fullPickup = false; type = touched->getType(); switch (type) { case 34: // Ammo player->addAmmo(2, 3); player->addScore(100); return true; case 36: // Ammo player->addAmmo(1, 3); player->addScore(100); return true; case 37: // Ammo player->addAmmo(0, 3); player->addScore(100); return true; case 38: // TNT player->addAmmo(3, 3); player->addScore(100); return true; case 44: coins++; player->addScore(500); return true; case 45: coins += 5; player->addScore(1000); return true; case 59: // Board floating = true; return true; case 60: // Frozen green spring throwY = y - TTOF(14); dx = 0; event = JJ2PE_SPRING; break; case 61: // Rapid fire player->fireSpeed++; return true; case 62: // Spring crate throwY = y - TTOF(18); dx = 0; event = JJ2PE_SPRING; break; case 63: // Red gem gems[0]++; player->addScore(100); return true; case 64: // Green gem gems[1]++; player->addScore(100); return true; case 65: // Blue gem gems[2]++; player->addScore(100); return true; case 66: // Purple gem gems[3]++; player->addScore(100); return true; case 72: // Carrot if ((energy < 5) || fullPickup) { if (energy < 5) energy++; player->addScore(200); return true; } break; case 73: // Full carrot if ((energy < 5) || fullPickup) { energy = 5; return true; } break; case 80: // 1-up player->addLife(); return true; case 83: // Checkpoint player->setCheckpoint(FTOT(x + JJ2PXO_MID), FTOT(y + JJ2PYO_MID)); break; case 85: // Red spring throwY = y - TTOF(8); dx = 0; event = JJ2PE_SPRING; break; case 86: // Green spring throwY = y - TTOF(14); dx = 0; event = JJ2PE_SPRING; break; case 87: // Blue spring throwY = y - TTOF(18); dx = 0; event = JJ2PE_SPRING; break; case 88: //Invincibility reaction = JJ2PR_INVINCIBLE; reactionTime = ticks + JJ2PRT_INVINCIBLE; return true; case 91: // Horizontal red spring if (true) throwX = x + TTOF(7); else throwX = x - TTOF(7); dy = 0; break; case 92: // Horizontal green spring if (true) throwX = x + TTOF(14); else throwX = x - TTOF(14); dy = 0; break; case 93: // Horizontal blue spring if (true) throwX = x + TTOF(18); else throwX = x - TTOF(18); dy = 0; break; case 96: // Helicarrot floating = true; return true; case 192: // Gem ring gems[0] += 8; return true; default: if (((type >= 141) && (type <= 147)) || ((type >= 154) && (type <= 182))) { // Food player->addScore(50); return true; } break; } return false; } /** * Fill a buffer with player data. * * @param buffer The buffer */ void JJ2LevelPlayer::send (unsigned char *buffer) { buffer[9] = birds; buffer[23] = energy; buffer[25] = shield; buffer[26] = floating; buffer[27] = getFacing(); buffer[29] = jumpHeight >> 24; buffer[30] = (jumpHeight >> 16) & 255; buffer[31] = (jumpHeight >> 8) & 255; buffer[32] = jumpHeight & 255; buffer[33] = throwY >> 24; buffer[34] = (throwY >> 16) & 255; buffer[35] = (throwY >> 8) & 255; buffer[36] = throwY & 255; buffer[37] = x >> 24; buffer[38] = (x >> 16) & 255; buffer[39] = (x >> 8) & 255; buffer[40] = x & 255; buffer[41] = y >> 24; buffer[42] = (y >> 16) & 255; buffer[43] = (y >> 8) & 255; buffer[44] = y & 255; } /** * Adjust player data based on the contents of a given buffer. * * @param buffer The buffer */ void JJ2LevelPlayer::receive (unsigned char *buffer) { int count; switch (buffer[1]) { case MT_P_ANIMS: for (count = 0; count < JJ2PANIMS; count++) { anims[count] = jj2Level->getPlayerAnim(buffer[3], count, false); flippedAnims[count] = jj2Level->getPlayerAnim(buffer[3], count, true); } break; case MT_P_TEMP: birds = buffer[9]; energy = buffer[23]; shield = (JJ2Shield)buffer[25]; floating = buffer[26]; facing = buffer[27]; jumpHeight = (buffer[29] << 24) + (buffer[30] << 16) + (buffer[31] << 8) + buffer[32]; throwY = (buffer[33] << 24) + (buffer[34] << 16) + (buffer[35] << 8) + buffer[36]; x = (buffer[37] << 24) + (buffer[38] << 16) + (buffer[39] << 8) + buffer[40]; y = (buffer[41] << 24) + (buffer[42] << 16) + (buffer[43] << 8) + buffer[44]; break; } } openjazz-20240919/src/jj2/level/jj2levelplayer.h000066400000000000000000000146421467303304200213250ustar00rootroot00000000000000 /** * * @file jj2levelplayer.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created player.h from parts of OpenJazz.h * - 24th June 2010: Created levelplayer.h from parts of player.h * - 29th June 2010: Created jj2levelplayer.h from parts of levelplayer.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ /* "Tile" is a flexible term. Here it is used to refer specifically to the individual elements of the tile set. "Tiles" in the context of level units are referred to as grid elements. */ #ifndef _JJ2LEVELPLAYER_H #define _JJ2LEVELPLAYER_H #include "jj2level.h" #include "level/levelplayer.h" #include "player/player.h" #include "OpenJazz.h" // Constants // Colour offsets #define JJ2PCO_GREY 72 #define JJ2PCO_SGREEN 16 #define JJ2PCO_BLUE 35 #define JJ2PCO_RED 24 #define JJ2PCO_LGREEN 16 #define JJ2PCO_LEVEL1 96 #define JJ2PCO_YELLOW 59 #define JJ2PCO_LEVEL2 112 #define JJ2PCO_ORANGE 43 #define JJ2PCO_LEVEL3 128 #define JJ2PCO_LEVEL4 144 #define JJ2PCO_SANIM 160 #define JJ2PCO_LANIM 176 #define JJ2PCO_LEVEL5 208 // Colour lengths #define JJ2PCL_GREY 8 #define JJ2PCL_SGREEN 8 #define JJ2PCL_BLUE 5 #define JJ2PCL_RED 8 #define JJ2PCL_LGREEN 8 #define JJ2PCL_LEVEL1 16 #define JJ2PCL_YELLOW 5 #define JJ2PCL_LEVEL2 16 #define JJ2PCL_ORANGE 5 #define JJ2PCL_LEVEL3 16 #define JJ2PCL_LEVEL4 16 #define JJ2PCL_SANIM 16 #define JJ2PCL_LANIM 32 #define JJ2PCL_LEVEL5 16 // Player reaction times #define JJ2PRT_HURT 1000 #define JJ2PRT_HURTANIM 200 #define JJ2PRT_KILLED 2000 #define JJ2PRT_INVINCIBLE 10000 // Other time periods #define T_FASTFEET 25000 #define T_WARP 1000 // Player offsets #define JJ2PXO_MID F16 #define JJ2PXO_L (JJ2PXO_MID - F10) #define JJ2PXO_ML (JJ2PXO_MID - F4) #define JJ2PXO_MR (JJ2PXO_MID + F4) #define JJ2PXO_R (JJ2PXO_MID + F10) #define JJ2PYO_TOP (-F20) #define JJ2PYO_MID (-F10) #define JJ2PYO_JUMP ITOF(92) // Player speeds #define JJ2PXS_LIMIT ITOF(500) #define JJ2PYS_LIMIT ITOF(500) #define JJ2PXS_WALK ITOF(300) #define JJ2PXS_RUN ITOF(325) #define JJ2PXS_FFRUN ITOF(500) #define JJ2PYS_FALL ITOF(350) #define JJ2PYS_SINK ITOF(150) #define JJ2PYS_JUMP -ITOF(350) #define JJ2PXS_POLE ITOF(500) #define JJ2PYS_POLE ITOF(500) #define JJ2PYS_SPRING -ITOF(500) // Player accelerations #define JJ2PXA_REVERSE 900 #define JJ2PXA_STOP 1000 #define JJ2PXA_WALK 500 #define JJ2PXA_RUN 200 #define JJ2PXA_FFRUN 200 #define JJ2PYA_GRAVITY 2750 #define JJ2PYA_SINK 1000 // Enum /// JJ2 player event types enum JJ2PlayerEvent { JJ2PE_NONE, ///< No event JJ2PE_SPRING, ///< Spring JJ2PE_FLOAT, ///< Float up JJ2PE_PLATFORM ///< Moving platform }; /// JJ2 player reaction type enum JJ2PlayerReaction { JJ2PR_NONE, ///< Not reacting JJ2PR_HURT, ///< Hurt JJ2PR_KILLED, ///< Killed JJ2PR_INVINCIBLE ///< Invincibility }; /// JJ2 shield type enum JJ2Shield { JJ2S_NONE = 0, ///< No shield JJ2S_FLAME = 1, ///< Flame shield JJ2S_BUBBLE = 2, ///< Bubble shield JJ2S_PLASMA = 3, ///< Plasma shield JJ2S_LASER = 4 ///< Laser shield }; // Classes class Anim; class JJ2Event; /// JJ2 level player class JJ2LevelPlayer final : public LevelPlayer { private: int birds; ///< Placeholder for eventual JJ2Bird objects Anim* anims[JJ2PANIMS]; ///< Animations Anim* flippedAnims[JJ2PANIMS]; ///< Animations (flipped) JJ2Modifier* mod; ///< Modifier currently affecting player int energy; ///< 0 = dead, 3 or 5 = maximum JJ2Shield shield; ///< Current shield int floating; ///< 0 = normal, 1 = helicopter ears, 2 = boarding bool facing; ///< false = left, true = right unsigned char animType; ///< Current animation JJ2PlayerEvent event; ///< Event type int lookTime; ///< Negative if looking up, positive if looking down, 0 if neither JJ2PlayerReaction reaction; ///< Reaction type unsigned int reactionTime; ///< Time reaction will end unsigned int fireTime; ///< The next time the player can fire fixed jumpHeight; ///< The height the player can reach when jumping fixed throwX; ///< Having been thrown, the x-coordinate the player can reach fixed throwY; ///< Having been thrown, the y-coordinate the player can reach unsigned int fastFeetTime; ///< Time fast feet will expire unsigned int stopTime; ///< Time a modifier will cease stopping the player int gems[4]; ///< Gems collected int coins; ///< Value of coins collected bool checkMaskDown (fixed yOffset, bool drop); bool checkMaskUp (fixed yOffset); void centreX (); void centreY (); void ground (); void modify (JJ2Modifier* nextMod, unsigned int ticks); public: JJ2LevelPlayer (Player* parent, Anim** newAnims, Anim** newFlippedAnims, unsigned char startX, unsigned char startY, int flockSize); ~JJ2LevelPlayer (); void reset (int startX, int startY) override; void addGem (int colour); int countBirds () override; Anim* getAnim (); int getEnemies (); int getEnergy (); bool getFacing (); int getGems (int colour); bool hit (Player* source, unsigned int ticks); void kill (Player* source, unsigned int ticks); bool overlap (fixed left, fixed top, fixed width, fixed height); JJ2PlayerReaction reacted (unsigned int ticks); void setPosition (fixed newX, fixed newY); void setSpeed (fixed newDx, fixed newDy); bool touchEvent (JJ2Event* touched, unsigned int ticks, int msps); void send (unsigned char* buffer) override; void receive (unsigned char* buffer) override; void control (unsigned int ticks, int msps); void move (unsigned int ticks, int msps); void view (unsigned int ticks, int mspf, int change); void draw (unsigned int ticks, int change); }; #endif openjazz-20240919/src/jj2/level/jj2levelplayerframe.cpp000066400000000000000000000430041467303304200226650ustar00rootroot00000000000000 /** * * @file jj2levelplayerframe.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 18th July 2009: Created playerframe.cpp from parts of player.cpp * - 24th June 2010: Renamed playerframe.cpp to levelplayerframe.cpp * - 29th June 2010: Created jj2levelplayerframe.cpp from parts of * levelplayerframe.cpp * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides the once-per-frame functions of players in levels. * */ #include "event/jj2event.h" #include "jj2level.h" #include "jj2levelplayer.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "util.h" /** * Determine whether or not the area below the player is solid when travelling * downwards. * * @param yOffset Vertical offset of the mask values to check * * @return Solidity */ bool JJ2LevelPlayer::checkMaskDown (fixed yOffset, bool drop) { return jj2Level->checkMaskDown(x + JJ2PXO_ML, y + yOffset, drop) || jj2Level->checkMaskDown(x + JJ2PXO_MID, y + yOffset, drop) || jj2Level->checkMaskDown(x + JJ2PXO_MR, y + yOffset, drop); } /** * Determine whether or not the area above the player is solid when travelling * upwards. * * @param yOffset Vertical offset of the mask values to check * * @return Solidity */ bool JJ2LevelPlayer::checkMaskUp (fixed yOffset) { return jj2Level->checkMaskUp(x + JJ2PXO_ML, y + yOffset) || jj2Level->checkMaskUp(x + JJ2PXO_MID, y + yOffset) || jj2Level->checkMaskUp(x + JJ2PXO_MR, y + yOffset); } /** * Move the player to the ground's surface. */ void JJ2LevelPlayer::ground () { // If on an uphill slope, push the player upwards if (checkMaskUp(0) && !checkMaskUp(-F1)) y -= F1; // If on a downhill slope, push the player downwards if (!checkMaskUp(F1) && checkMaskUp(F2)) y += F1; } /** * Respond to tile modifier events. * * @param nextMod The modifier event * @param ticks Time */ void JJ2LevelPlayer::modify (JJ2Modifier* nextMod, unsigned int ticks) { switch (nextMod->type) { case 2: // Spikes if (jj2Level->checkMaskDown(x + JJ2PXO_MID, y + F1, false)) hit(NULL, ticks); break; case 4: // Hook dx = 0; break; case 6: // H-pole if (!stopTime) { // Catch player if (nextMod != mod) { centreX(); centreY(); stopTime = ticks + 1000; } } else if (ticks > stopTime) { dx = (dx > 0) ? JJ2PXS_POLE: -JJ2PXS_POLE; stopTime = 0; } break; case 7: // V-pole if (!stopTime) { // Catch player if (nextMod != mod) { centreX(); centreY(); stopTime = ticks + 1000; } } else if (ticks > stopTime) { if (dy < 0) { throwY = y - TTOF(16); dy = -JJ2PYS_POLE; } else { dy = JJ2PYS_POLE; } stopTime = 0; } break; case 8: // Fly off floating = false; break; case 17: // End of level case 18: // End of level if (!energy) return; if (!player->endOfLevel(FTOT(x + JJ2PXO_MID), FTOT(y + JJ2PYO_MID))) return; jj2Level->setStage(LS_END); break; case 206: // Sucker tube dx = (nextMod->properties & 0x40) ? -((nextMod->properties << 15) & 0x1F8000): ((nextMod->properties << 15) & 0x1F8000); dy = (nextMod->properties & 0x2000) ? -((nextMod->properties << 8) & 0x1F8000): ((nextMod->properties << 8) & 0x1F8000); if (dx) centreY(); if (dy) centreX(); break; case 207: // Text break; case 208: // Water level jj2Level->setWaterLevel(nextMod->properties & 0xFF, nextMod->properties & 0x100); break; case 230: // Warp if (!stopTime) { // Catch player if (coins >= ((nextMod->properties >> 8) & 255)) stopTime = ticks + 1000; } else if (ticks > stopTime) { coins -= (nextMod->properties >> 8) & 255; jj2Level->warp(this, nextMod->properties & 255); stopTime = 0; } break; default: stopTime = 0; break; } mod = nextMod; } /** * Respond to controls, unless the player has been killed * * @param ticks Time * @param msps Ticks per step */ void JJ2LevelPlayer::control (unsigned int ticks, int msps) { JJ2Modifier* nextMod; bool drop, platform; // If the player has been killed, do not move if (!energy) { dx = 0; dy = 0; animType = JJ2PA_DIE; return; } // Get overlapping modifier nextMod = jj2Level->getModifier(FTOT(x + JJ2PXO_MID), FTOT(y + JJ2PYO_MID)); if (stopTime) { // Can't control player, so just apply modifier modify(nextMod, ticks); return; } if (player->pcontrols[C_RIGHT]) { // Walk/run right if (dx < 0) dx += JJ2PXA_REVERSE * msps; else if (dx < JJ2PXS_WALK) dx += JJ2PXA_WALK * msps; else if (dx < JJ2PXS_RUN) dx += JJ2PXA_RUN * msps; facing = true; } else if (player->pcontrols[C_LEFT]) { // Walk/run left if (dx > 0) dx -= JJ2PXA_REVERSE * msps; else if (dx > -JJ2PXS_WALK) dx -= JJ2PXA_WALK * msps; else if (dx > -JJ2PXS_RUN) dx -= JJ2PXA_RUN * msps; facing = false; } else if ((nextMod->type >> 1) != 3) { // Slow down if (dx > 0) { if (dx < JJ2PXA_STOP * msps) dx = 0; else dx -= JJ2PXA_STOP * msps; } if (dx < 0) { if (dx > -JJ2PXA_STOP * msps) dx = 0; else dx += JJ2PXA_STOP * msps; } } drop = player->pcontrols[C_DOWN]; // Check for platform event, bridge or level mask below player platform = (event == JJ2PE_PLATFORM) || checkMaskDown(1, drop) || ((dx > 0) && jj2Level->checkMaskDown(x + JJ2PXO_ML, y + F8, drop)) || ((dx < 0) && jj2Level->checkMaskDown(x + JJ2PXO_MR, y + F8, drop)); if (floating) { if (player->pcontrols[C_UP]) { // Fly upwards if (dy > 0) dy -= JJ2PXA_REVERSE * msps; else if (dy > -JJ2PXS_WALK) dy -= JJ2PXA_WALK * msps; else if (dy > -JJ2PXS_RUN) dy -= JJ2PXA_RUN * msps; } else if (player->pcontrols[C_DOWN]) { // Fly downwards if (dy < 0) dy += JJ2PXA_REVERSE * msps; else if (dy < JJ2PXS_WALK) dy += JJ2PXA_WALK * msps; else if (dy < JJ2PXS_RUN) dy += JJ2PXA_RUN * msps; } else if ((nextMod->type >> 1) != 3) { // Slow down if (dy > 0) { if (dy < JJ2PXA_STOP * msps) dy = 0; else dy -= JJ2PXA_STOP * msps; } if (dy < 0) { if (dy > -JJ2PXA_STOP * msps) dy = 0; else dy += JJ2PXA_STOP * msps; } } if (event == JJ2PE_SPRING) dy = JJ2PYS_SPRING; else if (event == JJ2PE_FLOAT) dy = JJ2PYS_JUMP; } else if (y + JJ2PYO_MID > jj2Level->getWaterLevel()) { if (player->pcontrols[C_SWIM]) { // Swim upwards if (dy > 0) dy -= JJ2PXA_REVERSE * msps; else if (dy > -JJ2PXS_WALK) dy -= JJ2PXA_WALK * msps; else if (dy > -JJ2PXS_RUN) dy -= JJ2PXA_RUN * msps; // Prepare to jump upon leaving the water if (!jj2Level->checkMaskUp(x + JJ2PXO_MID, y - F36)) { throwY = y - jumpHeight; if (dx < 0) throwY += dx >> 4; else if (dx > 0) throwY -= dx >> 4; event = JJ2PE_NONE; } } else if (player->pcontrols[C_DOWN]) { // Swim downwards if (dy < 0) dy += JJ2PXA_REVERSE * msps; else if (dy < JJ2PXS_WALK) dy += JJ2PXA_WALK * msps; else if (dy < JJ2PXS_RUN) dy += JJ2PXA_RUN * msps; } else { // Sink dy += JJ2PYA_SINK * msps; if (dy > JJ2PYS_SINK) dy = JJ2PYS_SINK; } } else { if (platform && player->pcontrols[C_JUMP] && !jj2Level->checkMaskUp(x + JJ2PXO_MID, y - F36)) { // Jump throwY = y - jumpHeight; // Increase jump height if walking/running if (dx < 0) throwY += dx >> 3; else if (dx > 0) throwY -= dx >> 3; event = JJ2PE_NONE; playSound(SE::JUMPA); } // Stop jumping if (!player->pcontrols[C_JUMP] && (event != JJ2PE_SPRING) && (event != JJ2PE_FLOAT)) throwY = TTOF(256); if (y >= throwY) { // If jumping, rise dy = (throwY - y - F64) * 4; // Avoid jumping too fast, unless caused by an event if ((event == JJ2PE_NONE) && (dy < JJ2PYS_JUMP)) dy = JJ2PYS_JUMP; } else if (!platform) { // Fall under gravity dy += JJ2PYA_GRAVITY * msps; if (dy > JJ2PYS_FALL) dy = JJ2PYS_FALL; } // Don't descend through platforms if ((dy > 0) && (event == JJ2PE_PLATFORM)) dy = 0; if (platform && !lookTime) { // If requested, look up or down if (player->pcontrols[C_UP]) lookTime = -ticks; else if (player->pcontrols[C_DOWN]) lookTime = ticks; } // Stop looking if there is no platform or the control has been released if (!platform || (!player->pcontrols[C_UP] && (lookTime < 0)) || (!player->pcontrols[C_DOWN] && (lookTime > 0))) lookTime = 0; } // If there is an obstacle above and the player is not floating up, stop // rising if (jj2Level->checkMaskUp(x + JJ2PXO_MID, y + JJ2PYO_TOP - F1) && (throwY < y) && (event != JJ2PE_FLOAT)) { throwY = TTOF(256); if (dy < 0) dy = 0; if (event != JJ2PE_PLATFORM) event = JJ2PE_NONE; } // If jump completed, stop rising if (y <= throwY) { throwY = TTOF(256); if (event != JJ2PE_PLATFORM) event = JJ2PE_NONE; } // Apply modifier modify(nextMod, ticks); // Limit speed if (dx < -JJ2PXS_LIMIT) dx = -JJ2PXS_LIMIT; else if (dx > JJ2PXS_LIMIT) dx = JJ2PXS_LIMIT; if (dy < -JJ2PYS_LIMIT) dy = -JJ2PYS_LIMIT; else if (dy > JJ2PYS_LIMIT) dy = JJ2PYS_LIMIT; // Handle firing if (player->pcontrols[C_FIRE]) { if (ticks > fireTime) { // Make sure bullet position is taken from correct animation if (platform) animType = JJ2PA_STANDSHOOT; /// @todo Create new bullet when firing // Set when the next bullet can be fired if (player->fireSpeed) fireTime = ticks + (1000 / player->fireSpeed); else fireTime = 0x7FFFFFFF; // Remove the bullet from the arsenal if (player->ammoType != -1) player->ammo[player->ammoType]--; /* If the current ammo type has been exhausted, use the previous non-exhausted ammo type */ while ((player->ammoType > -1) && !player->ammo[player->ammoType]) player->ammoType--; } } else fireTime = 0; // Check for a change in ammo if (player->pcontrols[C_CHANGE]) { if (player == localPlayer) controls.release(C_CHANGE); player->ammoType = ((player->ammoType + 2) % 5) - 1; // If there is no ammo of this type, go to the next type that has ammo while ((player->ammoType > -1) && !player->ammo[player->ammoType]) player->ammoType = ((player->ammoType + 2) % 5) - 1; } // Deal with the bird //if (birds) birds = birds->step(ticks, msps); // Choose animation if ((reaction == JJ2PR_HURT) && (reactionTime - ticks > JJ2PRT_HURT - JJ2PRT_HURTANIM)) animType = JJ2PA_HURT; else if (y + JJ2PYO_MID > jj2Level->getWaterLevel()) animType = JJ2PA_SWIM; else if (floating) animType = JJ2PA_BOARD; else if (dy < 0) { if (event == JJ2PE_SPRING) animType = JJ2PA_FLOAT1; else animType = JJ2PA_JUMP; } else if (platform) { if (dx) { if (dx <= -JJ2PXS_RUN) animType = JJ2PA_RUN; else if (dx >= JJ2PXS_RUN) animType = JJ2PA_RUN; else if ((dx < 0) && facing) animType = JJ2PA_STOP1; else if ((dx > 0) && !facing) animType = JJ2PA_STOP1; else animType = JJ2PA_WALKSHOOT; } else if (!jj2Level->checkMaskDown(x + JJ2PXO_ML, y + F12, drop) && !jj2Level->checkMaskDown(x + JJ2PXO_L, y + F2, drop) && (event != JJ2PE_PLATFORM)) animType = JJ2PA_EDGE; else if (!jj2Level->checkMaskDown(x + JJ2PXO_MR, y + F12, drop) && !jj2Level->checkMaskDown(x + JJ2PXO_R, y + F2, drop) && (event != JJ2PE_PLATFORM)) animType = JJ2PA_EDGE; else if ((lookTime < 0) && ((int)ticks > 1000 - lookTime)) animType = JJ2PA_LOOKUP; else if (lookTime > 0) { if ((int)ticks < 1000 + lookTime) animType = JJ2PA_CROUCHED; else animType = JJ2PA_CROUCH1; } else if (player->pcontrols[C_FIRE]) animType = JJ2PA_STANDSHOOT; else animType = JJ2PA_STAND; } else animType = JJ2PA_FALL; } /** * Move the player. * * @param ticks Time * @param msps Ticks per step */ void JJ2LevelPlayer::move (unsigned int ticks, int msps) { fixed pdx, pdy; bool grounded = false; int count; bool drop; if (stopTime) return; // Apply as much of the trajectory as possible, without going into the // scenery if (fastFeetTime > ticks) { pdx = (dx * msps * 3) >> 11; pdy = (dy * msps * 3) >> 11; } else { pdx = (dx * msps) >> 10; pdy = (dy * msps) >> 10; } // First for the vertical component of the trajectory drop = player->pcontrols[C_DOWN]; if (pdy < 0) { // Moving up count = (-pdy) >> 10; while (count > 0) { if (jj2Level->checkMaskUp(x + JJ2PXO_MID, y + JJ2PYO_TOP - F1)) { y &= ~1023; dy = 0; break; } y -= F1; count--; } pdy = (-pdy) & 1023; if (jj2Level->checkMaskUp(x + JJ2PXO_MID, y + JJ2PYO_TOP - pdy)) { y &= ~1023; dy = 0; } else y -= pdy; } else { if (pdy > 0) { // Moving down count = pdy >> 10; while (count > 0) { if (checkMaskDown(F1, drop)) { y |= 1023; dy = 0; break; } y += F1; count--; } pdy &= 1023; if (checkMaskDown(pdy, drop)) y |= 1023; else y += pdy; } if (checkMaskDown(0, drop)) { // In the ground, so move up if (y >= 1024) y = (y - 1024) | 1023; dy = 0; grounded = true; } else if (checkMaskDown(1, drop)) { // On the ground dy = 0; grounded = true; } } // Then for the horizontal component of the trajectory if (pdx < 0) { // Moving left count = (-pdx) >> 10; while (count > 0) { // If there is an obstacle, stop if (jj2Level->checkMaskUp(x + JJ2PXO_L - F1, y + JJ2PYO_MID)) { x &= ~1023; dx = 0; break; } x -= F1; count--; if (grounded) ground(); } pdx = (-pdx) & 1023; if (jj2Level->checkMaskUp(x + JJ2PXO_L - pdx, y + JJ2PYO_MID)) { x &= ~1023; dx = 0; } else x -= pdx; if (grounded) ground(); } else if (pdx > 0) { // Moving right count = pdx >> 10; while (count > 0) { // If there is an obstacle, stop if (jj2Level->checkMaskUp(x + JJ2PXO_R + F1, y + JJ2PYO_MID)) { x |= 1023; dx = 0; break; } x += F1; count--; if (grounded) ground(); } pdx &= 1023; if (jj2Level->checkMaskUp(x + JJ2PXO_R + pdx, y + JJ2PYO_MID)) { x |= 1023; dx = 0; } else x += pdx; if (grounded) ground(); } // If using a float up event and have hit a ceiling, ignore event if ((event == JJ2PE_FLOAT) && jj2Level->checkMaskUp(x + JJ2PXO_MID, y + JJ2PYO_TOP - F1)) { throwY = TTOF(256); event = JJ2PE_NONE; } if (jj2Level->getStage() == LS_END) return; } /** * Calculate viewport. * * @param ticks Time * @param mspf Ticks per frame * @param change Time since last step */ void JJ2LevelPlayer::view (unsigned int ticks, int mspf, int change) { int oldViewX, oldViewY, speed; // Record old viewport position for applying lag oldViewX = viewX; oldViewY = viewY; // Find new position viewX = x + ((dx * change) >> 10) + F8 - (canvasW << 9); viewY = y + ((dy * change) >> 10) - F24 - (canvasH << 9); if ((lookTime > 0) && ((int)ticks > 1000 + lookTime)) { // Look down if ((int)ticks < 2000 + lookTime) viewY += 64 * (ticks - (1000 + lookTime)); else viewY += F64; } else if ((lookTime < 0) && ((int)ticks > 1000 - lookTime)) { // Look up if ((int)ticks < 2000 - lookTime) viewY -= 64 * (ticks - (1000 - lookTime)); else viewY -= F64; } // Apply lag proportional to player "speed" speed = ((dx >= 0? dx: -dx) + (dy >= 0? dy: -dy)) >> 14; if (speed && (mspf < speed)) { viewX = ((oldViewX * (speed - mspf)) + (viewX * mspf)) / speed; viewY = ((oldViewY * (speed - mspf)) + (viewY * mspf)) / speed; } } /** * Draw the player. * * @param ticks Time * @param change Time since last step */ void JJ2LevelPlayer::draw (unsigned int ticks, int change) { Anim *an; int frame; fixed drawX, drawY; // The current frame for animations if (reaction == JJ2PR_KILLED) frame = (ticks + JJ2PRT_KILLED - reactionTime) / 75; else frame = ticks / 75; // Get position drawX = getDrawX(stopTime? 0: change); drawY = getDrawY(stopTime? 0: change); // Choose sprite an = getAnim(); an->setFrame(frame, reaction != JJ2PR_KILLED); // Show the player // Use player colour an->setPalette(palette, 16, 32); // Flash on and off if hurt if ((reaction != JJ2PR_HURT) || ((ticks / 30) & 2)) { // Draw "motion blur" if (fastFeetTime > ticks) an->draw(drawX + F16 - (dx >> 6), drawY + F16); // Draw player an->draw(drawX + F16, drawY + F16); } // Uncomment the following to see the area of the player /*drawRect(FTOI(drawX + JJ2PXO_L - viewX), FTOI(drawY + JJ2PYO_TOP - viewY), FTOI(JJ2PXO_R - JJ2PXO_L), FTOI(-JJ2PYO_TOP), 89); drawRect(FTOI(drawX + JJ2PXO_ML - viewX), FTOI(drawY + JJ2PYO_TOP - viewY), FTOI(JJ2PXO_MR - JJ2PXO_ML), FTOI(-JJ2PYO_TOP), 88);*/ if (reaction == JJ2PR_INVINCIBLE) { /// @todo Show invincibility effect } switch (shield) { case JJ2S_NONE: // Do nothing break; case JJ2S_FLAME: /// @todo Show shield effect break; case JJ2S_BUBBLE: /// @todo Show shield effect break; case JJ2S_PLASMA: /// @todo Show shield effect break; case JJ2S_LASER: /// @todo Show shield effect break; } // Show the bird //if (birds) birds->draw(ticks, change); // Show the player's name if (nPlayers > 1) panelBigFont->showString(player->name, FTOI(drawX + JJ2PXO_MID) - (panelBigFont->getStringWidth(player->name) >> 1), FTOI(drawY - F32 - F16)); //panelBigFont->showNumber(mod->properties, FTOI(drawX) + 24, FTOI(drawY) + 12); } openjazz-20240919/src/level/000077500000000000000000000000001467303304200155265ustar00rootroot00000000000000openjazz-20240919/src/level/level.cpp000066400000000000000000000243211467303304200173430ustar00rootroot00000000000000 /** * * @file level.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 3rd February 2009: Renamed level.c to level.cpp * - 19th July 2009: Created levelframe.cpp from parts of level.cpp * - 19th July 2009: Added parts of levelload.cpp to level.cpp * - 30th March 2010: Created baselevel.cpp from parts of level.cpp and * levelframe.cpp * - 1st August 2012: Renamed baselevel.cpp to level.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with functionality common to ordinary levels and bonus levels. * */ #include "level.h" #include "game/game.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/sprite.h" #include "io/gfx/video.h" #include "io/sound.h" #include "player/player.h" #include "jj1/scene/jj1scene.h" #include "loop.h" #include "setup.h" /** * Create a new base level */ Level::Level (Game* owner) { game = owner; menuOptions[0] = "continue game"; menuOptions[2] = "save game"; menuOptions[3] = "load game"; menuOptions[4] = "setup options"; menuOptions[5] = "quit game"; // Arbitrary initial value smoothfps = 50.0f; paletteEffects = NULL; paused = false; // Set the level stage stage = LS_NORMAL; sprites = tickOffset = steps = prevTicks = ticks = endTime = items = stats = 0; multiplayer = false; } /** * Destroy base level */ Level::~Level () { stopMusic(); if (paletteEffects) delete paletteEffects; } /** * Set the players' initial values. * * @param levelType The type of level for which to create a level player * @param anims New level player animations * @param flippedAnims New level player flipped animations * @param checkpoint Whether or not a checkpoint is in use * @param x The level players' new grid x-coordinate * @param y The level players' new grid y-coordinate */ void Level::createLevelPlayers (LevelType levelType, Anim** anims, Anim** flippedAnims, bool checkpoint, unsigned char x, unsigned char y) { int count; if (!checkpoint) game->setCheckpoint(x, y); for (count = 0; count < nPlayers; count++) { players[count].createLevelPlayer(levelType, anims, flippedAnims, x, y); game->resetPlayer(players + count); } } /** * Play a cutscene. * * @param file File name of the cutscene to be played * * @return Error code */ int Level::playScene (const char* file) { JJ1Scene* scene; int ret; delete paletteEffects; paletteEffects = NULL; try { scene = new JJ1Scene(file); } catch (int e) { return e; } ret = scene->play(); delete scene; return ret; } /** * Perform timing calculations. */ void Level::timeCalcs () { // Calculate smoothed fps smoothfps = smoothfps + 1.0f - (smoothfps * ((float)(ticks - prevTicks)) / 1000.0f); /* This equation is a simplified version of (fps * c) + (smoothfps * (1 - c)) where c = (1 / fps) and fps = 1000 / (ticks - prevTicks) In other words, the response of smoothFPS to changes in FPS decreases as the framerate increases The following version is for c = (1 / smoothfps) */ // smoothfps = (fps / smoothfps) + smoothfps - 1; // Ignore outlandish values if (smoothfps > 9999.0f) smoothfps = 9999.0f; if (smoothfps < 1.0f) smoothfps = 1.0f; // Track number of ticks of gameplay since the level started if (paused) { tickOffset = globalTicks - ticks; } else if (globalTicks - tickOffset > ticks + 100) { prevTicks = ticks; ticks += 100; tickOffset = globalTicks - ticks; } else { prevTicks = ticks; ticks = globalTicks - tickOffset; } } /** * Calculate the amount of time since the last completed step. * * @return Time since last step */ int Level::getTimeChange () { return paused? 0: ticks - ((steps * (setup.slowMotion? 100: 50)) / 3); } /** * Display menu (if visible) and statistics. * * @param bg Palette index of the box(es) * @param menu Whether or not the level menu should be displayed * @param option Selected menu uption * @param textPalIndex The first palette index for unseleceted text * @param selectedTextPalIndex The first palette index for selected text * @param textPalSpan The number of palette indices for text */ void Level::drawOverlay (unsigned char bg, bool menu, int option, unsigned char textPalIndex, unsigned char selectedTextPalIndex, int textPalSpan) { const char* difficultyOptions[4] = {"easy", "medium", "hard", "turbo"}; int count, width; // Draw graphics statistics if (stats & S_SCREEN) { #ifdef SCALE if (video.getScaleFactor() > 1) drawRect(canvasW - 84, 11, 80, 37, bg); else #endif drawRect(canvasW - 84, 11, 80, 25, bg); panelBigFont->showNumber(video.getWidth(), canvasW - 52, 14); panelBigFont->showString("x", canvasW - 48, 14); panelBigFont->showNumber(video.getHeight(), canvasW - 12, 14); panelBigFont->showString("fps", canvasW - 76, 26); panelBigFont->showNumber((int)smoothfps, canvasW - 12, 26); #ifdef SCALE if (video.getScaleFactor() > 1) { panelBigFont->showNumber(canvasW, canvasW - 52, 38); panelBigFont->showString("x", canvasW - 48, 39); panelBigFont->showNumber(canvasH, canvasW - 12, 38); } #endif } // Draw player list if (stats & S_PLAYERS) { width = 39; for (count = 0; count < nPlayers; count++) if (panelBigFont->getStringWidth(players[count].getName()) > width) width = panelBigFont->getStringWidth(players[count].getName()); drawRect((canvasW >> 1) - 48, 11, width + 57, (nPlayers * 12) + 1, bg); for (count = 0; count < nPlayers; count++) { panelBigFont->showNumber(count + 1, (canvasW >> 1) - 24, 14 + (count * 12)); panelBigFont->showString(players[count].getName(), (canvasW >> 1) - 16, 14 + (count * 12)); panelBigFont->showNumber(players[count].teamScore, (canvasW >> 1) + width + 1, 14 + (count * 12)); } } if (menu) { // Draw the menu drawRect((canvasW >> 2) - 8, (canvasH >> 1) - 54, 144, 108, bg); menuOptions[1] = difficultyOptions[game->getDifficulty()]; for (count = 0; count < 6; count++) { // Gray out options in multiplayer and Save (unimplemented) if ((multiplayer && count >= 2 && count <= 4) || count == 2) { drawRect((canvasW >> 2) - 4, (canvasH >> 1) + (count << 4) - 48, 136, 15, textPalIndex); } if (count == option) fontmn2->mapPalette(240, 8, selectedTextPalIndex, textPalSpan); else fontmn2->mapPalette(240, 8, textPalIndex, textPalSpan); fontmn2->showString(menuOptions[count], canvasW >> 2, (canvasH >> 1) + (count << 4) - 46); } fontmn2->restorePalette(); } } /** * Process in-game menu selection. * * @param menu Whether or not the level menu should be displayed * @param option Chosen menu option * * @return Error code */ int Level::select (bool& menu, int option) { switch (option) { case 0: // Continue menu = false; break; case 1: // Change difficulty if (!multiplayer) game->setDifficulty((game->getDifficulty() + 1) & 3); break; case 2: // Save if (!multiplayer) { int ret = fileMenu.main(true, false); if (ret == E_QUIT) return E_QUIT; if (ret >= 0) { // TODO } // Restore level palette video.setPalette(palette); } break; case 3: // Load if (!multiplayer) { int ret = fileMenu.main(false, false); if (ret == E_QUIT) return E_QUIT; if (ret >= 0) { return E_LOAD0 + ret; } // Restore level palette video.setPalette(palette); } break; case 4: // Setup if (!multiplayer) { bool wasSlow = setup.slowMotion; if (setupMenu.setupMain() == E_QUIT) return E_QUIT; if (wasSlow && !setup.slowMotion) steps <<= 1; else if (!wasSlow && setup.slowMotion) steps >>= 1; // Restore level palette video.setPalette(palette); } break; case 5: // Quit game return E_RETURN; } return E_NONE; } /** * Process iteration. * * @param menu Whether or not the level menu should be displayed * @param option Selected menu uption * @param message Whether or not the "paused" message is being displayed * * @return Error code */ int Level::loop (bool& menu, int& option, bool& message) { int ret, x, y; // Networking if (multiplayer) { ret = game->step(ticks); if (ret < 0) return ret; } // Main loop if (::loop(NORMAL_LOOP, paletteEffects, paused) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) { menu = !menu; option = 0; } if (controls.release(C_PAUSE)) message = !message; if (controls.release(C_STATS)) { if (!multiplayer) stats ^= S_SCREEN; else stats = (stats + 1) & 3; } if (menu) { // Deal with menu controls if (controls.release(C_UP)) option = (option + 5) % 6; if (controls.release(C_DOWN)) option = (option + 1) % 6; if (controls.release(C_ENTER)) { ret = select(menu, option); if (ret < 0) return ret; } if (controls.getCursor(x, y)) { x -= canvasW >> 2; y -= (canvasH >> 1) - 46; if ((x >= 0) && (x < 128) && (y >= 0) && (y < 96)) { option = y >> 4; if (controls.wasCursorReleased()) { ret = select(menu, option); if (ret < 0) return ret; } } else if (controls.wasCursorReleased()) menu = false; } } #if !defined(ANDROID) else { if (controls.wasCursorReleased()) menu = true; } #endif if (!multiplayer) paused = message || menu; timeCalcs(); return E_NONE; } /** * Add extra time. * * @param seconds Number of seconds to add */ void Level::addTimer (int seconds) { if (stage != LS_NORMAL) return; endTime += seconds * 1000; if (endTime >= ticks + (10 * 60 * 1000)) endTime = ticks + (10 * 60 * 1000) - 1; if (multiplayer) { unsigned char buffer[MTL_L_PROP]; buffer[0] = MTL_L_PROP; buffer[1] = MT_L_PROP; buffer[2] = 2; // add timer buffer[3] = seconds; buffer[4] = 0; // not used game->send(buffer); } } /** * Set the level stage. * * @param newStage New level stage */ void Level::setStage (LevelStage newStage) { if (stage == newStage) return; stage = newStage; if (multiplayer) { unsigned char buffer[MTL_L_STAGE]; buffer[0] = MTL_L_STAGE; buffer[1] = MT_L_STAGE; buffer[2] = stage; game->send(buffer); } } /** * Determine the current level stage. * * @return The current level stage. */ LevelStage Level::getStage () { return stage; } openjazz-20240919/src/level/level.h000066400000000000000000000065741467303304200170220ustar00rootroot00000000000000 /** * * @file level.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created level.h from parts of OpenJazz.h * - 30th March 2010: Created baselevel.h from parts of level.h * - 1st August 2012: Renamed baselevel.h to level.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _BASELEVEL_H #define _BASELEVEL_H #include "menu/menu.h" // Macros // For converting between tile positions and int/fixed values #define FTOT(x) ((x) >> 15) ///< Fixed to Tile #define TTOF(x) ((x) << 15) ///< Tile to Fixed #define ITOT(x) ((x) >> 5) ///< Int to Tile #define TTOI(x) ((x) << 5) ///< Tile to Int // Constants // Return values #define WON 1 #define LOST 2 // Time interval #define T_STEP 16 // Enums /// Level type enum LevelType { LT_JJ1 = 0, ///< JJ1 level LT_JJ1BONUS = 1, ///< JJ1 bonus level #ifdef ENABLE_JJ2 LT_JJ2 = 2 ///< JJ2 level #endif }; /// Which stats to display on-screen enum LevelStats { S_PLAYERS = 1, ///< Display player list S_SCREEN = 2 ///< Display video statisitics }; /// Level stage enum LevelStage { LS_NORMAL = 0, ///< Normal gameplay LS_SUDDENDEATH = 1, ///< Sudden death LS_END = 2 ///< Ending sequence }; // Classes class Anim; class File; class Game; class PaletteEffect; class Sprite; /// Base class for all level classes class Level { private: const char* menuOptions[6]; SetupMenu setupMenu; ///< Setup menu to run on the player's command FileMenu fileMenu; ///< Save and Load menus to run on the player's command int select (bool& menu, int option); protected: Game* game; PaletteEffect* paletteEffects; ///< Palette effects in use while playing the level SDL_Color palette[MAX_PALETTE_COLORS]; ///< Palette in use while playing the level int sprites; ///< The number of sprite that have been loaded unsigned int tickOffset; ///< Level time offset from system time unsigned int steps; ///< Number of steps taken unsigned int prevTicks; ///< Time the last visual update started unsigned int ticks; ///< Current time unsigned int endTime; ///< Tick at which the level will end float smoothfps; ///< Smoothed FPS counter int items; ///< Number of items to be collected bool multiplayer; ///< Whether or not this is a multiplayer game bool paused; ///< Whether or not the level is paused LevelStage stage; ///< Level stage int stats; ///< Which statistics to display on-screen, see #LevelStats void createLevelPlayers (LevelType levelType, Anim** anims, Anim** flippedAnims, bool checkpoint, unsigned char x, unsigned char y); int playScene (const char* file); void timeCalcs (); int getTimeChange (); void drawOverlay (unsigned char bg, bool menu, int option, unsigned char textPalIndex, unsigned char selectedTextPalIndex, int textPalSpan); int loop (bool& menu, int& option, bool& message); public: explicit Level(Game* owner); virtual ~Level(); void addTimer (int seconds); LevelStage getStage (); void setStage (LevelStage stage); virtual void receive (unsigned char* buffer) = 0; }; // Variables EXTERN fixed viewX, viewY; ///< Level viewing co-ordinates #endif openjazz-20240919/src/level/levelplayer.h000066400000000000000000000026511467303304200202270ustar00rootroot00000000000000 /** * * @file levelplayer.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created player.h from parts of OpenJazz.h * - 24th June 2010: Created bonusplayer.h and levelplayer.h from parts of * player.h * - 29th June 2010: Created jj2levelplayer.h from parts of levelplayer.h * - 1st August 2012: Renamed levelplayer.h to jj1levelplayer.h * - 5th August 2012: Created levelplayer.h from parts of jj1levelplayer.h, * jj1bonuslevelplayer.h and jj2levelplayer.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creation and destruction of players in levels, and their * interactions with other level objects. */ #ifndef _LEVELPLAYER_H #define _LEVELPLAYER_H #include "io/gfx/video.h" #include "level/movable.h" // Classes class Player; /// Level player class LevelPlayer : public Movable { protected: SDL_Color palette[MAX_PALETTE_COLORS]; ///< Palette (for custom colours) public: Player* player; ///< Corresponding game player virtual ~LevelPlayer () {}; virtual void reset (int startX, int startY) = 0; virtual int countBirds () = 0; virtual void send (unsigned char* buffer) = 0; virtual void receive (unsigned char* buffer) = 0; }; #endif openjazz-20240919/src/level/movable.cpp000066400000000000000000000022641467303304200176630ustar00rootroot00000000000000 /** * * @file movable.cpp * * Part of the OpenJazz project * * @par History: * - 15th January 2010: Created movable.cpp * * @par Licence: * Copyright (c) 2010-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Contains the base class for all movable objects. * */ #include "level.h" #include "movable.h" /** * Derive the x-coordinate of the Movable relative to the view coordinates for * the current time. * * @param change Time since last step * * @return The x-coordinate */ fixed Movable::getDrawX (int change) { return x + ((dx * change) >> 10) - viewX; } /** * Derive the y-coordinate of the Movable relative to the view coordinates for * the current time. * * @param change Time since last step * * @return The y-coordinate */ fixed Movable::getDrawY (int change) { return y + ((dy * change) >> 10) - viewY; } /** * Get the basic x-coordinate of the Movable. * * @return The x-coordinate */ fixed Movable::getX () { return x; } /** * Get the basic y-coordinate of the Movable. * * @return The y-coordinate */ fixed Movable::getY () { return y; } openjazz-20240919/src/level/movable.h000066400000000000000000000011431467303304200173230ustar00rootroot00000000000000 /** * * @file movable.h * * Part of the OpenJazz project * * @par History: * - 15th January 2010: Created movable.h * * @par Licence: * Copyright (c) 2005-2010 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _MOVABLE_H #define _MOVABLE_H #include "OpenJazz.h" // Class /// Base class for all movable objects (players, events, bullets, birds) class Movable { protected: fixed x, y, dx, dy; fixed getDrawX (int change); fixed getDrawY (int change); public: fixed getX (); fixed getY (); }; #endif openjazz-20240919/src/logo.h000066400000000000000000000065241467303304200155370ustar00rootroot00000000000000 /** * * @file logo.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _LOGO_H #define _LOGO_H /* This struct contains an image as zlib compressed deflate block with additional information about the image */ struct compressed_image { unsigned int width; unsigned int height; unsigned int size; unsigned int compressed_size; unsigned char *data; }; // Former openjazz.000, without RLE compression - raw image (without palette) unsigned char oj_logo_data[] = { 0x78, 0xda, 0xb5, 0x96, 0xb1, 0x92, 0x83, 0x20, 0x10, 0x86, 0x2b, 0x95, 0x9e, 0x36, 0xc3, 0x2b, 0xf0, 0x00, 0x57, 0xd3, 0xd3, 0x61, 0x61, 0x4c, 0x73, 0xcf, 0x90, 0x67, 0xd0, 0x22, 0xd1, 0x67, 0xf3, 0x66, 0x8c, 0x0f, 0x23, 0xcc, 0x45, 0x0d, 0x61, 0xc1, 0x45, 0x32, 0x37, 0x73, 0x7f, 0xc9, 0xf0, 0xfd, 0x2e, 0xbb, 0xcb, 0xca, 0xe9, 0x04, 0x95, 0x13, 0x2a, 0x67, 0xbd, 0x48, 0x48, 0x5e, 0x9c, 0x62, 0x1a, 0xc6, 0xb7, 0xe0, 0x72, 0x41, 0xb5, 0x51, 0x4e, 0x46, 0xf0, 0x2c, 0xc2, 0x4f, 0x56, 0x80, 0x2f, 0x24, 0x84, 0x37, 0x69, 0xfa, 0x29, 0x5f, 0xd0, 0x3d, 0xbd, 0x3a, 0xf0, 0x8f, 0x78, 0xa2, 0x55, 0x44, 0x86, 0x66, 0x69, 0x9e, 0x1b, 0x15, 0x97, 0xc8, 0x53, 0x3c, 0x39, 0xc2, 0x11, 0x83, 0x80, 0xe7, 0xc7, 0xb8, 0x52, 0xf3, 0x21, 0x5f, 0xa4, 0x70, 0xa5, 0xe8, 0x11, 0x2f, 0x93, 0xb8, 0x32, 0x24, 0xce, 0xfb, 0xd1, 0x9b, 0x73, 0xd3, 0x2e, 0xaa, 0x4b, 0xbf, 0x8c, 0x71, 0xde, 0xab, 0xdc, 0x77, 0xdb, 0xf5, 0x9b, 0xee, 0x95, 0xe7, 0xcb, 0x63, 0x3c, 0x87, 0xbb, 0x2a, 0x4b, 0x2f, 0x6a, 0x61, 0x08, 0x3a, 0xc7, 0xf9, 0x4c, 0x40, 0xbc, 0xf7, 0x04, 0x0d, 0x0c, 0xc7, 0x79, 0x58, 0x7a, 0xef, 0xeb, 0xab, 0x01, 0xf0, 0x96, 0x38, 0x4f, 0xdd, 0x8e, 0x32, 0xc4, 0xfb, 0xbe, 0xc1, 0x33, 0x08, 0x78, 0x50, 0xbc, 0x66, 0x87, 0xf7, 0x37, 0x77, 0x02, 0x93, 0xa3, 0xfc, 0xec, 0x36, 0xb4, 0x7b, 0xbe, 0xaf, 0x9d, 0x3d, 0x41, 0x79, 0x57, 0xbd, 0xf2, 0x86, 0xf0, 0x0d, 0x5a, 0x41, 0x9c, 0xbf, 0x23, 0x7c, 0x8b, 0xf6, 0xf0, 0xff, 0xf0, 0x7f, 0x8a, 0xdf, 0xe5, 0x4f, 0x21, 0xf9, 0xeb, 0x2e, 0xa9, 0xfc, 0xc9, 0x68, 0xf7, 0x85, 0xf5, 0xcb, 0x52, 0xfd, 0x63, 0xf6, 0x09, 0xa8, 0x92, 0xfd, 0x03, 0x87, 0xc7, 0xe5, 0x7e, 0xd0, 0xbf, 0x5f, 0x23, 0x43, 0xef, 0x1f, 0xbc, 0x3f, 0x75, 0x17, 0xbf, 0x3f, 0x8f, 0xc9, 0xf1, 0x23, 0xe0, 0x09, 0xbc, 0xbf, 0x35, 0xac, 0x41, 0x0b, 0x07, 0xc0, 0x75, 0x9a, 0x06, 0x8b, 0xb3, 0x37, 0xbe, 0xac, 0xcd, 0xd0, 0xa0, 0x6c, 0x5f, 0x67, 0xe8, 0x6e, 0xb5, 0x37, 0x80, 0x1e, 0xd3, 0xdb, 0x80, 0x8d, 0x1e, 0x1f, 0x4c, 0xdf, 0xb2, 0x5e, 0x06, 0x58, 0x75, 0xf6, 0x57, 0xaf, 0xdb, 0x6e, 0x16, 0xe0, 0x6b, 0x4e, 0xe8, 0x07, 0xf3, 0xf3, 0x61, 0x01, 0x00, 0xdb, 0x88, 0x72, 0x9d, 0xe4, 0x7f, 0x26, 0x5c, 0x2c, 0xf1, 0xf3, 0xb3, 0xb5, 0x8b, 0xe0, 0x36, 0xa3, 0x89, 0xff, 0x57, 0x0c, 0x77, 0x1d, 0x71, 0x18, 0x81, 0x18, 0x53, 0xf8, 0x33, 0x07, 0x22, 0x9a, 0x3a, 0xee, 0x65, 0x1c, 0xc7, 0x9f, 0x06, 0x14, 0x0d, 0xc1, 0x08, 0x12, 0x94, 0x0c, 0xc7, 0x71, 0x87, 0x8d, 0x5e, 0x0c, 0x86, 0xc0, 0x61, 0x1c, 0xd8, 0xfe, 0x59, 0x92, 0x73, 0x01, 0x1e, 0x50, 0x46, 0x53, 0x70, 0xe5, 0x3d, 0x07, 0x94, 0x7e, 0x59, 0x50, 0x29, 0x9e, 0x92, 0x94, 0x93, 0xe0, 0xd5, 0xc0, 0xd8, 0xb0, 0xbe, 0xdb, 0x06, 0x0f, 0xfe, 0x05, 0xcd, 0x6d, 0x45, 0x28 }; struct compressed_image oj_logo = { 64, 40, 2560, 436, oj_logo_data }; #endif openjazz-20240919/src/loop.h000066400000000000000000000021531467303304200155420ustar00rootroot00000000000000 /** * * @file loop.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 30th April 2010: Created loop.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _LOOP_H #define _LOOP_H #include "io/gfx/paletteeffects.h" // Constants // Return values #define JOYSTICKB 0x100 #define JOYSTICKANEG 0x200 #define JOYSTICKAPOS 0x300 #define JOYSTICKHUP 0x400 #define JOYSTICKHLFT 0x500 #define JOYSTICKHRHT 0x600 #define JOYSTICKHDWN 0x700 // Variable EXTERN unsigned int globalTicks; // Enum /// Ways the loop function should handle input. enum LoopType { NORMAL_LOOP, ///< Normal behaviour TYPING_LOOP, ///< Return key presses SET_KEY_LOOP, ///< Return key presses without modifying control state SET_JOYSTICK_LOOP ///< Return joystick actions without modifying control state }; // Function in main.cpp EXTERN int loop (LoopType type, PaletteEffect* paletteEffects = NULL, bool effectsStopped = false); #endif openjazz-20240919/src/main.cpp000066400000000000000000000273111467303304200160530ustar00rootroot00000000000000 /** * * @file main.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created main.c * - 22nd July 2008: Created util.c from parts of main.c * - 3rd February 2009: Renamed main.c to main.cpp * - 4th February 2009: Created palette.cpp from parts of main.cpp and util.cpp * - 13th July 2009: Created controls.cpp from parts of main.cpp * - 21st July 2013: Created setup.cpp from parts of main.cpp and setupmenu.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Contains the main function. * */ // consume all external variables #define EXTERN #include "game/game.h" #include "io/controls.h" #include "io/file.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/network.h" #include "io/sound.h" #ifdef ENABLE_JJ2 #include "jj2/level/jj2level.h" #endif #include "jj1/level/jj1level.h" #include "menu/menu.h" #include "player/player.h" #include "jj1/scene/jj1scene.h" #include "loop.h" #include "setup.h" #include "util.h" #include "io/log.h" #include "platforms/platforms.h" #include "version.h" #include #include #ifndef __SYMBIAN32__ #include #endif #define PI 3.141592f // Fallback links, should be provided by the build system #ifndef OJ_BUGREPORT #define OJ_BUGREPORT "https://github.com/AlisterT/openjazz/issues" #endif #ifndef OJ_URL #define OJ_URL "http://alister.eu/jazz/oj/" #endif static struct CliOptions { bool muteAudio; int fullScreen; int scaleFactor; int level; int world; char *verboseLevel; int quiet; } cli = { false, -1, -1, -1, -1, NULL, 0 }; #ifndef FULLSCREEN_ONLY int display_mode_cb(struct argparse *, const struct argparse_option *option) { cli.fullScreen = (option->short_name == 'f') ? 1 : 0; return 0; } #endif int version_cb(struct argparse *, const struct argparse_option */*option*/) { printf("OpenJazz %s, built on %s.\n", oj_version, oj_date); exit(EXIT_SUCCESS); } int checkOptions (int argc, char *argv[]) { struct argparse argparse; // generic usage message const char *usage[] = { "OpenJazz [options] [[--] ]", NULL, }; // command line options struct argparse_option opt[] = { OPT_HELP(), OPT_GROUP("Engine options"), OPT_BOOLEAN('m', "mute", &cli.muteAudio, "Mute audio output", NULL, 0, 0), #ifndef FULLSCREEN_ONLY OPT_BOOLEAN('f', "fullscreen", NULL, "Display in Fullscreen mode", display_mode_cb, 0, OPT_NONEG), OPT_BOOLEAN('\0', "window", NULL, "Display in Window mode", display_mode_cb, 0, OPT_NONEG), #endif OPT_INTEGER('s', "scale", &cli.scaleFactor, "Scale graphics times", NULL, 0, 0), OPT_GROUP("Developer options"), OPT_INTEGER('w', "world", &cli.world, "Load specific World", NULL, 0, 0), OPT_INTEGER('l', "level", &cli.level, "Load specific Level", NULL, 0, 0), OPT_BOOLEAN('q', "quiet", &cli.quiet, "Disable console logging (Enable with --no-quiet)", NULL, 0, 0), OPT_STRING('\0', "verbose", &cli.verboseLevel, "Verbosity level: max, trace, debug, info, warn, error, fatal", NULL, 0, 0), OPT_BOOLEAN('v', "version", NULL, "Show version information", version_cb, 0, OPT_NONEG), OPT_END(), }; argparse_init(&argparse, opt, usage, 0); argparse_describe(&argparse, "\nOpenJazz - Jack Jazzrabbit 1 game engine reimplementation", "\nBug reports: " OJ_BUGREPORT " - Homepage: " OJ_URL); argc = argparse_parse(&argparse, argc, argv); // apply logger options if (cli.quiet) logger.setQuiet(cli.quiet); int verbosity = logger.getLevel(); if (cli.verboseLevel) { if (!strcmp(cli.verboseLevel, "max")) verbosity = LL_MAX; else if (!strcmp(cli.verboseLevel, "trace")) verbosity = LL_TRACE; else if (!strcmp(cli.verboseLevel, "debug")) verbosity = LL_DEBUG; else if (!strcmp(cli.verboseLevel, "info")) verbosity = LL_INFO; else if (!strcmp(cli.verboseLevel, "warn")) verbosity = LL_WARN; else if (!strcmp(cli.verboseLevel, "error")) verbosity = LL_ERROR; else if (!strcmp(cli.verboseLevel, "fatal")) verbosity = LL_FATAL; else { fprintf(stderr, "error: option `--verbose` has invalid level\n"); exit(EXIT_FAILURE); } } logger.setLevel(verbosity); return argc; } /** * Initialises OpenJazz. * * Establishes the paths from which to read files, loads configuration, sets up * the game window and loads required data. * * @param argv0 program path * @param pathCount Number of path arguments * @param paths Array of path argument strings */ void startUp (const char *argv0, int pathCount, char *paths[]) { File* file; unsigned char* pixels = NULL; SetupOptions config; // Determine paths PLATFORM_AddGamePaths(); // Use any provided paths for (int i = 0; i < pathCount; i++) gamePaths.add(createString(paths[i]), PATH_TYPE_GAME); // Use the path of the program, but check before, since it is not always available // At least crashes in Dolphin emulator (Wii) and 3DS (.cia build) if (argv0) { int i = strlen(argv0) - 1; // Search for directory separator while ((argv0[i] != OJ_DIR_SEP) && (i > 0)) i--; // If a directory was found, copy it to the path if (i > 0) { char *dir = createString(argv0); dir[i+1] = '\0'; gamePaths.add(dir, PATH_TYPE_SYSTEM|PATH_TYPE_GAME); } } // Use the current working directory gamePaths.add(createString(""), PATH_TYPE_GAME|PATH_TYPE_CONFIG|PATH_TYPE_TEMP); // Use hard-coded data paths, if available #ifdef DATAPATH gamePaths.add(createString(DATAPATH), PATH_TYPE_SYSTEM|PATH_TYPE_GAME); #endif // Default settings // Sound settings #if defined(WIZ) || defined(GP2X) volume = 40; #endif // Create the network address netAddress = createString(NET_ADDRESS); // Load settings from config file config = setup.load(); if (!config.valid) { // Invalid config - apply defaults config.videoWidth = DEFAULT_SCREEN_WIDTH; config.videoHeight = DEFAULT_SCREEN_HEIGHT; config.videoScale = 1; } // Apply command-line override if (cli.fullScreen > -1) config.fullScreen = cli.fullScreen; if (cli.scaleFactor > 0) config.videoScale = cli.scaleFactor; if (cli.muteAudio) { setMusicVolume(0); setSoundVolume(0); } // Create the game's window canvas = NULL; if (!video.init(config)) { throw E_VIDEO; } if (SDL_NumJoysticks() > 0) SDL_JoystickOpen(0); // Set up audio openAudio(); // Load fonts // Open the panel, which contains two fonts try { file = new File("PANEL.000", PATH_TYPE_GAME); } catch (int e) { closeAudio(); LOG_FATAL("Unable to find game data files. When launching OpenJazz, \n" " pass the location of the original game data, eg:\n" " OpenJazz ~/jazz1"); PLATFORM_ErrorNoDatafiles(); throw; } pixels = file->loadRLE(46272); delete file; panelBigFont = NULL; panelSmallFont = NULL; font2 = NULL; fontbig = NULL; fontiny = NULL; fontmn1 = NULL; try { panelBigFont = new Font(pixels + (40 * 320), true); panelSmallFont = new Font(pixels + (48 * 320), false); font2 = new Font("FONT2.0FN"); fontbig = new Font("FONTBIG.0FN"); fontiny = new Font("FONTINY.0FN"); fontmn1 = new Font("FONTMN1.0FN"); fontmn2 = new Font("FONTMN2.0FN"); } catch (int e) { if (panelBigFont) delete panelBigFont; if (panelSmallFont) delete panelSmallFont; if (font2) delete font2; if (fontbig) delete fontbig; if (fontiny) delete fontiny; if (fontmn1) delete fontmn1; delete[] pixels; closeAudio(); throw; } delete[] pixels; // Establish arbitrary timing globalTicks = SDL_GetTicks() - 20; // Fill trigonometric function look-up tables for (int i = 0; i < 1024; i++) sinLut[i] = fixed(sinf(2 * PI * float(i) / 1024.0f) * 1024.0f); // Initiate networking net = new Network(); level = NULL; #ifdef ENABLE_JJ2 jj2Level = NULL; #endif } /** * De-initialises OpenJazz. * * Frees data, writes configuration, and shuts down SDL. */ void shutDown () { delete net; delete panelBigFont; delete panelSmallFont; delete font2; delete fontbig; delete fontiny; delete fontmn1; delete fontmn2; closeAudio(); video.deinit(); // Save settings to config file setup.save(); } /** * Run the cutscenes and the main menu. * * @return Error code */ int play () { MainMenu *mainMenu = NULL; JJ1Scene *scene = NULL; // Start the opening music playMusic("MENUSNG.PSM"); // Try loading the user-specified level if (cli.level > -1 && cli.world > -1) { try { mainMenu = new MainMenu(); } catch (int e) { return e; } if (mainMenu->skip(cli.level, cli.world) == E_QUIT) { delete mainMenu; } return E_NONE; } // Load and play the startup cutscene try { scene = new JJ1Scene("STARTUP.0SC"); } catch (int e) { return e; } if (scene->play() == E_QUIT) { delete scene; return E_NONE; } delete scene; // Load and run the menu try { mainMenu = new MainMenu(); } catch (int e) { return e; } if (mainMenu->main() == E_QUIT) { delete mainMenu; return E_NONE; } delete mainMenu; // Load and play the ending cutscene try { scene = new JJ1Scene("END.0SC"); } catch (int e) { return e; } scene->play(); delete scene; return E_NONE; } /** * Process iteration. * * Called once per game iteration. Updates timing, video, and input * * @param type Type of loop. Normal, typing, or input configuration * @param paletteEffects Palette effects to apply to video output * @param effectsStopped Whether the effects should be applied without advancing * * @return Error code */ int loop (LoopType type, PaletteEffect* paletteEffects, bool effectsStopped) { SDL_Event event; int prevTicks, ret; // Update tick count prevTicks = globalTicks; globalTicks = SDL_GetTicks(); if (globalTicks - prevTicks < 4) { // Limit framerate SDL_Delay(4 + prevTicks - globalTicks); globalTicks = SDL_GetTicks(); } // Show what has been drawn video.flip(globalTicks - prevTicks, paletteEffects, effectsStopped); // Process system events while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) return E_QUIT; ret = controls.update(&event, type); if (ret != E_NONE) return ret; video.update(&event); #if defined(WIZ) || defined(GP2X) if ((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP)) { if (event.jbutton.button == GP2X_BUTTON_VOLUP ) { if( event.type == SDL_JOYBUTTONDOWN ) volume_direction = VOLUME_UP; else volume_direction = VOLUME_NOCHG; } if (event.jbutton.button == GP2X_BUTTON_VOLDOWN ) { if( event.type == SDL_JOYBUTTONDOWN ) volume_direction = VOLUME_DOWN; else volume_direction = VOLUME_NOCHG; } } #endif } controls.loop(); #ifdef PSP if (PSP_WantsExit()) return E_QUIT; #elif defined(WIZ) || defined(GP2X) WIZ_AdjustVolume( volume_direction ); #endif return E_NONE; } /** * Main. * * Initialises SDL and launches game. */ int main(int argc, char *argv[]) { int ret; const char *argv0 = NULL; PLATFORM_Init(); // Some platforms (and emulators) do not provide arguments if (argc) { // Save program path argv0 = argv[0]; // Check command line options argc = checkOptions(argc, argv); } // Log current version LOG_INFO("This is OpenJazz %s, built on %s.", oj_version, oj_date); // Initialise SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK) < 0) { LOG_FATAL("Could not start SDL: %s\n", SDL_GetError()); return -1; } // Load configuration and establish a window try { startUp(argv0, argc, argv); } catch (int e) { SDL_Quit(); return -1; } // Play the opening cutscene, run the main menu, etc. ret = play(); // Save configuration and shut down LOG_DEBUG("Shutting down engine."); shutDown(); PLATFORM_Exit(); SDL_Quit(); return ret; } openjazz-20240919/src/menu/000077500000000000000000000000001467303304200153635ustar00rootroot00000000000000openjazz-20240919/src/menu/filemenu.cpp000066400000000000000000000044661467303304200177050ustar00rootroot00000000000000 /** * * @file filemenu.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the running of the menus used save and load a game. * */ #include "menu.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "jj1/save/jj1save.h" #include "loop.h" #include "util.h" #include int FileMenu::main (bool forSaving, bool showCustom) { int x, y; int chosen = 0; std::unique_ptr save[4]; int options = showCustom ? 5 : 4; // load save games char* fileName = createString("SAVE.0"); for (int i = 0; i < 4; i++) { save[i] = std::make_unique(fileName); fileName[5]++; } delete[] fileName; const char *loadGameOptions[5] = {save[0]->name, save[1]->name, save[2]->name, save[3]->name, "custom"}; auto fileChosen = [&]() -> bool { if (chosen < 4 && !forSaving) { if(!save[chosen]->valid) { playSound(SE::WAIT); return false; } } playConfirmSound(); return true; }; video.setPalette(menuPalette); while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_RETURN; if (controls.release(C_UP)) chosen = (chosen + options - 1) % options; if (controls.release(C_DOWN)) chosen = (chosen + 1) % options; if (controls.release(C_ENTER)) { if(fileChosen()) return chosen; } if (controls.getCursor(x, y)) { if ((x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_RETURN; x -= canvasW >> 2; y -= (canvasH >> 1) - (options << 3); if ((x >= 0) && (x < 256) && (y >= 0) && (y < (options << 4))) { chosen = y >> 4; if (controls.wasCursorReleased()) { if(fileChosen()) return chosen; } } } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); fontmn2->showString(forSaving ? "SAVE GAME" : "LOAD GAME", canvasW >> 2, (canvasH >> 1) - (options << 3) - 32); for (int i = 0; i < options; i++) { if (i == chosen) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString(loadGameOptions[i], canvasW >> 2, (canvasH >> 1) + (i << 4) - (options << 3)); if (i == chosen) fontmn2->restorePalette(); } showEscString(); } return E_NONE; } openjazz-20240919/src/menu/gamemenu.cpp000066400000000000000000000346431467303304200176770ustar00rootroot00000000000000 /** * * @file gamemenu.cpp * * Part of the OpenJazz project * * @par History: * - 23rd of August 2005: Created menu.c * - 3rd of February 2009: Renamed menu.c to menu.cpp * - 18th July 2009: Created menugame.cpp from parts of menu.cpp * - 26th July 2009: Renamed menugame.cpp to gamemenu.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the running of the menus used to create a new game. * */ #include "menu.h" #include "game/game.h" #include "game/gamemode.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "jj1/save/jj1save.h" #include "loop.h" #include "util.h" #include "io/log.h" #include static bool getSaveData(int slot, int &lvl, int &plnt, int &dffclty) { // load save game char* fileName = createString("SAVE.0"); fileName[5] += slot; auto save = std::make_unique(fileName); delete[] fileName; if(save->valid) { lvl = save->level; plnt = save->planet; dffclty = save->difficulty; return true; } return false; } /** * Create the game menu. * * @param file File containing menu graphics */ GameMenu::GameMenu (File *file) { difficulty = 0; // Load the difficulty graphics file->loadPalette(menuPalette); difficultyScreen = file->loadSurface(SW, SH); enableColorKey(difficultyScreen, 0); // Load the episode pictures (max. 10 episodes + bonus level) // Load their palette file->loadPalette(palette); // Generate a greyscale mapping for (int i = 0; i < MAX_PALETTE_COLORS; i++) { int col = ((palette[i].r >> 1) + (palette[i].g << 1) + (palette[i].b >> 1)) >> 3; if (col > 79) col = 79; greyPalette[i].r = greyPalette[i].g = greyPalette[i].b = col; } episodes = MAX_EPISODES; for (int i = 0; i < MAX_EPISODES; i++) { episodeScreens[i] = file->loadSurface(134, 110); if (file->tell() >= file->getSize()) { episodes = ++i; for (; i < MAX_EPISODES; i++) { unsigned char pixel = 0; episodeScreens[i] = createSurface(&pixel, 1, 1); } } } fileMenu = new FileMenu(); } /** * Delete the game menu. */ GameMenu::~GameMenu () { for (int i = 0; i < MAX_EPISODES; i++) SDL_FreeSurface(episodeScreens[i]); SDL_FreeSurface(difficultyScreen); delete fileMenu; } /** * Create and play a new game. * * @param mode Game mode * @param firstLevel First level's file name * * @return Error code */ int GameMenu::playNewGame (GameModeType mode, char* firstLevel) { Game* game; int ret; playConfirmSound(); if (mode == M_SINGLE) { try { game = new LocalGame(firstLevel, difficulty); } catch (int e) { if (message("COULD NOT START GAME") == E_QUIT) return E_QUIT; return e; } } else { try { game = new ServerGame(mode, firstLevel, difficulty); } catch (int e) { if (message("COULD NOT CREATE SERVER") == E_QUIT) return E_QUIT; return e; } } // Play the level(s) ret = game->play(); delete game; if (ret != E_QUIT) playMusic("MENUSNG.PSM"); switch (ret) { case E_LOAD0: case E_LOAD1: case E_LOAD2: case E_LOAD3: { // FIXME: Rewrite load logic, save state in memory int lvl, plnt, dffclty; if (getSaveData(ret - E_LOAD0, lvl, plnt, dffclty)) { char* firstLevel = createFileName("LEVEL", lvl, plnt); difficulty = dffclty; ret = playNewGame(M_SINGLE, firstLevel); // recursive call delete[] firstLevel; } else ret = E_NONE; return ret; } case E_QUIT: return E_QUIT; case E_FILE: return message("FILE NOT FOUND"); } return E_NONE; } /** * Run the new game difficulty menu. * * @param mode Game mode * @param firstLevel First level's file name * * @return Error code */ int GameMenu::newGameDifficulty (GameModeType mode, char* firstLevel) { const char *options[4] = {"easy", "medium", "hard", "turbo"}; SDL_Rect src, dst; int x, y, count; video.setPalette(menuPalette); while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_NONE; if (controls.release(C_UP)) difficulty = (difficulty + 3) % 4; if (controls.release(C_DOWN)) difficulty = (difficulty + 1) % 4; if (controls.release(C_ENTER)) return playNewGame(mode, firstLevel); if (controls.getCursor(x, y)) { if ((x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; x -= canvasW >> 2; y -= (canvasH >> 1) - 32; if ((x >= 0) && (x < 256) && (y >= 0) && (y < 64)) { difficulty = y >> 4; if (controls.wasCursorReleased()) return playNewGame(mode, firstLevel); } } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); for (count = 0; count < 4; count++) { if (count == difficulty) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString(options[count], canvasW >> 2, (canvasH >> 1) + (count << 4) - 32); if (count == difficulty) fontmn2->restorePalette(); } src.x = (difficulty & 1) * 160; src.y = (difficulty & 2) * 50; src.w = 160; src.h = 100; dst.x = (canvasW >> 1) - 40; dst.y = (canvasH >> 1) - 50; SDL_BlitSurface(difficultyScreen, &src, canvas, &dst); showEscString(); } return E_NONE; } /** * Run the new game difficulty menu. * * @param mode Game mode * @param levelNum First level's number * @param levelNum First level's world number * * @return Error code */ int GameMenu::newGameDifficulty (GameModeType mode, int levelNum, int worldNum) { char* firstLevel; int ret; if (levelNum == -1) firstLevel = createFileName("BONUSMAP", worldNum); else firstLevel = createFileName("LEVEL", levelNum, worldNum); ret = newGameDifficulty(mode, firstLevel); delete[] firstLevel; return ret; } /** * Run the game loading menu. * * @return Error code */ int GameMenu::loadGame () { int ret; while (true) { ret = fileMenu->main(false, true); if (ret < 0) return ret; if (ret == 4) { while (true) { ret = loadGameCustom(); if (ret == E_QUIT) return E_QUIT; // return to main menu if (ret <= 0) break; // return to load menu } } else { // load save game int lvl, plnt, dffclty; if (getSaveData(ret, lvl, plnt, dffclty)) { char* firstLevel = createFileName("LEVEL", lvl, plnt); difficulty = dffclty; ret = playNewGame(M_SINGLE, firstLevel); delete[] firstLevel; } else ret = E_NONE; break; } } return ret; } /** * Run the custom game loading menu. * * @return Error code */ int GameMenu::loadGameCustom () { int option, worldNum, levelNum, x, y; worldNum = levelNum = option = 0; video.setPalette(menuPalette); while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_NONE; if (controls.release(C_UP)) option ^= 1; if (controls.release(C_DOWN)) option ^= 1; if (controls.release(C_LEFT)) { if (option) levelNum = ((levelNum + MAX_EPISODES) % MAX_EPISODES) - 1; else worldNum = (worldNum + 999) % 1000; } if (controls.release(C_RIGHT)) { if (option) levelNum = ((levelNum + 2) % MAX_EPISODES) - 1; else worldNum = (worldNum + 1) % 1000; } if (controls.release(C_ENTER)) { playConfirmSound(); if (newGameDifficulty(M_SINGLE, levelNum, worldNum) == E_QUIT) return E_QUIT; video.setPalette(menuPalette); } if (controls.getCursor(x, y)) { if ((x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; if (y < (canvasH >> 1)) option = 0; else option = 1; } SDL_Delay(T_MENU_FRAME); video.clearScreen(15); if (option == 0) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString("choose world:", 32, canvasH / 3); fontmn2->showNumber(worldNum, 208, canvasH / 3); if (option == 0) fontmn2->restorePalette(); else fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString("choose level:", 32, (canvasH << 1) / 3); if (levelNum >= 0) fontmn2->showNumber(levelNum, 208, (canvasH << 1) / 3); else fontmn2->showString("bonus", 172, (canvasH << 1) / 3); if (option != 0) fontmn2->restorePalette(); showEscString(); } return E_NONE; } /** * Directly load level. * * @return Error code */ int GameMenu::loadGame (int levelNum, int worldNum) { if (newGameDifficulty(M_SINGLE, levelNum, worldNum) == E_QUIT) return E_QUIT; return E_NONE; } /** * Run the new game level selection menu. * * @param mode Game mode * * @return Error code */ int GameMenu::newGameLevel (GameModeType mode) { char* fileName; int ret; fileName = createString("level0.000"); ret = E_NONE; while (true) { ret = textInput("level file name:", fileName); if (ret < 0) break; ret = newGameDifficulty(mode, fileName); if (ret < 0) break; } delete[] fileName; return ret; } /** * Run the appropriate menu for the given episode selection. * * @param mode Game mode * @param episode Episode number * * @return Error code */ int GameMenu::selectEpisode (GameModeType mode, int episode) { playConfirmSound(); if (episode < 10) { int worldNum; if (episode < 6) worldNum = episode * 3; else if (episode < 9) worldNum = (episode + 4) * 3; else worldNum = 50; if (newGameDifficulty(mode, 0, worldNum) == E_QUIT) return E_QUIT; } else if (episode == 10) { if (newGameDifficulty(mode, -1, 0) == E_QUIT) return E_QUIT; } else { if (newGameLevel(mode) == E_QUIT) return E_QUIT; } video.setPalette(palette); return E_NONE; } /** * Run the new game episode menu. * * @param mode Game mode * * @return Error code */ int GameMenu::newGameEpisode (GameModeType mode) { const char *episodeTag[12] = { "1", "2", "3", "4", "5", "6", "a", "b", "c", "x", "z", " " }; const char *episodeTitle[12] = { "turtle terror", "ballistic bunny", "rabbits revenge", "gene machine", "the chase is on", "the final clash", "outta dis world", "turtle soup", "wild wabbit", "holiday hare", "bonus levels", "specific level" }; bool exists[12]; char *check; SDL_Rect dst; int episode, count, x, y; video.setPalette(palette); for (count = 0; count < 10; count++) { if (count < 6) x = count * 3; else if (count < 9) x = (count + 4) * 3; else x = 50; check = createFileName("LEVEL", 0, x); exists[count] = fileExists(check, PATH_TYPE_GAME); delete[] check; if (exists[count]) video.restoreSurfacePalette(episodeScreens[count]); else setLogicalPalette(episodeScreens[count], greyPalette, 0, MAX_PALETTE_COLORS); } if (mode == M_SINGLE) { check = createFileName("BONUSMAP", 0); exists[10] = fileExists(check, PATH_TYPE_GAME); delete[] check; } else exists[10] = false; exists[MAX_EPISODES] = true; // always possible to load custom level episode = 0; while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_NONE; if (controls.release(C_UP)) episode = (episode + MAX_EPISODES) % 12; if (controls.release(C_DOWN)) episode = (episode + 1) % 12; if (controls.release(C_ENTER) && exists[episode]) { count = selectEpisode(mode, episode); if (count < 0) return count; } if (controls.getCursor(x, y)) { if ((x >= canvasW - 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; x -= canvasW >> 3; y -= (canvasH >> 1) - 92; if ((x >= 0) && (x < 256) && (y >= 0) && (y < 192)) { episode = y >> 4; if (controls.wasCursorReleased() && exists[episode]) { count = selectEpisode(mode, episode); if (count < 0) return count; } } } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); dst.x = canvasW - 144; dst.y = (canvasH - 110) >> 1; if ((episode < episodes - 1) || (episode < 6)) { SDL_BlitSurface(episodeScreens[episode], NULL, canvas, &dst); } else if ((episode == 10) && (episodes > 6)) { SDL_BlitSurface(episodeScreens[episodes - 1], NULL, canvas, &dst); } int episodeX = canvasW >> 3; for (count = 0; count < 12; count++) { if (count == episode) { // black on white fontmn2->mapPalette(240, 8, 79, -80); drawRect(episodeX - 2, (canvasH >> 1) + (count << 4) - 94, 160, 15, 79); } else if (!exists[count]) fontmn2->mapPalette(240, 8, 94, -16); // align both separately fontmn2->showString(episodeTag[count], episodeX - (count < 6 ? 2 : 0), (canvasH >> 1) + (count << 4) - 92); fontmn2->showString(episodeTitle[count], episodeX + 16, (canvasH >> 1) + (count << 4) - 92); if ((count == episode) || (!exists[count])) fontmn2->mapPalette(240, 8, 9, 80); } fontbig->showString(ESCAPE_STRING, canvasW - 100, canvasH - 12); } return E_NONE; } /** * Run the game joining menu. * * @return Error code */ int GameMenu::joinGame () { Game* game; int ret; ret = textInput("ip address:", netAddress, true); if (ret < 0) return ret; try { game = new ClientGame(netAddress); } catch (int e) { switch (e) { case E_N_SOCKET: if (message("SOCKET ERROR") == E_QUIT) return E_QUIT; break; case E_N_ADDRESS: if (message("INVALID ADDRESS") == E_QUIT) return E_QUIT; break; case E_N_CONNECT: if (message("COULD NOT CONNECT") == E_QUIT) return E_QUIT; break; case E_TIMEOUT: if (message("OPERATION TIMED OUT") == E_QUIT) return E_QUIT; break; case E_DATA: if (message("INCORRECT DATA\nRECEIVED") == E_QUIT) return E_QUIT; break; case E_VERSION: if (message("WRONG SERVER VERSION") == E_QUIT) return E_QUIT; break; case E_RETURN: case E_QUIT: break; default: if (message("COULD NOT COMPLETE CONNECTION") == E_QUIT) return E_QUIT; break; } return e; } // Play the level(s) ret = game->play(); delete game; if (ret != E_QUIT) playMusic("MENUSNG.PSM"); switch (ret) { case E_QUIT: return E_QUIT; case E_FILE: return message("FILE NOT FOUND"); case E_N_DISCONNECT: return message("DISCONNECTED"); } return E_NONE; } /** * Run the new game menu. * * @return Error code */ int GameMenu::newGame () { #if (defined USE_SOCKETS) || (defined USE_SDL_NET) const char *newGameOptions[6] = {"new single player game", "new co-op game", "new battle", "new team battle", "new race", "join game"}; int option = 0; while (true) { video.setPalette(menuPalette); int ret = generic(newGameOptions, 6, option); if (ret == E_QUIT) return E_QUIT; if (ret < 0) return E_NONE; if (option == 5) { if (joinGame() == E_QUIT) return E_QUIT; } else { if (newGameEpisode(GameModeType(option)) == E_QUIT) return E_QUIT; } } return E_NONE; #else return newGameEpisode(M_SINGLE); #endif } openjazz-20240919/src/menu/mainmenu.cpp000066400000000000000000000165211467303304200177050ustar00rootroot00000000000000 /** * * @file mainmenu.cpp * * Part of the OpenJazz project * * @par History: * - 23rd of August 2005: Created menu.c * - 3rd of February 2009: Renamed menu.c to menu.cpp * - 19th July 2009: Created menumain.cpp from parts of menu.cpp * - 26th July 2009: Renamed menumain.cpp to mainmenu.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the running of the main menu and its generic sub-menus. * */ #include "menu.h" #include "plasma.h" #include "game/game.h" #include "io/controls.h" #include "io/gfx/video.h" #include "io/gfx/font.h" #include "io/sound.h" #include "jj1/scene/jj1scene.h" #include "loop.h" #include "util.h" #include "io/log.h" #include "logo.h" #include "version.h" #include #include /** * Create the main menu. */ MainMenu::MainMenu () { File *file; time_t currentTime; // Load the OpenJazz logo unsigned char* pixels = new unsigned char[oj_logo.size]; size_t res = tinfl_decompress_mem_to_mem(pixels, oj_logo.size, oj_logo.data, oj_logo.compressed_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF|TINFL_FLAG_PARSE_ZLIB_HEADER); if (res == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED || res != oj_logo.size) { LOG_WARN("Could not uncompress logo (expected: %d, actual: %zu).", oj_logo.size, res); } else { logo = createSurface(pixels, oj_logo.width, oj_logo.height); } delete[] pixels; // Load the menu graphics try { file = new File("MENU.000", PATH_TYPE_GAME); } catch (int e) { if (logo) SDL_FreeSurface(logo); throw; } // only available in Holiday Hare 94/95 if (file->getSize() > 200000) { time(¤tTime); // In December, load the Christmas menu graphics if (localtime(¤tTime)->tm_mon == 11) { LOG_TRACE("Loading christmas menu theme."); file->skipRLE(); file->skipRLE(); file->skipRLE(); file->loadPalette(palette); background = file->loadSurface(SW, SH); highlight = file->loadSurface(SW, SH); } else { file->loadPalette(palette); background = file->loadSurface(SW, SH); highlight = file->loadSurface(SW, SH); file->skipRLE(); file->skipRLE(); file->skipRLE(); } } else { file->loadPalette(palette); background = file->loadSurface(SW, SH); highlight = file->loadSurface(SW, SH); } enableColorKey(background, 0); enableColorKey(highlight, 0); if (logo) enableColorKey(logo, 28); gameMenu = new GameMenu(file); delete file; } /** * Delete the main menu. */ MainMenu::~MainMenu () { SDL_FreeSurface(background); SDL_FreeSurface(highlight); if (logo) SDL_FreeSurface(logo); delete gameMenu; } /** * Process a main menu selection. * * @param option Chosen menu option * * @return Error code */ int MainMenu::select (int option) { JJ1Scene *scene; SetupMenu setupMenu; playConfirmSound(); switch (option) { case 0: // New game if (gameMenu->newGame() == E_QUIT) return E_QUIT; break; case 1: // Load game if (gameMenu->loadGame() == E_QUIT) return E_QUIT; break; case 2: // Instructions try { scene = new JJ1Scene("INSTRUCT.0SC"); } catch (int e) { if (message("COULD NOT LOAD INSTRUCTIONS") == E_QUIT) return E_QUIT; break; } if (scene->play() == E_QUIT) { delete scene; return E_QUIT; } delete scene; break; case 3: // Setup options if (setupMenu.setupMain() == E_QUIT) return E_QUIT; break; case 4: // Order info try { scene = new JJ1Scene("ORDER.0SC"); } catch (int e) { if (message("COULD NOT LOAD ORDER INFO") == E_QUIT) return E_QUIT; break; } if (scene->play() == E_QUIT) { delete scene; return E_QUIT; } delete scene; break; case 5: // Exit return E_RETURN; } // Restore the main menu palette video.setPalette(palette); return E_NONE; } /** * Run the main menu. * * @return Error code */ int MainMenu::main () { SDL_Rect options[6] = { {92, 35, 136, 22}, {92, 57, 140, 22}, {88, 83, 144, 22}, {86, 109, 150, 23}, {82, 137, 156, 26}, {78, 166, 166, 29}}; int macroType[4]; File* file; char* fileName; Plasma plasma; SDL_Rect dst; int option, macro, x, y, ret; unsigned int idleTime; option = 0; video.setPalette(palette); playMusic("MENUSNG.PSM"); // Demo timeout idleTime = globalTicks + T_DEMO; // Check for demo macros fileName = createString("MACRO.1"); for (macro = 0; macro < 4; macro++) { try { file = new File(fileName, PATH_TYPE_GAME); macroType[macro] = file->loadChar(); delete file; } catch (int e) { macroType[macro] = -1; } fileName[6]++; } macro = 3; delete[] fileName; while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) option = 5; if (controls.release(C_UP)) option = (option + 5) % 6; if (controls.release(C_DOWN)) option = (option + 1) % 6; if (controls.release(C_ENTER)) { ret = select(option); if (ret < 0) return ret; // New demo timeout idleTime = globalTicks + T_DEMO; } if (controls.getCursor(x, y)) { int count; x -= (canvasW - SW) >> 1; y -= (canvasH - SH) >> 1; for (count = 0; count < 6; count++) { if ((x >= options[count].x) && (x < options[count].x + options[count].w) && (y >= options[count].y) && (y < options[count].y + options[count].h)) { option = count; if (controls.wasCursorReleased()) { ret = select(option); if (ret < 0) return ret; } // New demo timeout idleTime = globalTicks + T_DEMO; break; } } } if (idleTime <= globalTicks) { Game* game = NULL; try { game = new LocalGame("", 0); } catch (int e) { // Do nothing } if (game) { // Load the macro x = macro; macro = (macro + 1) & 3; while ((macroType[macro] != 0xFF) && (macro != x)) macro = (macro + 1) & 3; if (macro != x) { fileName = createString("MACRO.1"); fileName[6] += macro; if (game->playLevel(fileName) == E_QUIT) { delete[] fileName; delete game; return E_QUIT; } delete[] fileName; } delete game; playMusic("MENUSNG.PSM"); // Restore the main menu palette video.setPalette(palette); } idleTime = globalTicks + T_DEMO; } SDL_Delay(T_MENU_FRAME); //as long as we're drawing plasma, we don't need to clear the screen. //video.clearScreen(28); plasma.draw(); // draw logo and version string if (logo) { dst.x = (canvasW >> 2) - 72; dst.y = canvasH - (canvasH >> 2); SDL_BlitSurface(logo, NULL, canvas, &dst); } panelBigFont->mapPalette(0, MAX_PALETTE_COLORS, 8, 8); panelBigFont->showString("OpenJazz", 1, canvasH - 9); panelBigFont->showString(oj_version, 90, canvasH - 9); panelBigFont->restorePalette(); dst.x = (canvasW - SW) >> 1; dst.y = (canvasH - SH) >> 1; SDL_BlitSurface(background, NULL, canvas, &dst); dst.x = ((canvasW - SW) >> 1) + options[option].x; dst.y = ((canvasH - SH) >> 1) + options[option].y; SDL_BlitSurface(highlight, options + option, canvas, &dst); } return E_NONE; } /** * Skip the main menu. Directly loads game * * @return Error code */ int MainMenu::skip (int levelNum, int worldNum) { if (gameMenu->loadGame(levelNum, worldNum) == E_QUIT) return E_QUIT; return E_RETURN; } openjazz-20240919/src/menu/menu.cpp000066400000000000000000000142451467303304200170410ustar00rootroot00000000000000 /** * * @file menu.cpp * * Part of the OpenJazz project * * @par History: * - 23rd of August 2005: Created menu.c * - 3rd of February 2009: Renamed menu.c to menu.cpp * - 9th March 2009: Created game.cpp from parts of menu.cpp and level.cpp * - 18th July 2009: Created menugame.cpp from parts of menu.cpp * - 18th July 2009: Created menuutil.cpp from parts of menu.cpp * - 18th July 2009: Created menusetup.cpp from parts of menu.cpp * - 19th July 2009: Created menumain.cpp from parts of menu.cpp * - 23rd June 2010: Merged menuutil.cpp into menu.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Provides various generic menus. * */ #include "menu.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "loop.h" #include "util.h" #ifdef _3DS #include "platforms/3ds.h" #elif defined(__vita__) #include "platforms/psvita.h" #endif #include /** * Show the "(esc) quits" string. */ void Menu::showEscString () { fontbig->showString(ESCAPE_STRING, 3, canvasH - 12); } /** * Display a message to the user. * * @param text The message to display * * @return Error code */ int Menu::message (const char* text) { video.setPalette(menuPalette); while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ENTER) || controls.release(C_ESCAPE) || controls.wasCursorReleased()) return E_NONE; SDL_Delay(T_MENU_FRAME); video.clearScreen(15); // Draw the message fontmn2->showString(text, canvasW >> 2, (canvasH >> 1) - 16); } return E_NONE; } /** * Let the user select from a menu of the given options. * * @param optionNames Array of option names * @param options The number of options (and size of the names array) * @param chosen Which option is selected * * @return Error code */ int Menu::generic (const char** optionNames, int options, int& chosen) { int x, y, count; if (chosen >= options) chosen = 0; while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_RETURN; if (controls.release(C_UP)) chosen = (chosen + options - 1) % options; if (controls.release(C_DOWN)) chosen = (chosen + 1) % options; if (controls.release(C_ENTER)) { playConfirmSound(); return E_NONE; } if (controls.getCursor(x, y)) { if ((x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_RETURN; x -= canvasW >> 2; y -= (canvasH >> 1) - (options << 3); if ((x >= 0) && (x < 256) && (y >= 0) && (y < (options << 4))) { chosen = y >> 4; if (controls.wasCursorReleased()) { playConfirmSound(); return E_NONE; } } } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); for (count = 0; count < options; count++) { if (count == chosen) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString(optionNames[count], canvasW >> 2, (canvasH >> 1) + (count << 4) - (options << 3)); if (count == chosen) fontmn2->restorePalette(); } showEscString(); } return E_NONE; } /** * Let the user edit a text string * * @param request Description of the text string * @param text The text string to be edited * * @return Error code */ int Menu::textInput (const char* request, char*& text, bool ip) { char *input; #ifdef _3DS int res; if (ip) res = N3DS_InputIP(text, input); else res = N3DS_InputString(request, text, input); if (res) { playConfirmSound(); delete[] text; text = input; return E_NONE; } #elif defined(__vita__) if (PSVITA_InputString(request, text, input)) { playConfirmSound(); delete[] text; text = input; return E_NONE; } #else int count, terminate, added, x, y; video.setPalette(menuPalette); // Create input string input = createEditableString(text); unsigned int cursor = strlen(input); while (true) { int character = loop(TYPING_LOOP); if (character == E_QUIT) { delete[] input; return E_QUIT; } // Ensure there is space for another character if (cursor < STRING_LENGTH) { terminate = (input[cursor] == 0); // If the character is valid, add it to the input string added = 0; if (!ip) { if (((character >= 'a') && (character <= 'z')) || (character == ' ')) { input[cursor] = character; added = 1; } else if ((character >= 'A') && (character <= 'Z')) { input[cursor] = character | 32; added = 1; } } if (((character >= '0') && (character <= '9')) || (character == '.')) { input[cursor] = character; added = 1; } if (added) { cursor++; if (terminate) input[cursor] = 0; } } if ((character == SDLK_DELETE) && (cursor < strlen(input))) { for (count = cursor; count < STRING_LENGTH; count++) input[count] = input[count + 1]; } if ((character == SDLK_BACKSPACE) && (cursor > 0)) { for (count = cursor - 1; count < STRING_LENGTH; count++) input[count] = input[count + 1]; cursor--; } if (controls.release(C_ESCAPE) || (controls.getCursor(x, y) && (x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased())) { delete[] input; return E_RETURN; } SDL_Delay(T_MENU_FRAME); video.clearScreen(15); // Draw the prompt fontmn2->showString(request, canvasW >> 2, (canvasH >> 1) - 16); // Draw the section of the text before the cursor fontmn2->mapPalette(240, 8, 114, 16); terminate = input[cursor]; input[cursor] = 0; x = fontmn2->showString(input, (canvasW >> 2) + 8, canvasH >> 1); // Draw the cursor drawRect(x, (canvasH >> 1) + 10, 8, 2, 79); // Draw the section of text after the cursor input[cursor] = terminate; fontmn2->showString(input + cursor, x, canvasH >> 1); fontmn2->restorePalette(); showEscString(); if (controls.release(C_LEFT) && (cursor > 0)) cursor--; if (controls.release(C_RIGHT) && (cursor < strlen(input))) cursor++; if (controls.release(C_ENTER)) { playConfirmSound(); // Replace the original string with the input string delete[] text; text = input; return E_NONE; } } delete[] input; #endif return E_RETURN; } openjazz-20240919/src/menu/menu.h000066400000000000000000000060161467303304200165030ustar00rootroot00000000000000 /** * * @file menu.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 3rd February 2009: Created menu.h from parts of OpenJazz.h * - 21st July 2013: Created setup.h from parts of menu.h * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _MENU_H #define _MENU_H #include "game/gamemode.h" #include "io/file.h" #include "OpenJazz.h" // Constants #define ESCAPE_STRING "(esc) quits" #define MAX_EPISODES 11 // Demo timeout #define T_DEMO 20000 // Classes /// Menu base class, providing generic menu screens class Menu { protected: void showEscString (); int message (const char* text); int generic (const char** optionNames, int options, int& chosen); int textInput (const char* request, char*& text, bool ip = false); }; /// Saving and loading menu class FileMenu : public Menu { public: int main (bool forSaving, bool showCustom); }; /// New game menus class GameMenu : public Menu { private: SDL_Surface* episodeScreens[MAX_EPISODES]; ///< Episode images SDL_Surface* difficultyScreen; ///< 4 difficulty images SDL_Color palette[MAX_PALETTE_COLORS]; ///< Episode selection palette SDL_Color greyPalette[MAX_PALETTE_COLORS]; ///< Greyed-out episode selection palette int episodes; ///< Number of episodes unsigned char difficulty; ///< Difficulty setting (0 = easy, 1 = medium, 2 = hard, 3 = turbo (hard in JJ2 levels)) FileMenu* fileMenu; ///< Load menu int playNewGame (GameModeType mode, char* firstLevel); int newGameDifficulty (GameModeType mode, char* firstLevel); int newGameDifficulty (GameModeType mode, int levelNum, int worldNum); int newGameLevel (GameModeType mode); int selectEpisode (GameModeType mode, int episode); int newGameEpisode (GameModeType mode); int joinGame (); int loadGameCustom (); public: explicit GameMenu(File* file); ~GameMenu(); int newGame (); int loadGame (); int loadGame (int levelNum, int worldNum); }; /// Setup menus class SetupMenu : public Menu { private: int setupKeyboard (); int setupJoystick (); int setupResolution (); #ifdef SCALE int setupScaling (); #endif int setupSound (); public: int setupMain (); }; /// Main menu class MainMenu : public Menu { private: SDL_Surface* background; ///< Menu image SDL_Surface* highlight; ///< Menu image with highlighted text SDL_Surface* logo; ///< OJ logo image GameMenu* gameMenu; ///< New game menu SDL_Color palette[MAX_PALETTE_COLORS]; ///< Menu palette MainMenu(const MainMenu&); // non construction-copyable MainMenu& operator=(const MainMenu&); // non copyable int select (int option); public: MainMenu (); ~MainMenu (); int main (); int skip (int levelNum, int worldNum); }; // Variables EXTERN SDL_Color menuPalette[MAX_PALETTE_COLORS]; /// Palette used by most menu screens #endif openjazz-20240919/src/menu/plasma.cpp000066400000000000000000000026511467303304200173500ustar00rootroot00000000000000 /** * * @file plasma.cpp * * Part of the OpenJazz project * * @par History: * - 23rd June 2010: Created plasma.cpp * * @par Licence: * Copyright (c) 2010 Alireza Nejati * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Cool plasma effects for the main menu. * */ #include "plasma.h" #include "level/level.h" #include "util.h" #include "io/gfx/video.h" #include /** * Create the plasma. */ Plasma::Plasma(){ p0=0; p1=0; p2=0; p3=0; //fSin, fCos: pi = 512 // -1024 < out < 1024 } /** * Draw the plasma. * * @return Error code */ int Plasma::draw(){ int x,y; int w,h,pitch; unsigned char *px; unsigned char colour; // draw plasma SDL_LockSurface(canvas); w = canvas->w; h = canvas->h; pitch = canvas->pitch; px = static_cast(canvas->pixels); int t1 = p0; int t2 = p1; for(y=0;y>10) & 0xF; t3 += 3; t4 += 2; px[x] = colour; } // go to next row px += pitch; t1 += 2; t2 += 1; } p0 = p0 < 256 ? p0+1 : 1; p1 = p1 < 256 ? p1+2 : 2; p2 = p2 < 256 ? p2+3 : 3; p3 = p3 < 256 ? p3+4 : 4; SDL_UnlockSurface(canvas); return E_NONE; } openjazz-20240919/src/menu/plasma.h000066400000000000000000000007041467303304200170120ustar00rootroot00000000000000 /** * * @file plasma.h * * Part of the OpenJazz project * * @par History: * - 23rd June 2010: Created plasma.h * * @par Licence: * Copyright (c) 2010 Alireza Nejati * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _PLASMA_H #define _PLASMA_H /// Main menu background plasma effect class Plasma { private: int p0,p1,p2,p3; public: Plasma (); int draw(); }; #endif openjazz-20240919/src/menu/setupmenu.cpp000066400000000000000000000422411467303304200201170ustar00rootroot00000000000000 /** * * @file setupmenu.cpp * * Part of the OpenJazz project * * @par History: * - 23rd of August 2005: Created menu.c * - 3rd of February 2009: Renamed menu.c to menu.cpp * - 18th July 2009: Created menusetup.cpp from parts of menu.cpp * - 26th July 2009: Renamed menusetup.cpp to setupmenu.cpp * - 21st July 2013: Created setup.cpp from parts of main.cpp and setupmenu.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the running of setup menus. * */ #include "menu.h" #include "io/controls.h" #include "io/gfx/font.h" #include "io/gfx/video.h" #include "io/sound.h" #include "player/player.h" #include "loop.h" #include "setup.h" #include "util.h" /** * Run the keyboard setup menu. * * @return Error code */ int SetupMenu::setupKeyboard () { const char *options[PCONTROLS] = {"up", "down", "left", "right", "jump", "swim up", "fire", "weapon"}; int progress, x, y, count; progress = 0; while (true) { int character = loop(SET_KEY_LOOP); if (character == E_QUIT) return E_QUIT; if (character == controls.getKey(C_ESCAPE)) return E_NONE; if (controls.getCursor(x, y) && (x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; if (character > 0) { // If this is a navigation controls (up, down, or enter), // make sure it's not the same as other navigation controls if (((progress != C_UP) && (progress != C_DOWN) && (progress != C_ENTER)) || (controls.getKey(progress) == character) || ((controls.getKey(C_UP) != character) && (controls.getKey(C_DOWN) != character) && (controls.getKey(C_ENTER) != character))) { controls.setKey(progress, character); progress++; if (progress == PCONTROLS) { // If all controls have been assigned, return playConfirmSound(); return E_NONE; } } } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); for (count = 0; count < PCONTROLS; count++) { if (count < progress) fontmn2->showString("okay", (canvasW >> 2) + 176, (canvasH >> 1) + (count << 4) - 56); else if (count == progress) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString(options[count], canvasW >> 2, (canvasH >> 1) + (count << 4) - 56); if (count == progress) { fontmn2->showString("press key", (canvasW >> 2) + 112, (canvasH >> 1) + (count << 4) - 56); fontmn2->restorePalette(); } } showEscString(); } return E_NONE; } /** * Run the joystick setup menu. * * @return Error code */ int SetupMenu::setupJoystick () { const char *options[PCONTROLS] = {"up", "down", "left", "right", "jump", "swim up", "fire", "weapon"}; int x, y; int progress = 0; while (true) { int control = loop(SET_JOYSTICK_LOOP); if (control == E_QUIT) return E_QUIT; switch (control & 0xF00) { case JOYSTICKB: // If this is a navigation controls (up, down, or enter), // make sure it's not the same as other navigation controls if (((progress != C_UP) && (progress != C_DOWN) && (progress != C_ENTER)) || (controls.getButton(progress) == (control & 0xFF)) || ((controls.getButton(C_UP) != (control & 0xFF)) && (controls.getButton(C_DOWN) != (control & 0xFF)) && (controls.getButton(C_ENTER) != (control & 0xFF)))) { controls.setButton(progress, control & 0xFF); progress++; if (progress == PCONTROLS) { // If all controls have been assigned, return playConfirmSound(); return E_NONE; } } break; case JOYSTICKANEG: // If this is a navigation controls (up, down, or enter), // make sure it's not the same as other navigation controls if (((progress != C_UP) && (progress != C_DOWN) && (progress != C_ENTER)) || ((controls.getAxis(progress) == (control & 0xFF)) && !controls.getAxisDirection(progress)) || (((controls.getAxis(C_UP) != (control & 0xFF)) || controls.getAxisDirection(C_UP)) && ((controls.getAxis(C_DOWN) != (control & 0xFF)) || controls.getAxisDirection(C_DOWN)) && ((controls.getAxis(C_ENTER) != (control & 0xFF)) || controls.getAxisDirection(C_ENTER)))) { controls.setAxis(progress, control & 0xFF, false); progress++; if (progress == PCONTROLS) { // If all controls have been assigned, return playConfirmSound(); return E_NONE; } } break; case JOYSTICKAPOS: // If this is a navigation controls (up, down, or enter), // make sure it's not the same as other navigation controls if (((progress != C_UP) && (progress != C_DOWN) && (progress != C_ENTER)) || ((controls.getAxis(progress) == (control & 0xFF)) && controls.getAxisDirection(progress)) || (((controls.getAxis(C_UP) != (control & 0xFF)) || !controls.getAxisDirection(C_UP)) && ((controls.getAxis(C_DOWN) != (control & 0xFF)) || !controls.getAxisDirection(C_DOWN)) && ((controls.getAxis(C_ENTER) != (control & 0xFF)) || !controls.getAxisDirection(C_ENTER)))) { controls.setAxis(progress, control & 0xFF, true); progress++; if (progress == PCONTROLS) { // If all controls have been assigned, return playConfirmSound(); return E_NONE; } } break; case JOYSTICKHUP: case JOYSTICKHLFT: case JOYSTICKHRHT: case JOYSTICKHDWN: int direction = 0; switch(control & 0xF00) { case JOYSTICKHUP: direction = SDL_HAT_UP; break; case JOYSTICKHLFT: direction = SDL_HAT_LEFT; break; case JOYSTICKHRHT: direction = SDL_HAT_RIGHT; break; case JOYSTICKHDWN: direction = SDL_HAT_DOWN; break; } // If this is a navigation controls (up, down, or enter), // make sure it's not the same as other navigation controls if (((progress != C_UP) && (progress != C_DOWN) && (progress != C_ENTER)) || ((controls.getHat(progress) == (control & 0xFF)) && (controls.getHatDirection(progress) == direction)) || (((controls.getHat(C_UP) != (control & 0xFF)) || (controls.getHatDirection(C_UP) != direction)) && ((controls.getHat(C_DOWN) != (control & 0xFF)) || (controls.getHatDirection(C_DOWN) != direction)) && ((controls.getHat(C_ENTER) != (control & 0xFF)) || (controls.getHatDirection(C_ENTER) != direction)))) { controls.setHat(progress, control & 0xFF, direction); progress++; if (progress == PCONTROLS) { // If all controls have been assigned, return playConfirmSound(); return E_NONE; } } break; } if (controls.release(C_ESCAPE)) return E_NONE; if ((controls.getCursor(x, y) && (x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased())) return E_NONE; SDL_Delay(T_MENU_FRAME); video.clearScreen(0); for (int count = 0; count < PCONTROLS; count++) { if (count < progress) fontmn2->showString("okay", (canvasW >> 2) + 176, (canvasH >> 1) + (count << 4) - 56); else if (count == progress) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString(options[count], canvasW >> 2, (canvasH >> 1) + (count << 4) - 56); if (count == progress) { fontmn2->showString("press control", (canvasW >> 2) + 112, (canvasH >> 1) + (count << 4) - 56); fontmn2->restorePalette(); } } showEscString(); } return E_NONE; } /** * Run the resolution setup menu. * * @return Error code */ int SetupMenu::setupResolution () { int widthOptions[] = {SW, 352, 384, 400, 480, 512, 640, 720, 768, 800, 960, 1024, 1152, 1280, 1366, 1400, 1440, 1600, 1680, 1920, 2048, 2560, 3200, 3440, 3840, 4096, MAX_SCREEN_WIDTH}; int heightOptions[] = {SH, 240, 288, 300, 320, 384, 400, 480, 576, 600, 720, 768, 800, 864, 900, 960, 1024, 1050, 1080, 1152, 1200, 1440, 1536, 1600, 2048, 2160, MAX_SCREEN_HEIGHT}; int screenW, screenH, oldscreenW, oldscreenH, x, y; screenW = oldscreenW = video.getWidth(); screenH = oldscreenH = video.getHeight(); bool dimension = false; bool resOK = true; while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_NONE; if (controls.getCursor(x, y)) { if ((x >= 32) && (x < 132) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; dimension = (x >= (canvasW >> 2) + 44); } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); // Show screen corners drawRect(0, 0, 32, 32, 79); drawRect(canvasW - 32, 0, 32, 32, 79); drawRect(canvasW - 32, canvasH - 32, 32, 32, 79); drawRect(0, canvasH - 32, 32, 32, 79); // X fontmn2->showString("x", (canvasW >> 2) + 40, canvasH >> 1); if (!dimension) fontmn2->mapPalette(240, 8, 114, 16); // Width fontmn2->showNumber(screenW, (canvasW >> 2) + 32, canvasH >> 1); if (!dimension) fontmn2->restorePalette(); else fontmn2->mapPalette(240, 8, 114, 16); // Height fontmn2->showNumber(screenH, (canvasW >> 2) + 104, canvasH >> 1); if (dimension) fontmn2->restorePalette(); int count = 0; if (controls.release(C_LEFT)) dimension = !dimension; if (controls.release(C_RIGHT)) dimension = !dimension; if (controls.release(C_UP)) { if ((!dimension) && (screenW < video.getMaxWidth())) { while (screenW >= widthOptions[count]) count++; screenW = widthOptions[count]; } if (dimension && (screenH < video.getMaxHeight())) { while (screenH >= heightOptions[count]) count++; screenH = heightOptions[count]; } resOK = true; } if (controls.release(C_DOWN)) { if ((!dimension) && (screenW > video.getMinWidth())) { count = sizeof(widthOptions)/sizeof(widthOptions[0]) - 1; while (screenW <= widthOptions[count]) count--; screenW = widthOptions[count]; } if (dimension && (screenH > video.getMinHeight())) { count = sizeof(heightOptions)/sizeof(heightOptions[0]) - 1; while (screenH <= heightOptions[count]) count--; screenH = heightOptions[count]; } resOK = true; } // Check for a resolution change if (screenH != oldscreenH || screenW != oldscreenW) { fontmn2->showString(resOK ? "press enter to apply" : "invalid resolution!", (canvasW >> 2) - 32, (canvasH >> 1) + 16); } // Apply resolution change if (controls.release(C_ENTER)) { playConfirmSound(); if (video.reset(screenW, screenH)) { // New resolution is ok oldscreenW = screenW; oldscreenH = screenH; } else { // It failed, reset to sanity video.reset(oldscreenW, oldscreenH); resOK = false; } } fontbig->showString(ESCAPE_STRING, 35, canvasH - 12); } return E_NONE; } #ifdef SCALE /** * Run the scaling setup menu. * * @return Error code */ int SetupMenu::setupScaling () { int scaleFactor, x, y; scaleFactor = video.getScaleFactor(); if ( scaleFactor < MIN_SCALE || scaleFactor > MAX_SCALE ) scaleFactor = 1; while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_NONE; if (controls.release(C_ENTER)) return E_NONE; if (controls.getCursor(x, y) && (x >= 32) && (x < 132) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; SDL_Delay(T_MENU_FRAME); video.clearScreen(0); // Show screen corners drawRect(0, 0, 32, 32, 79); drawRect(canvasW - 32, 0, 32, 32, 79); drawRect(canvasW - 32, canvasH - 32, 32, 32, 79); drawRect(0, canvasH - 32, 32, 32, 79); fontmn2->mapPalette(240, 8, 114, 16); // Scale fontmn2->showNumber(video.getScaleFactor(), (canvasW >> 2) + 32, canvasH >> 1); // X fontmn2->showString("x", (canvasW >> 2) + 40, canvasH >> 1); fontmn2->restorePalette(); if ((controls.release(C_DOWN) || controls.release(C_LEFT)) && (scaleFactor > MIN_SCALE)) scaleFactor--; if ((controls.release(C_UP) || controls.release(C_RIGHT)) && (scaleFactor < MAX_SCALE)) scaleFactor++; // Check for a scaling change if (scaleFactor != video.getScaleFactor()) { playConfirmSound(); scaleFactor = video.setScaleFactor(scaleFactor); } fontbig->showString(ESCAPE_STRING, 35, canvasH - 12); } return E_NONE; } #endif /** * Run the audio setup menu. * * @return Error code */ int SetupMenu::setupSound () { int x, y; bool soundActive; soundActive = false; while (true) { if (loop(NORMAL_LOOP) == E_QUIT) return E_QUIT; if (controls.release(C_ESCAPE)) return E_NONE; if (controls.release(C_ENTER)) return E_NONE; if (controls.getCursor(x, y)) { if ((x < 100) && (y >= canvasH - 12) && controls.wasCursorReleased()) return E_NONE; x -= (canvasW >> 2) + 128; y -= canvasH >> 1; if ((x >= 0) && (x < (MAX_VOLUME >> 1)) && (y >= 0) && (y < 11)) setMusicVolume(x << 1); if ((x >= 0) && (x < (MAX_VOLUME >> 1)) && (y >= 16) && (y < 27)) setSoundVolume(x << 1); if (controls.wasCursorReleased()) playConfirmSound(); } SDL_Delay(T_MENU_FRAME); video.clearScreen(0); // Music Volume if (!soundActive) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString("music volume", canvasW >> 2, canvasH >> 1); fontmn2->restorePalette(); drawRect((canvasW >> 2) + 128, canvasH >> 1, getMusicVolume() >> 1, 11, 175); // Sound Volume if (soundActive) fontmn2->mapPalette(240, 8, 114, 16); fontmn2->showString("effect volume", canvasW >> 2, (canvasH >> 1) + 16); fontmn2->restorePalette(); drawRect((canvasW >> 2) + 128, (canvasH >> 1) + 16, getSoundVolume() >> 1, 11, 175); if (controls.release(C_UP)) soundActive = !soundActive; if (controls.release(C_DOWN)) soundActive = !soundActive; if (controls.release(C_LEFT)) { if (soundActive) setSoundVolume(getSoundVolume() - 4); else setMusicVolume(getMusicVolume() - 4); playConfirmSound(); } if (controls.release(C_RIGHT)) { if (soundActive) setSoundVolume(getSoundVolume() + 4); else setMusicVolume(getMusicVolume() + 4); playConfirmSound(); } showEscString(); } return E_NONE; } /** * Run the setup menu. * * @return Error code */ int SetupMenu::setupMain () { const char* setupOptions[7] = {"character", "keyboard", "joystick", "resolution", "scaling", "sound", "gameplay"}; const char* setupCharacterOptions[5] = {"name", "fur", "bandana", "gun", "wristband"}; const char* setupCharacterColOptions[8] = {"white", "red", "orange", "yellow", "green", "blue", "animation 1", "animation 2"}; const unsigned char setupCharacterCols[8] = {PC_GREY, PC_RED, PC_ORANGE, PC_YELLOW, PC_LGREEN, PC_BLUE, PC_SANIM, PC_LANIM}; const char* setupModsOff[4] = {"slow motion: off", "extra items: take", "bird limit: one", "scaling: nearest"}; const char* setupModsOn[4] = {"slow motion: on", "extra items: leave", "bird limit: no", "scaling: filtered" }; const char* setupMods[4]; int ret; int option, suboption, subsuboption; option = 0; setupMods[0] = (setup.slowMotion? setupModsOn[0]: setupModsOff[0]); setupMods[1] = (setup.leaveUnneeded? setupModsOn[1]: setupModsOff[1]); setupMods[2] = (setup.manyBirds? setupModsOn[2]: setupModsOff[2]); setupMods[3] = (setup.scale2x? setupModsOn[3]: setupModsOff[3]); video.setPalette(menuPalette); while (true) { ret = generic(setupOptions, 7, option); if (ret == E_RETURN) return E_NONE; if (ret < 0) return ret; switch (option) { case 0: suboption = 0; while (true) { ret = generic(setupCharacterOptions, 5, suboption); if (ret == E_QUIT) return E_QUIT; if (ret < 0) break; switch (suboption) { case 0: // Character name if (textInput("character name:", setup.characterName) == E_QUIT) return E_QUIT; break; default: // Character colour subsuboption = 0; ret = generic(setupCharacterColOptions, 8, subsuboption); if (ret == E_QUIT) return E_QUIT; if (ret == E_NONE) setup.characterCols[suboption - 1] = setupCharacterCols[subsuboption]; break; } } break; case 1: #if !(defined(CAANOO) || defined(WIZ) || defined(GP2X) || defined(PSP) || defined(_3DS) || defined(__vita__)) if (setupKeyboard() == E_QUIT) return E_QUIT; #else if (message("FEATURE NOT AVAILABLE") == E_QUIT) return E_QUIT; #endif break; case 2: #if !defined(DINGOO) && !defined(PSP) && !defined(__vita__) // FIXME: psp, psvita if (setupJoystick() == E_QUIT) return E_QUIT; #else if (message("FEATURE NOT AVAILABLE") == E_QUIT) return E_QUIT; #endif break; case 3: #ifndef NO_RESIZE if (setupResolution() == E_QUIT) return E_QUIT; #else if (message("FEATURE NOT AVAILABLE") == E_QUIT) return E_QUIT; #endif break; case 4: #ifdef SCALE if (setupScaling() == E_QUIT) return E_QUIT; #else if (message("FEATURE NOT AVAILABLE") == E_QUIT) return E_QUIT; #endif break; case 5: if (setupSound() == E_QUIT) return E_QUIT; break; case 6: suboption = 0; while (true) { ret = generic(setupMods, 4, suboption); if (ret == E_QUIT) return E_QUIT; if (ret < 0) break; if (setupMods[suboption] == setupModsOff[suboption]) setupMods[suboption] = setupModsOn[suboption]; else setupMods[suboption] = setupModsOff[suboption]; setup.slowMotion = (setupMods[0] == setupModsOn[0]); setup.leaveUnneeded = (setupMods[1] == setupModsOn[1]); setup.manyBirds = (setupMods[2] == setupModsOn[2]); setup.scale2x = (setupMods[3] == setupModsOn[3]); } break; } } return E_NONE; } openjazz-20240919/src/platforms/000077500000000000000000000000001467303304200164265ustar00rootroot00000000000000openjazz-20240919/src/platforms/3ds.cpp000066400000000000000000000042771467303304200176350ustar00rootroot00000000000000 /** * * @file 3ds.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "3ds.h" #ifdef _3DS #include <3ds.h> #include "util.h" #include "io/file.h" void N3DS_Init() { // file system romfsInit(); } void N3DS_Exit() { romfsExit(); } void N3DS_AddGamePaths() { gamePaths.add(createString("sdmc:/3ds/OpenJazz/"), PATH_TYPE_GAME|PATH_TYPE_CONFIG); gamePaths.add(createString("romfs:/"), PATH_TYPE_SYSTEM|PATH_TYPE_GAME); } int N3DS_InputIP(char*& current_ip, char*& new_ip) { SwkbdState swkbd; char ip_buf[16]; SwkbdButton button = SWKBD_BUTTON_NONE; swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 2, 15); swkbdSetInitialText(&swkbd, current_ip); swkbdSetHintText(&swkbd, "Please enter an IP address"); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, 0); swkbdSetFeatures(&swkbd, SWKBD_FIXED_WIDTH); swkbdSetNumpadKeys(&swkbd, '.', 0); button = swkbdInputText(&swkbd, ip_buf, sizeof(ip_buf)); if (button == SWKBD_BUTTON_CONFIRM) { new_ip = createString(ip_buf); return 1; } return 0; } static SwkbdCallbackResult StringFilter(void*, const char** ppMessage, const char* text, size_t textlen) { for (size_t i = 0; i < textlen; i++) { if ((text[i] != ' ') && (text[i] != '.') && ((text[i] < '0') || (text[i] > '9')) && ((text[i] < 'a') || (text[i] > 'z'))) { *ppMessage = "Invalid character(s) found.\nOnly a-z, 0-9, Dot and Space\nare allowed."; return SWKBD_CALLBACK_CONTINUE; } } return SWKBD_CALLBACK_OK; } int N3DS_InputString(const char* hint, char*& current_string, char*& new_string) { SwkbdState swkbd; char string_buf[STRING_LENGTH + 1]; SwkbdButton button = SWKBD_BUTTON_NONE; swkbdInit(&swkbd, SWKBD_TYPE_QWERTY, 2, STRING_LENGTH); swkbdSetInitialText(&swkbd, current_string); swkbdSetHintText(&swkbd, hint); swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, 0, 0); swkbdSetFilterCallback(&swkbd, StringFilter, NULL); button = swkbdInputText(&swkbd, string_buf, sizeof(string_buf)); if (button == SWKBD_BUTTON_CONFIRM) { new_string = createString(string_buf); return 1; } return 0; } #endif openjazz-20240919/src/platforms/3ds.h000066400000000000000000000007431467303304200172740ustar00rootroot00000000000000 /** * * @file 3ds.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _3DS_H #define _3DS_H #ifdef _3DS void N3DS_Init(); void N3DS_Exit(); void N3DS_AddGamePaths(); int N3DS_InputIP(char*& current_ip, char*& new_ip); int N3DS_InputString(const char* hint, char*& current_string, char*& new_string); #endif #endif openjazz-20240919/src/platforms/haiku.cpp000066400000000000000000000031241467303304200202330ustar00rootroot00000000000000 /** * * @file haiku.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "haiku.h" #ifdef __HAIKU__ #include #include #include #include #include "util.h" #include "io/file.h" void HAIKU_AddGamePaths() { char dir_buffer[10 + B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH]; dev_t volume = dev_for_path("/boot"); find_directory(B_USER_NONPACKAGED_DATA_DIRECTORY, volume, false, dir_buffer, sizeof(dir_buffer)); strncat(dir_buffer, "/openjazz/", sizeof(dir_buffer) - 1); gamePaths.add(createString(dir_buffer), PATH_TYPE_GAME|PATH_TYPE_CONFIG); find_directory(B_SYSTEM_DATA_DIRECTORY, volume, false, dir_buffer, sizeof(dir_buffer)); strncat(dir_buffer, "/openjazz/", sizeof(dir_buffer) - 1); gamePaths.add(createString(dir_buffer), PATH_TYPE_SYSTEM|PATH_TYPE_GAME); } void HAIKU_ErrorNoDatafiles() { char dir_buffer[10 + B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH]; find_directory(B_USER_NONPACKAGED_DATA_DIRECTORY, dev_for_path("/boot"), false, dir_buffer, sizeof(dir_buffer)); strncat(dir_buffer, "/openjazz/", sizeof(dir_buffer) - 1); char alertBuffer[100 + B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH]; strcpy(alertBuffer, "Unable to find game data files!\n" "Put the data into the folder:\n"); strncat(alertBuffer, dir_buffer, sizeof(alertBuffer)); BAlert* alert = new BAlert("OpenJazz", alertBuffer, "Exit", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->Go(); } #endif openjazz-20240919/src/platforms/haiku.h000066400000000000000000000005431467303304200177020ustar00rootroot00000000000000 /** * * @file haiku.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _HAIKU_H #define _HAIKU_H #ifdef __HAIKU__ void HAIKU_AddGamePaths(); void HAIKU_ErrorNoDatafiles(); #endif #endif openjazz-20240919/src/platforms/platforms.h000066400000000000000000000026431467303304200206130ustar00rootroot00000000000000 /** * * @file platforms.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _PLATFORMS_H #define _PLATFORMS_H #include "psp.h" #include "3ds.h" #include "wii.h" #include "psvita.h" #include "haiku.h" #include "wiz.h" #include "riscos.h" #include "symbian.h" #include "xdg.h" inline void PLATFORM_Init() { #ifdef PSP PSP_Init(); #endif #ifdef __wii__ WII_Init(); #endif #ifdef _3DS N3DS_Init(); #endif #ifdef __vita__ PSVITA_Init(); #endif } inline void PLATFORM_Exit() { #ifdef _3DS N3DS_Exit(); #endif } inline void PLATFORM_AddGamePaths() { #ifdef __HAIKU__ HAIKU_AddGamePaths(); #endif #ifdef __SYMBIAN32__ SYMBIAN_AddGamePaths(); #endif #ifdef _3DS N3DS_AddGamePaths(); #endif #ifdef __wii__ WII_AddGamePaths(); #endif #ifdef __vita__ PSVITA_AddGamePaths(); #endif #ifdef __riscos__ RISCOS_AddGamePaths(); #endif #ifdef __SYMBIAN32__ SYMBIAN_AddGamePaths(); #endif // using __unix__ might add too much #if (__linux__ && !__ANDROID__) || __FreeBSD__ || __OpenBSD__ #ifndef PORTABLE // Only use XDG dirs for installed package XDG_AddGamePaths(); #endif #endif } inline void PLATFORM_ErrorNoDatafiles() { #ifdef __HAIKU__ HAIKU_ErrorNoDatafiles(); #endif #ifdef PSP PSP_ErrorNoDatafiles(); #endif //FIXME: add SDL2 error message box } #endif openjazz-20240919/src/platforms/psp.cpp000066400000000000000000000033011467303304200177310ustar00rootroot00000000000000 /** * * @file psp.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "psp.h" #ifdef PSP #include #include #include #include #include #include #include #include PSP_MODULE_INFO("OpenJazz", PSP_MODULE_USER, 0, 1); //PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER|PSP_THREAD_ATTR_VFPU); // vfpu is used in SDL afaik PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER); PSP_HEAP_SIZE_KB(-2048); static int exitRequest = 0; static int exitCallback(int arg1, int arg2, void *common) { exitRequest = 1; return 0; } static int callbackThread(SceSize args, void *argp) { int callbackID; callbackID = sceKernelCreateCallback("Exit Callback", exitCallback, NULL); sceKernelRegisterExitCallback(callbackID); sceKernelSleepThreadCB(); return 0; } int PSP_WantsExit() { return exitRequest; } void PSP_Init() { // debug output pspDebugScreenInit(); //pspDebugInstallErrorHandler(NULL); int threadID = sceKernelCreateThread("Callback Update Thread", callbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0); if(threadID >= 0) { sceKernelStartThread(threadID, 0, 0); } atexit(sceKernelExitGame); sceIoChdir("ms0:/PSP/GAME/OpenJazz"); } void PSP_ErrorNoDatafiles() { pspDebugScreenClear(); pspDebugScreenSetXY(12, 0); pspDebugScreenPuts("Unable to find game data files."); pspDebugScreenPuts("Please put them in the same directory as this executable."); pspDebugScreenPuts("\te.g. ms0:/PSP/GAME/OpenJazz"); sceKernelDelayThread(3000000); } #endif openjazz-20240919/src/platforms/psp.h000066400000000000000000000005411467303304200174010ustar00rootroot00000000000000 /** * * @file psp.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _PSP_H #define _PSP_H #ifdef PSP int PSP_WantsExit(); void PSP_Init(); void PSP_ErrorNoDatafiles(); #endif #endif openjazz-20240919/src/platforms/psvita.cpp000066400000000000000000000016151467303304200204430ustar00rootroot00000000000000 /** * * @file psvita.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "psvita.h" #include "util.h" #ifdef __vita__ #include #include #include #include "util.h" #include "io/file.h" void PSVITA_Init() { // controls sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG_WIDE); sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); } void PSVITA_AddGamePaths() { gamePaths.add(createString("ux0:data/jazz/"), PATH_TYPE_CONFIG); } int PSVITA_InputString(const char* hint, char*& current_string, char*& new_string) { char* text = kbdvita_get(hint, current_string, STRING_LENGTH); if (text != NULL) { new_string = createString(text); return 1; } return 0; } #endif openjazz-20240919/src/platforms/psvita.h000066400000000000000000000006621467303304200201110ustar00rootroot00000000000000 /** * * @file psvita.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _PSPVITA_H #define _PSPVITA_H #ifdef __vita__ void PSVITA_Init(); void PSVITA_AddGamePaths(); int PSVITA_InputString(const char* hint, char*& current_string, char*& new_string); #endif #endif openjazz-20240919/src/platforms/riscos.cpp000066400000000000000000000006521467303304200204370ustar00rootroot00000000000000 /** * * @file riscos.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "riscos.h" #ifdef __riscos__ #include "util.h" #include "io/file.h" void RISCOS_AddGamePaths() { gamePaths.add(createString("//OpenJazz/"), PATH_TYPE_CONFIG); } #endif openjazz-20240919/src/platforms/riscos.h000066400000000000000000000005101467303304200200750ustar00rootroot00000000000000 /** * * @file riscos.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _RISCOS_H #define _RISCOS_H #ifdef __riscos__ void RISCOS_AddGamePaths(); #endif #endif openjazz-20240919/src/platforms/symbian.cpp000066400000000000000000000063261467303304200206030ustar00rootroot00000000000000 /** * * @file symbian.cpp * * Part of the OpenJazz project * * @section Licence * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifdef __SYMBIAN32__ #include #include #include #include #include #include #include #include #include #include "util.h" #include "io/file.h" char KOpenJazzPath[256]; FILE* mystdout = NULL; FILE *mystderr = NULL; /// Symbian app class COpenJazzApp: public CSDLApp { public: COpenJazzApp(); ~COpenJazzApp(); #ifdef UIQ3 /** * Returns the resource id to be used to declare the views supported by this UIQ3 app * @return TInt, resource id */ TInt ViewResourceId(); #endif /** * This has a default empty implementation. * Is called just before SDL_Main is called to allow init of system vars */ virtual void PreInitializeAppL(); TUid AppDllUid() const; }; CApaApplication* NewApplication() { // Return pointer to newly created CQMApp return new COpenJazzApp; } #include // E32Main() contains the program's start up code, the entry point for an EXE. GLDEF_C TInt E32Main() { return EikStart::RunApplication(NewApplication); } COpenJazzApp::COpenJazzApp() { } COpenJazzApp::~COpenJazzApp() { fflush(mystdout); fflush(mystderr); fflush(stdout); fflush(stderr); fclose(stdout); fclose(stderr); } #ifdef UIQ3 #include /** * Returns the resource id to be used to declare the views supported by this UIQ3 app * @return TInt, resource id */ TInt COpenJazzApp::ViewResourceId() { return R_SDL_VIEW_UI_CONFIGURATIONS; } #endif /** * This has a default empty implementation. * Is called just before SDL_Main is called to allow init of system vars */ void COpenJazzApp::PreInitializeAppL() { TFileName filename; TPtr8 ptr((unsigned char*) KOpenJazzPath, 0, 255); #ifdef UIQ3 mystdout = fopen("c:\\shared\\openjazz\\stdout.txt","w+"); mystderr = fopen("c:\\shared\\openjazz\\stderr.txt","w+"); #else mystdout = fopen("c:\\data\\openjazz\\stdout.txt","w+"); mystderr = fopen("c:\\data\\openjazz\\stderr.txt","w+"); #endif *stderr = *mystdout; *stdout = *mystderr; filename = _L("C:\\openjazz\\"); for(TInt i = 'D';i<'Z';i++) { filename[0] = i; if(BaflUtils::PathExists(CEikonEnv::Static()->FsSession(), filename)) { ptr.Copy(filename); ptr.ZeroTerminate(); return; } } #ifdef UIQ3 ptr.Copy(_L8("c:\\shared\\openjazz\\")); #else ptr.Copy(_L8("c:\\data\\openjazz\\")); #endif ptr.ZeroTerminate(); } /** * Responsible for returning the unique UID of this application * @return unique UID for this application in a TUid **/ TUid COpenJazzApp::AppDllUid() const { return TUid::Uid(0xA000A005); } float sinf(float value) { TReal ret; Math::Sin(ret, value); return ret; } ///////////////////////////////////////////////////////////////////////////////////////////////// void SYMBIAN_AddGamePaths() { #ifdef UIQ3 gamePaths.add(createString("c:\\shared\\openjazz\\"), PATH_TYPE_GAME|PATH_TYPE_CONFIG); #else gamePaths.add(createString("c:\\data\\openjazz\\"), PATH_TYPE_GAME|PATH_TYPE_CONFIG); #endif gamePaths.add(createString(KOpenJazzPath), PATH_TYPE_SYSTEM|PATH_TYPE_GAME); } #endif openjazz-20240919/src/platforms/symbian.h000066400000000000000000000005361467303304200202450ustar00rootroot00000000000000 /** * * @file symbian.h * * Part of the OpenJazz project * * @par Licence: * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _SYMBIAN_H #define _SYMBIAN_H #ifdef __SYMBIAN32__ extern char KOpenJazzPath[256]; extern float sinf (float); void SYMBIAN_AddGamePaths(); #endif #endif openjazz-20240919/src/platforms/wii.cpp000066400000000000000000000014101467303304200177160ustar00rootroot00000000000000 /** * * @file wii.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "wii.h" #ifdef __wii__ #include #include #include "util.h" #include "io/file.h" void WII_Init() { // enable file system fatInitDefault(); // USBGecko Debugging constexpr int SLOT = CARD_SLOTB; if (usb_isgeckoalive(SLOT)) { usb_flush(SLOT); CON_EnableGecko(SLOT, false); } #ifndef NDEBUG // Dolphin UART Debugging else if(!__system_argv->argc) { SYS_STDIO_Report(true); } #endif } void WII_AddGamePaths() { gamePaths.add(createString("sd:/apps/OpenJazz/"), PATH_TYPE_GAME|PATH_TYPE_CONFIG); } #endif openjazz-20240919/src/platforms/wii.h000066400000000000000000000005131467303304200173660ustar00rootroot00000000000000 /** * * @file wii.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _WII_H #define _WII_H #ifdef __wii__ void WII_Init(); void WII_AddGamePaths(); #endif #endif openjazz-20240919/src/platforms/wiz.cpp000066400000000000000000000020401467303304200177370ustar00rootroot00000000000000 /** * * @file wiz.cpp * * Part of the OpenJazz project * * @par Licence: * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "wiz.h" #if defined(WIZ) || defined(GP2X) #include #include #include #include #include #include "io/sound.h" void WIZ_AdjustVolume(int direction) { if(direction != VOLUME_NOCHG) { if(volume <= 10) { if(direction == VOLUME_UP) volume += VOLUME_CHANGE_RATE/2; if(direction == VOLUME_DOWN) volume -= VOLUME_CHANGE_RATE/2; } else { if(direction == VOLUME_UP) volume += VOLUME_CHANGE_RATE; if(direction == VOLUME_DOWN) volume -= VOLUME_CHANGE_RATE; } if(volume < VOLUME_MIN) volume = VOLUME_MIN; if(volume > VOLUME_MAX) volume = VOLUME_MAX; printf( "Volume Change: %i\n", volume ); } unsigned long soundDev = open("/dev/mixer", O_RDWR); if(soundDev) { int vol = ((volume << 8) | volume); ioctl(soundDev, SOUND_MIXER_WRITE_PCM, &vol); close(soundDev); } } #endif openjazz-20240919/src/platforms/wiz.h000066400000000000000000000010121467303304200174020ustar00rootroot00000000000000 /** * * @file wiz.h * * Part of the OpenJazz project * * @par Licence: * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _WIZ_H #define _WIZ_H #if defined(WIZ) || defined(GP2X) #define GP2X_BUTTON_VOLUP (16) #define GP2X_BUTTON_VOLDOWN (17) #define VOLUME_MIN 0 #define VOLUME_MAX 100 #define VOLUME_CHANGE_RATE 2 #define VOLUME_NOCHG 0 #define VOLUME_DOWN 1 #define VOLUME_UP 2 void WIZ_AdjustVolume(int direction); #endif #endif openjazz-20240919/src/platforms/xdg.cpp000066400000000000000000000031621467303304200177160ustar00rootroot00000000000000 /** * * @file xdg.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #include "xdg.h" #if __unix__ #include #include "util.h" #include "io/file.h" #include "io/log.h" static struct { const char *env; const char *relPath; int type; bool create; } xdgSpec[] = { {"XDG_CONFIG_HOME", "/.config", PATH_TYPE_CONFIG, true}, {"XDG_CACHE_HOME", "/.cache", PATH_TYPE_TEMP, true}, {"XDG_DATA_HOME", "/.local/share", PATH_TYPE_GAME, false} }; void XDG_AddGamePaths() { // first get home directory, will be useful later and serves as sanity check const char *homeDir = getenv("HOME"); if (!homeDir) { LOG_WARN("No $HOME found, this might break something."); return; } // define our subdirectory const char *appDir = "/openjazz"; // check all needed XDG directories from spec for(auto const &entry : xdgSpec) { // length of final path size_t xdgLen = 0; // check for user override const char *env = getenv(entry.env); if (env) { xdgLen = strlen(env); } else { // use fallback directory xdgLen = strlen(homeDir) + strlen(entry.relPath); } // concatenate path char *xdgPath = new char[xdgLen + strlen(appDir) + 1]; if(env) { strcpy(xdgPath, env); } else { strcpy(xdgPath, homeDir); strcat(xdgPath, entry.relPath); } strcat(xdgPath, appDir); // create if needed if(entry.create) { LOG_TRACE("Trying to create '%s' directory", xdgPath); mkdir(xdgPath, 0777); } gamePaths.add(xdgPath, entry.type); } } #endif openjazz-20240919/src/platforms/xdg.h000066400000000000000000000004671467303304200173700ustar00rootroot00000000000000 /** * * @file xdg.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _XDG_H #define _XDG_H #if __unix__ void XDG_AddGamePaths(); #endif #endif openjazz-20240919/src/player/000077500000000000000000000000001467303304200157135ustar00rootroot00000000000000openjazz-20240919/src/player/player.cpp000066400000000000000000000216721467303304200177230ustar00rootroot00000000000000 /** * * @file player.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created level.c * - 1st January 2006: Created events.c from parts of level.c * - 3rd February 2009: Renamed events.c to events.cpp and level.c to level.cpp, * created player.cpp * - 5th February 2009: Added parts of events.cpp and level.cpp to player.cpp * - 19th March 2009: Created sprite.cpp from parts of event.cpp and player.cpp * - 18th July 2009: Created playerframe.cpp from parts of player.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the creation and destruction of players, and their interactions * with other objects. * */ #include "jj1/bonuslevel/jj1bonuslevelplayer.h" #include "jj1/level/jj1levelplayer.h" #ifdef ENABLE_JJ2 #include "jj2/level/jj2levelplayer.h" #endif #include "level/levelplayer.h" #include "game/game.h" #include "io/controls.h" #include "util.h" #include #include /** * Create the player. */ Player::Player () { levelPlayer = NULL; levelPlayerType = LT_JJ1; name = NULL; } /** * Delete the player. */ Player::~Player () { deinit(); } /** * Initialise player data. * * Deinitialise any existing player data, assign properties and initial values. * * @param owner The current game * @param playerName Name (displayed in multiplayer games) * @param playerCols Colours (only used in multiplayer games) * @param newTeam Team (in multiplayer games) */ void Player::init (Game* owner, char *playerName, unsigned char *playerCols, unsigned char newTeam) { // Clear existing player deinit(); // Assign owner game = owner; // Assign name name = createString(playerName); // Assign initial values score = 0; lives = 3; ammoType = -1; ammo[0] = 0; ammo[1] = 0; ammo[2] = 0; ammo[3] = 0; ammo[4] = 0; fireSpeed = 0; flockSize = 0; team = newTeam; teamScore = 0; if (playerCols) { memcpy(cols, playerCols, PCOLOURS); } else { cols[0] = CHAR_FUR; cols[1] = CHAR_BAND; cols[2] = CHAR_GUN; cols[3] = CHAR_WBAND; } } /** * Deinitialise player data. */ void Player::deinit () { if (levelPlayer) delete levelPlayer; levelPlayer = NULL; if (name) delete[] name; name = NULL; } /** * Discard all the player's ammo. */ void Player::clearAmmo () { int type; for (type = 0; type < 5; type++) ammo[type] = 0; ammoType = -1; fireSpeed = 0; } /** * Reset the player's current level player. * * @param x The level player's new grid x-coordinate * @param y The level player's new grid y-coordinate */ void Player::reset (int x, int y) { levelPlayer->reset(x, y); } /** * Create a new level player for the player (and delete any existing one). * * @param levelType The type of level for which to create a level player * @param anims New level player animations * @param flippedAnims New level player flipped animations * @param x The level player's new grid x-coordinate * @param y The level player's new grid y-coordinate */ void Player::createLevelPlayer (LevelType levelType, Anim** anims, Anim** flippedAnims, unsigned char x, unsigned char y) { #ifndef ENABLE_JJ2 (void)flippedAnims; #endif if (levelPlayer) { flockSize = levelPlayer->countBirds(); delete levelPlayer; } levelPlayerType = levelType; switch (levelType) { case LT_JJ1: levelPlayer = new JJ1LevelPlayer(this, anims, x, y, flockSize); break; case LT_JJ1BONUS: levelPlayer = new JJ1BonusLevelPlayer(this, anims, x, y, flockSize); break; #ifdef ENABLE_JJ2 case LT_JJ2: levelPlayer = new JJ2LevelPlayer(this, anims, flippedAnims, x, y, flockSize); break; #endif } for (int i = 0; i < PCONTROLS; i++) pcontrols[i] = false; } /** * Get the player's level player. * * @return The level player */ LevelPlayer* Player::getLevelPlayer () { return levelPlayer; } /** * Get the player's colours. * * @return The player's colours */ unsigned char * Player::getCols () { return cols; } /** * Get the player's name. * * @return The player's name */ char * Player::getName () { return name; } /** * Get the player's JJ1 bonus level player. * * @return The JJ1 bonus level player */ JJ1BonusLevelPlayer* Player::getJJ1BonusLevelPlayer () { assert(levelPlayerType == LT_JJ1BONUS); return static_cast(levelPlayer); } /** * Get the player's JJ1 level player. * * @return The JJ1 level player */ JJ1LevelPlayer* Player::getJJ1LevelPlayer () { assert(levelPlayerType == LT_JJ1); return static_cast(levelPlayer); } #ifdef ENABLE_JJ2 /** * Get the player's JJ2 level player. * * @return The JJ2 level player */ JJ2LevelPlayer* Player::getJJ2LevelPlayer () { assert(levelPlayerType == LT_JJ2); return static_cast(levelPlayer); } #endif /** * Set the state of the specified control. * * @param control Affected control * @param state New state */ void Player::setControl (int control, bool state) { pcontrols[control] = state; } /** * Get the state of the specified control. * * @param control The control * * @return State */ bool Player::getControl (int control) { return pcontrols[control]; } /** * Add to the player's total score. * * @param addedScore The amount to be added */ void Player::addScore (int addedScore) { score += addedScore; } /** * Get the player's score. */ int Player::getScore () { return score; } /** * Add an extra life to the player. */ void Player::addLife () { if (lives < 99) lives++; } /** * Get the number of extra lives. * * @return Number of extra lives */ int Player::getLives () { return lives; } /** * Add ammo to the player's arsenal. * * @param type Type of ammo * @param amount Amount of ammo to add */ void Player::addAmmo (int type, int amount) { if (!ammo[type]) ammoType = type; ammo[type] += amount; } /** * Get the current type of ammo. * * @return The type of ammo */ int Player::getAmmoType () { return ammoType; } /** * Get the total amount of ammo of the current type. * * @return The amount of ammo */ int Player::getAmmo () { return ammo[ammoType]; } /** * Get the player's team * * @return Team number */ unsigned char Player::getTeam () { return team; } /** * Deal with bullet collisions. * * @param source Player that fired the bullet (NULL if an event) * * @return Whether or not the hit was successful */ bool Player::hit (Player *source) { return game->getMode()->hit(source, this); } /** * Kill the player. * * @param source Player responsible for the kill (NULL if due to an event or time) * * @return Whether or not the kill was successful */ bool Player::kill (Player *source) { return game->getMode()->kill(game, source, this); } /** * Set the checkpoint * * @param gridX X-coordinate (in tiles) of the checkpoint * @param gridY Y-coordinate (in tiles) of the checkpoint */ void Player::setCheckpoint (int gridX, int gridY) { game->setCheckpoint(gridX, gridY); } /** * Outcome of level being completed * * @param gridX X-coordinate (in tiles) of finishing position * @param gridY Y-coordinate (in tiles) of finishing position * * @return Whether or not the level should end */ bool Player::endOfLevel (int gridX, int gridY) { return game->getMode()->endOfLevel(game, this, gridX, gridY); } /** * Copy data to be sent to clients/server */ void Player::send (unsigned char *buffer) { buffer[3] = pcontrols[C_UP]; buffer[4] = pcontrols[C_DOWN]; buffer[5] = pcontrols[C_LEFT]; buffer[6] = pcontrols[C_RIGHT]; buffer[7] = pcontrols[C_JUMP]; buffer[8] = pcontrols[C_FIRE]; buffer[10] = ammo[0] >> 8; buffer[11] = ammo[0] & 255; buffer[12] = ammo[1] >> 8; buffer[13] = ammo[1] & 255; buffer[14] = ammo[2] >> 8; buffer[15] = ammo[2] & 255; buffer[16] = ammo[3] >> 8; buffer[17] = ammo[3] & 255; buffer[18] = ammoType + 1; buffer[19] = score >> 24; buffer[20] = (score >> 16) & 255; buffer[21] = (score >> 8) & 255; buffer[22] = score & 255; buffer[24] = lives; buffer[28] = fireSpeed; buffer[45] = pcontrols[C_SWIM]; if (levelPlayer) levelPlayer->send(buffer); } /** * Interpret data received from client/server */ void Player::receive (unsigned char *buffer) { if (buffer[1] == MT_P_TEMP) { pcontrols[C_UP] = buffer[3]; pcontrols[C_DOWN] = buffer[4]; pcontrols[C_LEFT] = buffer[5]; pcontrols[C_RIGHT] = buffer[6]; pcontrols[C_JUMP] = buffer[7]; pcontrols[C_SWIM] = buffer[45]; pcontrols[C_FIRE] = buffer[8]; pcontrols[C_CHANGE] = false; flockSize = buffer[9]; ammo[0] = (buffer[10] << 8) + buffer[11]; ammo[1] = (buffer[12] << 8) + buffer[13]; ammo[2] = (buffer[14] << 8) + buffer[15]; ammo[3] = (buffer[16] << 8) + buffer[17]; ammoType = buffer[18] - 1; score = (buffer[19] << 24) + (buffer[20] << 16) + (buffer[21] << 8) + buffer[22]; lives = buffer[24]; fireSpeed = buffer[28]; } if (levelPlayer) levelPlayer->receive(buffer); } openjazz-20240919/src/player/player.h000066400000000000000000000075531467303304200173720ustar00rootroot00000000000000 /** * * @file player.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 31st January 2006: Created player.h from parts of OpenJazz.h * - 24th June 2010: Created levelplayer.h from parts of player.h * - 24th June 2010: Created bonusplayer.h from parts of player.h * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ /* "Tile" is a flexible term. Here it is used to refer specifically to the individual elements of the tile set. "Tiles" in the context of level units are referred to as grid elements. */ #ifndef _PLAYER_H #define _PLAYER_H #include "level/level.h" // Constants // Player defaults #define CHAR_NAME "jazz" #define CHAR_FUR 4 #define CHAR_BAND 3 #define CHAR_GUN 2 #define CHAR_WBAND 8 // General #define PCONTROLS 8 /* Number of player controls. */ // Number of configurable player colour ranges #define PCOLOURS 4 // Enums /// Player colours enum PlayerColour { PC_GREY = 0, PC_SGREEN = 1, PC_BLUE = 2, PC_RED = 3, PC_LGREEN = 4, PC_LEVEL1 = 5, PC_YELLOW = 6, PC_LEVEL2 = 7, PC_ORANGE = 8, PC_LEVEL3 = 9, PC_LEVEL4 = 10, PC_SANIM = 11, PC_LANIM = 12, PC_LEVEL5 = 13 }; // Classes class Anim; class JJ1LevelPlayer; class JJ1BonusLevelPlayer; #ifdef ENABLE_JJ2 class JJ2LevelPlayer; #endif class LevelPlayer; /// Game player class Player { private: Game* game; LevelPlayer* levelPlayer; ///< Level player base class LevelType levelPlayerType; char* name; ///< Name bool pcontrols[PCONTROLS]; ///< Control status unsigned char cols[PCOLOURS]; ///< Character colours int ammo[5]; ///< Amount of ammo int ammoType; ///< Ammo type. -1 = blaster, 0 = toaster, 1 = missiles, 2 = bouncer, 3 = unknown, 4 = TNT int score; ///< Total score int lives; ///< Remaining lives int fireSpeed; ///< Rapid-fire rate int flockSize; ///< Number of accompanying birds unsigned char team; ///< Team ID void addAmmo (int type, int amount); public: int teamScore; ///< Team's total score Player (); ~Player (); void init (Game* owner, char* playerName, unsigned char* cols, unsigned char newTeam); void deinit (); void clearAmmo (); void reset (int x, int y); void createLevelPlayer (LevelType levelType, Anim** anims, Anim** flippedAnims, unsigned char x, unsigned char y); LevelPlayer* getLevelPlayer (); JJ1BonusLevelPlayer* getJJ1BonusLevelPlayer (); JJ1LevelPlayer* getJJ1LevelPlayer (); #ifdef ENABLE_JJ2 JJ2LevelPlayer* getJJ2LevelPlayer (); #endif void addLife (); void addScore (int addedScore); bool endOfLevel (int gridX, int gridY); int getAmmoType (); int getAmmo (); unsigned char* getCols (); bool getControl (int control); int getLives (); char* getName (); int getScore (); unsigned char getTeam (); bool hit (Player* source); bool kill (Player* source); void setCheckpoint (int gridX, int gridY); void setControl (int control, bool state); void send (unsigned char* buffer); void receive (unsigned char* buffer); friend class JJ1LevelPlayer; #ifdef ENABLE_JJ2 friend class JJ2LevelPlayer; #endif }; // Variables EXTERN Player* players; EXTERN Player* localPlayer; EXTERN int nPlayers; #endif openjazz-20240919/src/setup.cpp000066400000000000000000000122301467303304200162610ustar00rootroot00000000000000 /** * * @file setup.cpp * * Part of the OpenJazz project * * @par History: * - 23rd of August 2005: Created main.c and menu.c * - 3rd of February 2009: Renamed main.c to main.cpp and menu.c to menu.cpp * - 18th July 2009: Created menusetup.cpp from parts of menu.cpp * - 26th July 2009: Renamed menusetup.cpp to setupmenu.cpp * - 21st July 2013: Created setup.cpp from parts of main.cpp and setupmenu.cpp * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Deals with the running of setup menus. * */ #include "io/controls.h" #include "io/file.h" #include "io/gfx/video.h" #include "io/sound.h" #include "player/player.h" #include "setup.h" #include "util.h" #include "io/log.h" #define CONFIG_FILE "openjazz.cfg" /** * Create default setup */ Setup::Setup () { // Create the player's name characterName = createEditableString(CHAR_NAME); // Assign the player's colour characterCols[0] = CHAR_FUR; characterCols[1] = CHAR_BAND; characterCols[2] = CHAR_GUN; characterCols[3] = CHAR_WBAND; // defaults scale2x = true; manyBirds = false; leaveUnneeded = true; slowMotion = false; } /** * Delete the setup data */ Setup::~Setup () { delete[] characterName; } /** * Load settings from config file. */ SetupOptions Setup::load () { File* file; SetupOptions cfg = { false, 0, 0, false, 0 }; #ifdef FULLSCREEN_ONLY cfg.fullScreen = true; #endif // Open config file try { file = new File(CONFIG_FILE, PATH_TYPE_CONFIG, false); } catch (int e) { LOG_INFO("Configuration file not found. Using defaults."); return cfg; } // Check that the config file has the correct version if (file->loadChar() != 6) { LOG_WARN("Valid configuration file not found."); delete file; return cfg; } // Read video settings cfg.videoWidth = file->loadShort(MAX_SCREEN_WIDTH); cfg.videoHeight = file->loadShort(MAX_SCREEN_HEIGHT); int vOpt = file->loadChar(); #ifndef FULLSCREEN_ONLY cfg.fullScreen = vOpt & 1; #endif #ifdef SCALE if (vOpt >= 10) vOpt = 2; cfg.videoScale = vOpt >> 1; #endif (void)vOpt; cfg.valid = true; // Read controls for (int i = 0; i < CONTROLS - 4; i++) controls.setKey(i, file->loadInt()); for (int i = 0; i < CONTROLS; i++) controls.setButton(i, file->loadInt()); for (int i = 0; i < CONTROLS; i++) { int a, d; a = file->loadInt(); d = file->loadInt(); controls.setAxis(i, a, d); } for (int i = 0; i < CONTROLS; i++) { int h, d; h = file->loadInt(); d = file->loadInt(); controls.setHat(i, h, d); } // Read the player's name for (int i = 0; i < STRING_LENGTH; i++) setup.characterName[i] = file->loadChar(); setup.characterName[STRING_LENGTH] = 0; // Read the player's colours setup.characterCols[0] = file->loadChar(); setup.characterCols[1] = file->loadChar(); setup.characterCols[2] = file->loadChar(); setup.characterCols[3] = file->loadChar(); // Read the music and sound effect volume setMusicVolume(file->loadChar()); setSoundVolume(file->loadChar()); // Read gameplay options int opt = file->loadChar(); setup.manyBirds = ((opt & 1) != 0); setup.leaveUnneeded = ((opt & 2) != 0); setup.slowMotion = ((opt & 4) != 0); setup.scale2x = ((opt & 8) == 0); delete file; return cfg; } /** * Save settings to config file. */ void Setup::save () { File *file; int count; int videoScale; // Open config file try { file = new File(CONFIG_FILE, PATH_TYPE_CONFIG, true); } catch (int e) { file = NULL; } // Check that the config file was opened if (!file) { LOG_ERROR("Could not write configuration file: File could not be opened."); return; } // Write the version number file->storeChar(6); // Write video settings file->storeShort(video.getWidth()); file->storeShort(video.getHeight()); #ifdef SCALE videoScale = video.getScaleFactor(); #else videoScale = 1; #endif videoScale <<= 1; #ifndef FULLSCREEN_ONLY videoScale |= video.isFullscreen()? 1: 0; #endif file->storeChar(videoScale); // Write controls for (count = 0; count < CONTROLS - 4; count++) file->storeInt(controls.getKey(count)); for (count = 0; count < CONTROLS; count++) file->storeInt(controls.getButton(count)); for (count = 0; count < CONTROLS; count++) { file->storeInt(controls.getAxis(count)); file->storeInt(controls.getAxisDirection(count)); } for (count = 0; count < CONTROLS; count++) { file->storeInt(controls.getHat(count)); file->storeInt(controls.getHatDirection(count)); } // Write the player's name for (count = 0; count < STRING_LENGTH; count++) file->storeChar(setup.characterName[count]); // Write the player's colour file->storeChar(setup.characterCols[0]); file->storeChar(setup.characterCols[1]); file->storeChar(setup.characterCols[2]); file->storeChar(setup.characterCols[3]); // Write the music and sound effect volume file->storeChar(getMusicVolume()); file->storeChar(getSoundVolume()); // Write gameplay options count = 0; if (setup.manyBirds) count |= 1; if (setup.leaveUnneeded) count |= 2; if (setup.slowMotion) count |= 4; if (!setup.scale2x) count |= 8; file->storeChar(count); delete file; } openjazz-20240919/src/setup.h000066400000000000000000000017441467303304200157360ustar00rootroot00000000000000 /** * * @file setup.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 3rd February 2009: Created menu.h from parts of OpenJazz.h * - 21st July 2013: Created setup.h from parts of menu.h * * @par Licence: * Copyright (c) 2005-2013 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _SETUP_H #define _SETUP_H #include "player/player.h" #include "OpenJazz.h" // Available options in config file struct SetupOptions { bool valid; int videoWidth; int videoHeight; bool fullScreen; int videoScale; }; // Class /// Configuration class Setup { public: char* characterName; unsigned char characterCols[PCOLOURS]; bool manyBirds; bool leaveUnneeded; bool slowMotion; bool scale2x; Setup (); ~Setup (); SetupOptions load (); void save (); }; // Variable EXTERN Setup setup; #endif openjazz-20240919/src/types.h000066400000000000000000000021311467303304200157310ustar00rootroot00000000000000 /** * * @file types.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2005-2017 Alister Thomson * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef OJ_TYPES_H #define OJ_TYPES_H // Constants // Numbers in -10 exponent fixed point #define FE 128 #define FQ 256 #define FH 512 #define F1 1024 #define F2 2048 #define F4 4096 #define F8 8192 #define F10 10240 #define F12 12288 #define F16 16384 #define F20 20480 #define F24 24576 #define F32 32768 #define F36 36864 #define F40 40960 #define F80 81920 #define F64 65536 #define F100 102400 #define F160 163840 #define F192 196608 // Macros // For fixed-point operations #define FTOI(x) ((x) >> 10) ///< Fixed to Int #define ITOF(x) ((x) << 10) ///< Int to Fixed #define MUL(x, y) (((x) * (y)) >> 10) ///< multiplication #define DIV(x, y) (((x) << 10) / (y)) ///< division // Datatypes typedef int fixed; ///< Custom fixed-point data type namespace SE { enum Type : int; ///< Sound Index type } #endif openjazz-20240919/src/util.cpp000066400000000000000000000131631467303304200161040ustar00rootroot00000000000000 /** * * @file util.cpp * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created main.c * - 22nd July 2008: Created util.c from parts of main.c * - 3rd February 2009: Renamed util.c to util.cpp * - 3rd February 2009: Created file.cpp from parts of util.cpp * - 4th February 2009: Created palette.cpp from parts of main.cpp and util.cpp * - 13th July 2009: Created graphics.cpp from parts of util.cpp * * @par Licence: * Copyright (c) 2005-2012 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Contains core utility functions. * */ #include "util.h" #include "io/file.h" #include /** * Check if a file exists. * * @param fileName The file to check * @param pathType Kind of directory * * @return Whether or not the file exists */ bool fileExists (const char * fileName, int pathType) { File *file; #ifdef VERBOSE printf("Check: "); #endif try { file = new File(fileName, pathType); } catch (int e) { return false; } delete file; return true; } /** * Create a short based on the little-endian contents of the first two bytes in * the given memory location. * * @param data Pointer to the memory location * * @return The generated short */ unsigned short int createShort (const unsigned char* data) { unsigned short int val; val = data[0] + (data[1] << 8); return val; } /** * Create an int based on the little-endian contents of the first two bytes in * the given memory location. * * @param data Pointer to the memory location * * @return The generated int */ int createInt (const unsigned char* data) { unsigned int val; val = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); return *((int *)&val); } /** * Create a new string from the contents of an existing string. * * @param string The existing string * * @return The new string */ char * createString (const char *string) { char *cloned; cloned = new char[strlen(string) + 1]; strcpy(cloned, string); return cloned; } /** * Create a new string from the concatenation of two existing strings. * * @param first The existing string to form the start of the new string * @param second The exisitng string to form the end of the new string * * @return The new string */ char * createString (const char *first, const char *second) { char *concatenated; concatenated = new char[strlen(first) + strlen(second) + 1]; strcpy(concatenated, first); strcat(concatenated, second); return concatenated; } /** * Create a new file name string with a 3-digit numerical extension. * * @param type The pre-dot file name * @param extension The number to constitute the extension * * @return The new file name string */ char * createFileName (const char *type, int extension) { char *fileName; int pos; pos = strlen(type); fileName = new char[pos + 5]; strcpy(fileName, type); fileName[pos++] = '.'; fileName[pos++] = '0' + ((extension / 100) % 10); fileName[pos++] = '0' + ((extension / 10) % 10); fileName[pos++] = '0' + (extension % 10); fileName[pos] = 0; return fileName; } /** * Create a new file name string with the given extension. * * @param type The pre-dot file name * @param extension The extension * * @return The new file name string */ char * createFileName (const char *type, const char *extension) { char *fileName; int pos; pos = strlen(type); fileName = new char[pos + strlen(extension) + 2]; strcpy(fileName, type); fileName[pos++] = '.'; strcpy(fileName + pos, extension); return fileName; } /** * Create a new file name string with a 1-digit numerical suffix and a 3-digit * numerical extension. * * @param type The pre-dot file name * @param level The number to constitute the suffix * @param extension The number to constitute the extension * * @return The new file name string */ char * createFileName (const char *type, int level, int extension) { char *fileName; int pos; pos = strlen(type); fileName = new char[pos + 6]; strcpy(fileName, type); fileName[pos++] = '0' + (level % 10); fileName[pos++] = '.'; fileName[pos++] = '0' + ((extension / 100) % 10); fileName[pos++] = '0' + ((extension / 10) % 10); fileName[pos++] = '0' + (extension % 10); fileName[pos] = 0; return fileName; } /** * Create a new variable-length string from the contents of an existing string. * * @param string The existing string * * @return The new string */ char * createEditableString (const char *string) { char *cloned; cloned = new char[STRING_LENGTH + 1]; strcpy(cloned, string); return cloned; } void lowercaseString (char *string) { for (int i = 0; string[i]; i++) { if ((string[i] >= 65) && (string[i] <= 90)) string[i] += 32; } } void uppercaseString (char *string) { for (int i = 0; string[i]; i++) { if ((string[i] >= 97) && (string[i] <= 122)) string[i] -= 32; } } void camelcaseString (char *string) { bool isFirst = true; for (int i = 0; string[i]; i++) { if(isFirst) { if ((string[i] >= 97) && (string[i] <= 122)) string[i] -= 32; isFirst = false; } else { if ((string[i] >= 65) && (string[i] <= 90)) string[i] += 32; } if(string[i] == 32) isFirst = true; } } /** * Get the sine of the given angle * * @param angle The given angle (where 1024 represents a full circle) * * @return The sine of the angle */ fixed fSin (fixed angle) { return sinLut[angle & 1023]; } /** * Get the cosine of the given angle * * @param angle The given angle (where 1024 represetns a full circle) * * @return The cosine of the angle */ fixed fCos (fixed angle) { return sinLut[(angle + 256) & 1023]; } openjazz-20240919/src/util.h000066400000000000000000000027241467303304200155520ustar00rootroot00000000000000 /** * * @file util.h * * Part of the OpenJazz project * * @par History: * - 23rd August 2005: Created OpenJazz.h * - 30th April 2010: Created util.h from parts of OpenJazz.h * * @par Licence: * Copyright (c) 2005-2010 Alister Thomson * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef _UTIL_H #define _UTIL_H #include "OpenJazz.h" #include "io/file.h" // Variable /// Trigonometric function look-up table EXTERN fixed sinLut[1024]; // Functions bool fileExists (const char *fileName, int pathType = PATH_TYPE_ANY); unsigned short int createShort (const unsigned char* data); int createInt (const unsigned char* data); char* createString (const char *string); char* createString (const char *first, const char *second); char* createFileName (const char *type, int extension); char* createFileName (const char *type, const char *extension); char* createFileName (const char *type, int level, int extension); char* createEditableString (const char *string); void lowercaseString (char *string); void uppercaseString (char *string); void camelcaseString (char *string); fixed fSin (fixed angle); fixed fCos (fixed angle); #endif openjazz-20240919/src/version.cpp000066400000000000000000000012021467303304200166030ustar00rootroot00000000000000 /** * * @file version.cpp * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * * @par Description: * Contains version information. * */ #include "version.h" // Fallback version data, should be defined by the build system #ifndef OJ_DATE #define OJ_DATE __DATE__ #endif #ifndef OJ_VERSION #define OJ_VERSION "git" #endif const char *oj_date = OJ_DATE; #ifdef OJ_VERSION_GIT const char *oj_version = OJ_VERSION " (" OJ_VERSION_GIT ")"; #else const char *oj_version = OJ_VERSION; #endif openjazz-20240919/src/version.h000066400000000000000000000005521467303304200162570ustar00rootroot00000000000000 /** * * @file version.h * * Part of the OpenJazz project * * @par Licence: * Copyright (c) 2015-2023 Carsten Teibes * * OpenJazz is distributed under the terms of * the GNU General Public License, version 2.0 * */ #ifndef OJ_VERSION_H #define OJ_VERSION_H #include "OpenJazz.h" extern const char *oj_version; extern const char *oj_date; #endif