diff --git a/.github/scripts/buildRelease.sh b/.github/scripts/buildRelease.sh new file mode 100755 index 0000000..33faf8c --- /dev/null +++ b/.github/scripts/buildRelease.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +ROOT_DIR="$(pwd)" +PROJECT_DIR="$ROOT_DIR/cart-tool" +OPENBIOS_DIR="$ROOT_DIR/nugget/openbios" +TOOLCHAIN_DIR="$ROOT_DIR/gcc-mipsel-none-elf" + +## Build project + +cmake --preset release -DTOOLCHAIN_PATH="$TOOLCHAIN_DIR" "$PROJECT_DIR" \ + || exit 1 +cmake --build "$PROJECT_DIR/build" \ + || exit 1 + +RELEASE_NAME="$( + ls "$PROJECT_DIR/build" | + grep -E -o '^cart-tool-[0-9]+\.[0-9]+\.[0-9]+' | + head -n 1 +)" + +mkdir -p "$ROOT_DIR/$RELEASE_NAME" +cd "$ROOT_DIR/$RELEASE_NAME" +cp \ + "$PROJECT_DIR/build/$RELEASE_NAME.chd" \ + "$PROJECT_DIR/build/$RELEASE_NAME.iso" \ + "$PROJECT_DIR/build/$RELEASE_NAME.psexe" \ + "$PROJECT_DIR/build/readme.txt" \ + . + +## Build BIOS ROM + +cd "$OPENBIOS_DIR" + +make \ + BUILD=Release \ + PREFIX="$TOOLCHAIN_DIR/bin/mipsel-none-elf" \ + FORMAT=elf32-littlemips \ + FASTBOOT=true \ + EMBED_PSEXE="$PROJECT_DIR/build/${RELEASE_NAME}-tiny.psexe" \ + || exit 2 + +cd "$ROOT_DIR/$RELEASE_NAME" +cp "$OPENBIOS_DIR/openbios.bin" "${RELEASE_NAME}-bios.bin" + +## Package release + +zip -9 -r "$ROOT_DIR/$RELEASE_NAME.zip" . \ + || exit 3 + +#cd "$ROOT_DIR" +#rm -rf "$RELEASE_NAME" diff --git a/.github/scripts/buildToolchain.sh b/.github/scripts/buildToolchain.sh new file mode 100755 index 0000000..2963aa6 --- /dev/null +++ b/.github/scripts/buildToolchain.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +ROOT_DIR="$(pwd)" +BINUTILS_VERSION="2.41" +GCC_VERSION="13.2.0" +NUM_JOBS="4" + +if [ $# -eq 2 ]; then + PACKAGE_NAME="$1" + TARGET_NAME="$2" + BUILD_OPTIONS="" +elif [ $# -eq 3 ]; then + PACKAGE_NAME="$1" + TARGET_NAME="$2" + BUILD_OPTIONS="--build=x86_64-linux-gnu --host=$3" +else + echo "Usage: $0 [host triplet]" + exit 0 +fi + +## Download binutils and GCC + +if [ ! -d binutils-$BINUTILS_VERSION ]; then + wget "https://ftpmirror.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.xz" \ + || exit 1 + tar Jxf binutils-$BINUTILS_VERSION.tar.xz \ + || exit 1 + + rm -f binutils-$BINUTILS_VERSION.tar.xz +fi + +if [ ! -d gcc-$GCC_VERSION ]; then + wget "https://ftpmirror.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.xz" \ + || exit 1 + tar Jxf gcc-$GCC_VERSION.tar.xz \ + || exit 1 + + cd gcc-$GCC_VERSION + contrib/download_prerequisites \ + || exit 1 + + cd .. + rm -f gcc-$GCC_VERSION.tar.xz +fi + +## Build binutils + +mkdir -p binutils-build +cd binutils-build + +../binutils-$BINUTILS_VERSION/configure \ + --prefix="$ROOT_DIR/$PACKAGE_NAME" \ + $BUILD_OPTIONS \ + --target=$TARGET_NAME \ + --with-float=soft \ + --disable-docs \ + --disable-nls \ + --disable-werror \ + || exit 2 +make -j $NUM_JOBS \ + || exit 2 +make install-strip \ + || exit 2 + +cd .. +rm -rf binutils-build + +## Build GCC + +mkdir -p gcc-build +cd gcc-build + +../gcc-$GCC_VERSION/configure \ + --prefix="$ROOT_DIR/$PACKAGE_NAME" \ + $BUILD_OPTIONS \ + --target=$TARGET_NAME \ + --with-float=soft \ + --disable-docs \ + --disable-nls \ + --disable-werror \ + --disable-libada \ + --disable-libssp \ + --disable-libquadmath \ + --disable-threads \ + --disable-libgomp \ + --disable-libstdcxx-pch \ + --disable-hosted-libstdcxx \ + --enable-languages=c,c++ \ + --without-isl \ + --without-headers \ + --with-gnu-as \ + --with-gnu-ld \ + || exit 3 +make -j $NUM_JOBS \ + || exit 3 +make install-strip \ + || exit 3 + +cd .. +rm -rf gcc-build + +## Package toolchain + +#cd $PACKAGE_NAME + +#zip -9 -r ../$PACKAGE_NAME-$GCC_VERSION.zip . \ +# || exit 4 + +#cd .. +#rm -rf $PACKAGE_NAME diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..b394342 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,60 @@ +# The GCC toolchain is stored in the GitHub Actions cache after being built. To +# minimize build times, the toolchain build step is skipped if there is a cached +# copy of the toolchain that has not expired. + +name: Build +on: [ push, pull_request ] + +jobs: + build: + name: Run build + runs-on: ubuntu-latest + + steps: + - name: Initialize toolchain cache + id: cache + uses: actions/cache@v3 + with: + key: toolchain + path: gcc-mipsel-none-elf + + - name: Fetch repo contents + uses: actions/checkout@v4 + with: + path: cart-tool + + - name: Fetch OpenBIOS repo contents + uses: actions/checkout@v4 + with: + repository: pcsx-redux/nugget + path: nugget + submodules: recursive + + - name: Install prerequisites + run: | + sudo apt-get update -y + sudo apt-get install -y --no-install-recommends ninja-build mame-tools + sudo pip3 install -r cart-tool/tools/requirements.txt + + - name: Build GCC toolchain + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + run: | + cart-tool/.github/scripts/buildToolchain.sh gcc-mipsel-none-elf mipsel-none-elf + + - name: Build project + run: | + cart-tool/.github/scripts/buildRelease.sh + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build + if-no-files-found: error + path: cart-tool-*/** + + - name: Publish release + if: ${{ github.ref_type == 'tag' }} + uses: softprops/action-gh-release@v1 + with: + fail_on_unmatched_files: true + files: cart-tool-*.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index ff68450..8f85db1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,28 @@ cmake_minimum_required(VERSION 3.25) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain.cmake") project( - cart_tool + cart-tool LANGUAGES C CXX ASM VERSION 0.4.3 DESCRIPTION "Konami System 573 security cartridge tool" ) +set( + RELEASE_INFO "${PROJECT_NAME} ${PROJECT_VERSION} - (C) 2022-2024 spicyjpeg" + CACHE STRING "Executable description and version string (optional)" +) +set( + RELEASE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}" + CACHE STRING "CD-ROM image and release package file name" +) + +string(TOUPPER "${RELEASE_NAME}" _cdVolumeName) +string(REGEX REPLACE "[^0-9A-Z_]" "_" _cdVolumeName "${_cdVolumeName}") +set( + CD_VOLUME_NAME "${_cdVolumeName}" + CACHE STRING "CD-ROM image volume label" +) + find_package(Python3 REQUIRED COMPONENTS Interpreter) find_program( CHDMAN_PATH chdman @@ -20,22 +36,6 @@ find_program( DOC "Path to MAME chdman tool (optional)" ) -string(TOUPPER "${PROJECT_NAME}" _name) -string(REPLACE "." "_" _version "${PROJECT_VERSION}") - -set( - RELEASE_INFO "${PROJECT_NAME} ${PROJECT_VERSION} - (C) 2022-2024 spicyjpeg" - CACHE STRING "Executable description and version string (optional)" -) -set( - RELEASE_NAME "${PROJECT_NAME}_${PROJECT_VERSION}" - CACHE STRING "CD-ROM image and release package file name" -) -set( - CD_VOLUME_NAME "${_name}_${_version}" - CACHE STRING "CD-ROM image volume label" -) - ## Files common to all executables add_library( @@ -171,14 +171,6 @@ target_compile_definitions( > ) -addExecutable( - boot 80010000 0 - src/boot/crt0.s - src/boot/main.cpp - src/common/util.cpp -) -target_link_libraries(boot PRIVATE bootFlags) - function(addLauncher address stackTop) addExecutable( launcher${address} ${address} ${stackTop} @@ -199,84 +191,60 @@ endfunction() addLauncher(801fd000 801ffff0) addLauncher(803fd000 803ffff0) -## Default resource archive +## Boot stub and resource archive -function(addBinaryFile target name sizeName path) - set(_file "${PROJECT_BINARY_DIR}/includes/${target}_${name}.s") - cmake_path(ABSOLUTE_PATH path OUTPUT_VARIABLE _path) +function(addBootStub name resourceName) + configure_file(${resourceName}.json ${resourceName}.json ESCAPE_QUOTES) - file( - CONFIGURE - OUTPUT "${_file}" - CONTENT [[ -.section .rodata.${name}, "a" -.balign 8 - -.global ${name} -.type ${name}, @object -.size ${name}, (${name}_end - ${name}) - -${name}: - .incbin "${_path}" -${name}_end: - -.section .rodata.${sizeName}, "a" -.balign 4 - -.global ${sizeName} -.type ${sizeName}, @object -.size ${sizeName}, 4 - -${sizeName}: - .int (${name}_end - ${name}) -]] - ESCAPE_QUOTES - NEWLINE_STYLE LF + add_custom_command( + COMMAND + "${Python3_EXECUTABLE}" + "${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py" + ${resourceName}.json + ${resourceName}.zip + OUTPUT ${resourceName}.zip + DEPENDS + ${resourceName}.json + assets/app.palette.json + assets/app.strings.json + main.psexe + launcher801fd000.psexe + launcher803fd000.psexe + COMMENT "Building ${name} resource archive" + VERBATIM ) - target_sources(${target} PRIVATE "${_file}") - set_source_files_properties("${_file}" PROPERTIES OBJECT_DEPENDS "${_path}") + addExecutable( + ${name} 80010000 0 + src/boot/crt0.s + src/boot/main.cpp + src/common/util.cpp + ) + addBinaryFile( + ${name} _resourceArchive _resourceArchiveLength + "${PROJECT_BINARY_DIR}/${resourceName}.zip" + ) + target_link_libraries(${name} PRIVATE bootFlags) endfunction() -configure_file(resources.json resources.json ESCAPE_QUOTES) - -add_custom_command( - COMMAND - "${Python3_EXECUTABLE}" - "${PROJECT_SOURCE_DIR}/tools/buildResourceArchive.py" - resources.json - resources.zip - OUTPUT resources.zip - DEPENDS - resources.json - assets/app.palette.json - assets/app.strings.json - main.psexe - launcher801fd000.psexe - launcher803fd000.psexe - COMMENT "Building resource archive" - VERBATIM -) -addBinaryFile( - boot _resourceArchive _resourceArchiveLength - "${PROJECT_BINARY_DIR}/resources.zip" -) +addBootStub("${RELEASE_NAME}" resources) +addBootStub("${RELEASE_NAME}-tiny" resourcestiny) ## CD-ROM image -configure_file(cd.json cd.json ESCAPE_QUOTES) +configure_file(cdrom.json cdrom.json ESCAPE_QUOTES) configure_file(assets/cdreadme.txt readme.txt NEWLINE_STYLE CRLF) add_custom_command( COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/tools/buildCDImage.py" - cd.json + cdrom.json "${RELEASE_NAME}.iso" OUTPUT "${RELEASE_NAME}.iso" DEPENDS - cd.json - boot.psexe + cdrom.json + "${RELEASE_NAME}" COMMENT "Building CD-ROM image" VERBATIM ) @@ -293,28 +261,7 @@ if(EXISTS "${CHDMAN_PATH}") VERBATIM ) - list(APPEND packageFiles "${RELEASE_NAME}.chd") + add_custom_target(cdrom ALL DEPENDS "${RELEASE_NAME}.chd") +else() + add_custom_target(cdrom ALL DEPENDS "${RELEASE_NAME}.iso") endif() - -## Release package - -list( - APPEND packageFiles - readme.txt - boot.psexe - "${RELEASE_NAME}.iso" -) - -add_custom_command( - COMMAND - "${CMAKE_COMMAND}" -E tar cf - "${RELEASE_NAME}.zip" - --format=zip - ${packageFiles} - OUTPUT "${RELEASE_NAME}.zip" - DEPENDS ${packageFiles} - COMMENT "Packaging built files" - VERBATIM -) - -add_custom_target(package ALL DEPENDS "${RELEASE_NAME}.zip") diff --git a/assets/textures/background.png b/assets/textures/background.png index c14dc40..c6a8ba1 100644 Binary files a/assets/textures/background.png and b/assets/textures/background.png differ diff --git a/cd.json b/cdrom.json similarity index 94% rename from cd.json rename to cdrom.json index d28e8b2..48351a2 100644 --- a/cd.json +++ b/cdrom.json @@ -15,11 +15,15 @@ "name": "README.TXT", "source": "${PROJECT_BINARY_DIR}/readme.txt" }, + { + "type": "empty", + "name": "NOBOOT.TXT" + }, { "type": "file", "name": "PSX.EXE", - "source": "${PROJECT_BINARY_DIR}/boot.psexe" + "source": "${PROJECT_BINARY_DIR}/${RELEASE_NAME}.psexe" }, { "type": "fileAlias", @@ -118,7 +122,7 @@ }, { - "type": "padding", + "type": "empty", "name": "PADDING.BIN", "size": 1048576 } diff --git a/cmake/setup.cmake b/cmake/setup.cmake index 96877e5..bd8bb81 100644 --- a/cmake/setup.cmake +++ b/cmake/setup.cmake @@ -81,3 +81,41 @@ target_link_options( -G8 "-T${CMAKE_CURRENT_LIST_DIR}/executable.ld" ) + +# Define a helper function to embed binary data into executables and libraries. +function(addBinaryFile target name sizeName path) + set(_file "${PROJECT_BINARY_DIR}/includes/${target}_${name}.s") + cmake_path(ABSOLUTE_PATH path OUTPUT_VARIABLE _path) + + file( + CONFIGURE + OUTPUT "${_file}" + CONTENT [[ +.section .rodata.${name}, "a" +.balign 8 + +.global ${name} +.type ${name}, @object +.size ${name}, (${name}_end - ${name}) + +${name}: + .incbin "${_path}" +${name}_end: + +.section .rodata.${sizeName}, "a" +.balign 4 + +.global ${sizeName} +.type ${sizeName}, @object +.size ${sizeName}, 4 + +${sizeName}: + .int (${name}_end - ${name}) +]] + ESCAPE_QUOTES + NEWLINE_STYLE LF + ) + + target_sources(${target} PRIVATE "${_file}") + set_source_files_properties("${_file}" PROPERTIES OBJECT_DEPENDS "${_path}") +endfunction() diff --git a/resourcestiny.json b/resourcestiny.json new file mode 100644 index 0000000..1eff047 --- /dev/null +++ b/resourcestiny.json @@ -0,0 +1,112 @@ +[ + { + "type": "binary", + "name": "binaries/main.psexe.lz4", + "source": "${PROJECT_BINARY_DIR}/main.psexe", + "compression": "lz4" + }, + { + "type": "binary", + "name": "binaries/launcher801fd000.psexe", + "source": "${PROJECT_BINARY_DIR}/launcher801fd000.psexe" + }, + { + "type": "binary", + "name": "binaries/launcher803fd000.psexe", + "source": "${PROJECT_BINARY_DIR}/launcher803fd000.psexe" + }, + + { + "type": "tim", + "name": "assets/textures/background.tim", + "source": "${PROJECT_SOURCE_DIR}/assets/textures/background.png", + "quantize": 16, + "imagePos": { "x": 960, "y": 0 }, + "clutPos": { "x": 1008, "y": 0 } + }, + { + "type": "tim", + "name": "assets/textures/font.tim", + "source": "${PROJECT_SOURCE_DIR}/assets/textures/font.png", + "quantize": 16, + "imagePos": { "x": 984, "y": 0 }, + "clutPos": { "x": 1008, "y": 1 } + }, + { + "type": "metrics", + "name": "assets/textures/font.metrics", + "source": "${PROJECT_SOURCE_DIR}/assets/textures/font.metrics.json" + }, + { + "type": "binary", + "name": "assets/sounds/alert.vag", + "source": "${PROJECT_SOURCE_DIR}/assets/sounds/alert.vag", + "compression": "none" + }, + { + "type": "binary", + "name": "assets/sounds/move.vag", + "source": "${PROJECT_SOURCE_DIR}/assets/sounds/move.vag", + "compression": "none" + }, + { + "type": "binary", + "name": "assets/sounds/enter.vag", + "source": "${PROJECT_SOURCE_DIR}/assets/sounds/enter.vag", + "compression": "none" + }, + { + "type": "binary", + "name": "assets/sounds/exit.vag", + "source": "${PROJECT_SOURCE_DIR}/assets/sounds/exit.vag", + "compression": "none" + }, + { + "type": "binary", + "name": "assets/sounds/click.vag", + "source": "${PROJECT_SOURCE_DIR}/assets/sounds/click.vag", + "compression": "none" + }, + { + "type": "binary", + "name": "assets/sounds/screenshot.vag", + "source": "${PROJECT_SOURCE_DIR}/assets/sounds/screenshot.vag", + "compression": "none" + }, + { + "type": "palette", + "name": "assets/app.palette", + "source": "${PROJECT_SOURCE_DIR}/assets/app.palette.json" + }, + { + "type": "strings", + "name": "assets/app.strings", + "source": "${PROJECT_SOURCE_DIR}/assets/app.strings.json" + }, + { + "type": "text", + "name": "assets/about.txt", + "source": "${PROJECT_SOURCE_DIR}/assets/about.txt" + }, + + { + "type": "binary", + "name": "data/fpga.bit", + "source": "${PROJECT_SOURCE_DIR}/data/fpga.bit" + }, + { + "type": "binary", + "name": "data/x76f041.db", + "source": "${PROJECT_SOURCE_DIR}/data/x76f041.db" + }, + { + "type": "binary", + "name": "data/zs01.db", + "source": "${PROJECT_SOURCE_DIR}/data/zs01.db" + }, + { + "type": "binary", + "name": "data/flash.db", + "source": "${PROJECT_SOURCE_DIR}/data/flash.db" + } +] diff --git a/tools/buildCDImage.py b/tools/buildCDImage.py index 1a6d183..9395fee 100755 --- a/tools/buildCDImage.py +++ b/tools/buildCDImage.py @@ -223,12 +223,12 @@ def main(): for entry in entryList: match entry.get("type", "file").strip(): - case "padding": + case "empty": name: str = normalizePath(entry["name"]) iso.add_fp( fp = paddingFile, - length = int(entry["size"]), + length = int(entry.get("size", 0)), iso_path = name ) iso.set_hidden(iso_path = name) diff --git a/tools/common/assets.py b/tools/common/assets.py index ca3b832..e80fc7b 100644 --- a/tools/common/assets.py +++ b/tools/common/assets.py @@ -65,18 +65,18 @@ def convertIndexedImage(imageObj: Image.Image) -> tuple[ndarray, ndarray]: # Pad the palette to 16 or 256 colors. padAmount: int = (16 if (numColors <= 16) else 256) - numColors if padAmount: - clut = numpy.c_[ clut, numpy.zeros(padAmount, "