From ff559580bf5d3d378cdcdb0bef4e03c9b2ef1b4a Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Wed, 2 Oct 2024 10:23:23 +0200 Subject: [PATCH] misc: merge tr2 and libtrx codebases --- .github/workflows/build_docker.yml | 12 +- .../{job_build_game.yml => job_build_tr1.yml} | 12 +- ...game_macos.yml => job_build_tr1_macos.yml} | 10 +- .github/workflows/job_build_tr2.yml | 40 + .github/workflows/job_release.yml | 29 +- .github/workflows/pr_builds.yml | 17 +- .github/workflows/prerelease.yml | 25 +- .../{release.yml => release_tr1.yml} | 15 +- .github/workflows/release_tr2.yml | 47 ++ .gitignore | 12 +- .pre-commit-config.yaml | 12 +- README.md | 713 ++---------------- data/download_tr1x.svg | 53 ++ data/download_tr2x.svg | 13 + data/tr1/ship/cfg/TR1X_gameflow.json5 | 2 +- data/tr1/ship/cfg/TR1X_gameflow_ub.json5 | 2 +- CONTRIBUTING.md => docs/CONTRIBUTING.md | 57 +- CHANGELOG.md => docs/tr1/CHANGELOG.md | 2 +- COMMANDS.md => docs/tr1/COMMANDS.md | 0 GAMEFLOW.md => docs/tr1/GAMEFLOW.md | 8 + docs/tr1/README.md | 604 +++++++++++++++ SECRETS.md => docs/tr1/SECRETS.md | 0 docs/{ => tr1}/showcase/3d_pickups.jpg | Bin docs/{ => tr1}/showcase/braid.jpg | Bin docs/{ => tr1}/showcase/compass_stats.jpg | Bin docs/{ => tr1}/showcase/console.webp | Bin docs/{ => tr1}/showcase/draw_distance.webp | Bin .../showcase/enemy_health_bar_and_scaling.jpg | Bin docs/{ => tr1}/showcase/fly_cheat.jpg | Bin docs/{ => tr1}/showcase/free_camera.jpg | Bin docs/{ => tr1}/showcase/ps1_ui_and_gfx.jpg | Bin docs/{ => tr1}/showcase/select_level.jpg | Bin docs/{ => tr1}/showcase/skybox.jpg | Bin docs/tr2/README.md | 72 +- justfile | 56 +- meson.build | 319 -------- src/libtrx/meson.build | 166 ++++ src/libtrx/meson.options | 20 + src/tr1/meson.build | 320 ++++++++ src/tr2/meson.build | 203 +++++ meson.options => src/tr2/meson.options | 0 src/tr2/subprojects/libtrx | 1 + subprojects/libtrx | 1 - tools/additional_lint | 74 +- tools/check_config_defaults | 25 +- tools/generate_icon | 24 + tools/generate_images | 7 +- tools/get_version | 19 +- tools/libtrx | 1 - tools/output_current_changelog | 44 +- tools/output_release_name | 30 + tools/release | 138 ++++ tools/{tr1x => shared}/__init__.py | 0 tools/shared/changelog.py | 39 + tools/{tr2/tr2x => shared/cli}/__init__.py | 0 tools/shared/cli/game_docker_entrypoint.py | 139 ++++ tools/shared/files.py | 53 ++ tools/shared/git.py | 77 ++ tools/shared/icons.py | 78 ++ tools/{tr2/tr2x => shared}/ida_progress.py | 0 tools/shared/import_sorter.py | 96 +++ tools/shared/linting.py | 129 ++++ tools/shared/packaging.py | 17 + tools/shared/paths.py | 34 + tools/shared/versioning.py | 22 + tools/sort_imports | 81 ++ tools/tr1/__init__.py | 0 .../TR1X_ConfigTool/TR1X_ConfigTool.csproj | 2 +- tools/tr1/docker/config/entrypoint.sh | 2 +- tools/tr1/docker/game-linux/entrypoint.sh | 11 +- tools/tr1/docker/game-win/entrypoint.sh | 11 +- tools/{ => tr1}/generate_init | 6 +- tools/{ => tr1}/generate_rcfile | 14 +- tools/tr1/inspect_save | 29 + tools/tr1/release | 8 - tools/tr1/shared | 1 + tools/tr1/sort_imports | 29 - tools/tr1/update_gameflow | 53 -- tools/tr1x/paths.py | 10 - tools/tr2/__init__.py | 0 .../TR2X_ConfigTool/TR2X_ConfigTool.csproj | 2 +- tools/tr2/docker/config/Dockerfile | 2 +- tools/tr2/docker/config/entrypoint.sh | 4 +- tools/tr2/docker/game-win/Dockerfile | 2 +- tools/tr2/docker/game-win/entrypoint.sh | 19 +- tools/tr2/ffmpeg_flags.txt | 1 - tools/tr2/generate_funcs | 27 +- tools/tr2/generate_ida_importer | 7 +- tools/tr2/generate_init | 6 +- tools/tr2/generate_rcfile | 14 +- tools/tr2/libtrx | 1 - tools/tr2/render_progress | 13 +- tools/tr2/shared | 1 + tools/tr2/tr2x/paths.py | 13 - tools/tr2/update_gameflow | 76 -- tools/update_gameflow | 113 +++ 96 files changed, 3026 insertions(+), 1421 deletions(-) rename .github/workflows/{job_build_game.yml => job_build_tr1.yml} (68%) rename .github/workflows/{job_build_game_macos.yml => job_build_tr1_macos.yml} (96%) create mode 100644 .github/workflows/job_build_tr2.yml rename .github/workflows/{release.yml => release_tr1.yml} (78%) create mode 100644 .github/workflows/release_tr2.yml create mode 100644 data/download_tr1x.svg create mode 100644 data/download_tr2x.svg rename CONTRIBUTING.md => docs/CONTRIBUTING.md (83%) rename CHANGELOG.md => docs/tr1/CHANGELOG.md (99%) rename COMMANDS.md => docs/tr1/COMMANDS.md (100%) rename GAMEFLOW.md => docs/tr1/GAMEFLOW.md (99%) create mode 100644 docs/tr1/README.md rename SECRETS.md => docs/tr1/SECRETS.md (100%) rename docs/{ => tr1}/showcase/3d_pickups.jpg (100%) rename docs/{ => tr1}/showcase/braid.jpg (100%) rename docs/{ => tr1}/showcase/compass_stats.jpg (100%) rename docs/{ => tr1}/showcase/console.webp (100%) rename docs/{ => tr1}/showcase/draw_distance.webp (100%) rename docs/{ => tr1}/showcase/enemy_health_bar_and_scaling.jpg (100%) rename docs/{ => tr1}/showcase/fly_cheat.jpg (100%) rename docs/{ => tr1}/showcase/free_camera.jpg (100%) rename docs/{ => tr1}/showcase/ps1_ui_and_gfx.jpg (100%) rename docs/{ => tr1}/showcase/select_level.jpg (100%) rename docs/{ => tr1}/showcase/skybox.jpg (100%) delete mode 100644 meson.build create mode 100644 src/libtrx/meson.build create mode 100644 src/libtrx/meson.options create mode 100644 src/tr1/meson.build create mode 100644 src/tr2/meson.build rename meson.options => src/tr2/meson.options (100%) create mode 120000 src/tr2/subprojects/libtrx delete mode 160000 subprojects/libtrx create mode 100755 tools/generate_icon delete mode 120000 tools/libtrx create mode 100755 tools/output_release_name create mode 100755 tools/release rename tools/{tr1x => shared}/__init__.py (100%) create mode 100644 tools/shared/changelog.py rename tools/{tr2/tr2x => shared/cli}/__init__.py (100%) create mode 100644 tools/shared/cli/game_docker_entrypoint.py create mode 100644 tools/shared/files.py create mode 100644 tools/shared/git.py create mode 100644 tools/shared/icons.py rename tools/{tr2/tr2x => shared}/ida_progress.py (100%) create mode 100644 tools/shared/import_sorter.py create mode 100644 tools/shared/linting.py create mode 100644 tools/shared/packaging.py create mode 100644 tools/shared/paths.py create mode 100644 tools/shared/versioning.py create mode 100755 tools/sort_imports create mode 100644 tools/tr1/__init__.py rename tools/{ => tr1}/generate_init (83%) rename tools/{ => tr1}/generate_rcfile (67%) mode change 100644 => 100755 create mode 100755 tools/tr1/inspect_save delete mode 100755 tools/tr1/release create mode 120000 tools/tr1/shared delete mode 100755 tools/tr1/sort_imports delete mode 100755 tools/tr1/update_gameflow delete mode 100644 tools/tr1x/paths.py create mode 100644 tools/tr2/__init__.py delete mode 120000 tools/tr2/ffmpeg_flags.txt delete mode 120000 tools/tr2/libtrx create mode 120000 tools/tr2/shared delete mode 100644 tools/tr2/tr2x/paths.py delete mode 100755 tools/tr2/update_gameflow create mode 100755 tools/update_gameflow diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index b6ff87ffa..3e3656e60 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -10,8 +10,12 @@ jobs: strategy: matrix: include: - - platform: win - - platform: linux + - game_version: tr1 + platform: win + - game_version: tr1 + platform: linux + - game_version: tr2 + platform: win steps: - name: Login to Docker Hub uses: docker/login-action@v1 @@ -29,5 +33,5 @@ jobs: - name: Build Docker image (${{ matrix.platform }}) run: | - just image-${{ matrix.platform }} - just push-image-${{ matrix.platform }} + just ${{ matrix.game_version }}-image-${{ matrix.platform }} + just ${{ matrix.game_version }}-push-image-${{ matrix.platform }} diff --git a/.github/workflows/job_build_game.yml b/.github/workflows/job_build_tr1.yml similarity index 68% rename from .github/workflows/job_build_game.yml rename to .github/workflows/job_build_tr1.yml index 59ee44f8a..fa81af90b 100644 --- a/.github/workflows/job_build_game.yml +++ b/.github/workflows/job_build_tr1.yml @@ -1,4 +1,4 @@ -name: Build the game and the installer +name: Build TR1X and the installer on: workflow_call: @@ -12,11 +12,11 @@ jobs: matrix: include: - platform: linux - just_target: package-linux + just_target: tr1-package-linux - platform: win - just_target: package-win-all + just_target: tr1-package-win-all - platform: win-installer - just_target: package-win-installer + just_target: tr1-package-win-installer steps: - name: Install dependencies @@ -30,7 +30,7 @@ jobs: - id: vars name: Prepare variables - run: echo "version=$(just output-current-version)" >> $GITHUB_OUTPUT + run: echo "version=$(just output-current-version 1)" >> $GITHUB_OUTPUT - name: Package asset (${{ matrix.platform }}) run: just ${{ matrix.just_target }} @@ -38,7 +38,7 @@ jobs: - name: Upload the artifact uses: actions/upload-artifact@v4 with: - name: ${{ github.event.repository.name }}-${{ steps.vars.outputs.version }}-${{ matrix.platform }} + name: TR1X-${{ steps.vars.outputs.version }}-${{ matrix.platform }} path: | *.zip *.exe diff --git a/.github/workflows/job_build_game_macos.yml b/.github/workflows/job_build_tr1_macos.yml similarity index 96% rename from .github/workflows/job_build_game_macos.yml rename to .github/workflows/job_build_tr1_macos.yml index 573344b27..3e430e0c8 100644 --- a/.github/workflows/job_build_game_macos.yml +++ b/.github/workflows/job_build_tr1_macos.yml @@ -1,4 +1,4 @@ -name: Build the game and the installer (macOS) +name: Build TR1X and the installer (macOS) on: workflow_call: @@ -225,14 +225,14 @@ jobs: - name: Build arm64 and create app bundle run: | BUILD_DIR=build-arm64 - BUILD_OPTIONS="--prefix=/tmp/TR1X.app --bindir=Contents/MacOS" + BUILD_OPTIONS="src/tr1 --prefix=/tmp/TR1X.app --bindir=Contents/MacOS" meson setup $BUILD_DIR $BUILD_OPTIONS meson install -C $BUILD_DIR - name: Build x86-64 run: | BUILD_DIR=build-x86-64 - BUILD_OPTIONS="--prefix=/tmp/TR1X.app --bindir=Contents/MacOS --cross-file tools/tr1/mac/x86-64_cross_file.txt" + BUILD_OPTIONS="src/tr1 --prefix=/tmp/TR1X.app --bindir=Contents/MacOS --cross-file tools/tr1/mac/x86-64_cross_file.txt" meson setup $BUILD_DIR $BUILD_OPTIONS meson compile -C $BUILD_DIR @@ -267,11 +267,11 @@ jobs: /usr/bin/codesign --force --options runtime -s "${IDENTITY}" --keychain $KEYCHAIN_PATH -v TR1X-Installer.dmg xcrun notarytool submit --wait --apple-id "$MACOS_APPLEID" --password "$MACOS_APP_PWD" --team-id "$MACOS_TEAMID" TR1X-Installer.dmg xcrun stapler staple -v TR1X-Installer.dmg - mv TR1X-Installer.dmg "TR1X-$(tools/get_version)-Installer.dmg" + mv TR1X-Installer.dmg "TR1X-$(tools/get_version 1)-Installer.dmg" - id: vars name: Prepare variables - run: echo "version=$(tools/get_version)" >> $GITHUB_OUTPUT + run: echo "version=$(tools/get_version 1)" >> $GITHUB_OUTPUT - name: Upload signed+notarized installer image uses: actions/upload-artifact@v4 diff --git a/.github/workflows/job_build_tr2.yml b/.github/workflows/job_build_tr2.yml new file mode 100644 index 000000000..625f4bae7 --- /dev/null +++ b/.github/workflows/job_build_tr2.yml @@ -0,0 +1,40 @@ +name: Build TR2X + +on: + workflow_call: + workflow_dispatch: + +jobs: + build: + name: Build release assets + runs-on: ubuntu-latest + strategy: + matrix: + include: + - platform: win + just_target: tr2-package-win-all + + steps: + - name: Install dependencies + uses: taiki-e/install-action@just + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 0 + + - id: vars + name: Prepare variables + run: echo "version=$(just output-current-version 2)" >> $GITHUB_OUTPUT + + - name: Package asset (${{ matrix.platform }}) + run: just ${{ matrix.just_target }} + + - name: Upload the artifact + uses: actions/upload-artifact@v4 + with: + name: TR2X-${{ steps.vars.outputs.version }}-${{ matrix.platform }} + path: | + *.zip + *.exe diff --git a/.github/workflows/job_release.yml b/.github/workflows/job_release.yml index fd1d7bbff..a62c615f7 100644 --- a/.github/workflows/job_release.yml +++ b/.github/workflows/job_release.yml @@ -3,6 +3,10 @@ name: Create a new release on: workflow_call: inputs: + changelog_game_version: + type: string + description: "Game version to build the changelog from" + required: true draft: type: boolean description: "Draft" @@ -13,11 +17,6 @@ on: description: "Prerelease" required: true default: false - release_name: - type: string - description: "Release name" - required: true - default: "Release ${{ github.ref_name }}" tag_name: type: string description: "Tag name" @@ -39,19 +38,21 @@ jobs: submodules: 'true' fetch-depth: 0 + - name: "Prepare release data" + id: prepare_release_data + run: | + echo -n "release_name=" >> $GITHUB_OUTPUT + just output-release-name ${{ inputs.changelog_game_version }} >> $GITHUB_OUTPUT + echo "changelog<> $GITHUB_OUTPUT + just output-current-changelog ${{ inputs.changelog_game_version }} >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: "Download built game assets" uses: actions/download-artifact@v4 with: path: artifacts/ merge-multiple: true - - name: "Generate changelog" - run: | - hash=$(git log -1 --pretty=format:%H) - tag=$(just output-current-version) - echo -e "**Commit: $hash** \n**Tag: $tag**\n\n### Changes\n" > _changes.txt - just output-current-changelog >> _changes.txt - - name: "Get information on the latest pre-release" if: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }} id: last_release @@ -77,9 +78,9 @@ jobs: uses: softprops/action-gh-release@v2 with: token: ${{ secrets.GITHUB_TOKEN }} - name: ${{ inputs.release_name }} tag_name: ${{ inputs.tag_name }} - body_path: _changes.txt + name: ${{ steps.prepare_release_data.outputs.release_name }} + body: ${{ steps.prepare_release_data.outputs.changelog }} draft: ${{ inputs.draft == true || inputs.draft == 'true' }} prerelease: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }} fail_on_unmatched_files: true diff --git a/.github/workflows/pr_builds.yml b/.github/workflows/pr_builds.yml index 5c58849d2..4ba3516cf 100644 --- a/.github/workflows/pr_builds.yml +++ b/.github/workflows/pr_builds.yml @@ -11,15 +11,20 @@ on: - '!develop' jobs: - package_multiplatform: - name: 'Create a test build' - uses: ./.github/workflows/job_build_game.yml + package_tr1_multiplatform: + name: Build TR1 + uses: ./.github/workflows/job_build_tr1.yml secrets: inherit - package_mac: - name: 'Create a test build (macOS)' + package_tr1_mac: + name: Build TR1 if: vars.MACOS_ENABLE == 'true' - uses: ./.github/workflows/job_build_game_macos.yml + uses: ./.github/workflows/job_build_tr1_macos.yml with: let_mac_fail: true secrets: inherit + + package_tr2_multiplatform: + name: Build TR2 + uses: ./.github/workflows/job_build_tr2.yml + secrets: inherit diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index e723ad28a..9d3d60b34 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -9,31 +9,38 @@ on: - develop jobs: - package_multiplatform: - name: Build prerelease assets + package_tr1_multiplatform: + name: Build TR1 if: vars.PRERELEASE_ENABLE == 'true' - uses: ./.github/workflows/job_build_game.yml + uses: ./.github/workflows/job_build_tr1.yml secrets: inherit - package_mac: - name: "Build prerelease assets (mac)" + package_tr1_mac: + name: Build TR1 if: | vars.PRERELEASE_ENABLE == 'true' && vars.MACOS_ENABLE == 'true' - uses: ./.github/workflows/job_build_game_macos.yml + uses: ./.github/workflows/job_build_tr1_macos.yml with: let_mac_fail: true secrets: inherit + package_tr2_multiplatform: + name: Build TR2 + if: vars.PRERELEASE_ENABLE == 'true' + uses: ./.github/workflows/job_build_tr2.yml + secrets: inherit + publish_prerelease: if: vars.PRERELEASE_ENABLE == 'true' name: Create a prerelease needs: - - package_multiplatform - - package_mac + - package_tr1_multiplatform + - package_tr1_mac + - package_tr2_multiplatform with: - release_name: 'Development snapshot' draft: false prerelease: true tag_name: 'latest' + changelog_game_version: 'all' uses: ./.github/workflows/job_release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release_tr1.yml similarity index 78% rename from .github/workflows/release.yml rename to .github/workflows/release_tr1.yml index 3f8541dff..e6ffedfd3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release_tr1.yml @@ -1,4 +1,4 @@ -name: Publish a new release +name: Publish a new TR1X release permissions: contents: write @@ -7,7 +7,7 @@ on: push: branch: stable tags: - - "v?[0-9]*" + - "tr1-*" workflow_dispatch: inputs: @@ -21,11 +21,6 @@ on: required: true type: boolean default: false - release_name: - description: "Release name" - required: true - type: string - default: "Release name" tag_name: description: "Tag name" required: false @@ -36,7 +31,7 @@ jobs: package_multiplatform: name: Build release assets if: vars.RELEASE_ENABLE == 'true' - uses: ./.github/workflows/job_build_game.yml + uses: ./.github/workflows/job_build_tr1.yml secrets: inherit package_mac: @@ -44,7 +39,7 @@ jobs: if: | vars.RELEASE_ENABLE == 'true' && vars.MACOS_ENABLE == 'true' - uses: ./.github/workflows/job_build_game_macos.yml + uses: ./.github/workflows/job_build_tr1_macos.yml with: let_mac_fail: ${{ inputs.let_mac_fail == true || inputs.let_mac_fail == 'true' }} secrets: inherit @@ -56,8 +51,8 @@ jobs: - package_multiplatform - package_mac with: - release_name: ${{ inputs.release_name }} draft: ${{ inputs.draft || false }} prerelease: ${{ inputs.draft || false }} tag_name: ${{ inputs.tag_name || github.ref_name }} + changelog_game_version: 1 uses: ./.github/workflows/job_release.yml diff --git a/.github/workflows/release_tr2.yml b/.github/workflows/release_tr2.yml new file mode 100644 index 000000000..926eba130 --- /dev/null +++ b/.github/workflows/release_tr2.yml @@ -0,0 +1,47 @@ +name: Publish a new TR2X release + +permissions: + contents: write + +on: + push: + branch: stable + tags: + - "tr2-*" + + workflow_dispatch: + inputs: + draft: + description: "Draft" + required: true + type: boolean + default: false + prerelease: + description: "Prerelease" + required: true + type: boolean + default: false + tag_name: + description: "Tag name" + required: false + type: string + default: github.ref_name + +jobs: + package_multiplatform: + name: Build release assets + if: vars.RELEASE_ENABLE == 'true' + uses: ./.github/workflows/job_build_tr2.yml + secrets: inherit + + publish_release: + if: vars.RELEASE_ENABLE == 'true' + name: Create a GitHub release + needs: + - package_multiplatform + with: + draft: ${{ inputs.draft || false }} + prerelease: ${{ inputs.draft || false }} + tag_name: ${{ inputs.tag_name || github.ref_name }} + changelog_game_version: 2 + uses: ./.github/workflows/job_release.yml diff --git a/.gitignore b/.gitignore index b9ec2a7a4..a69d0c2b1 100644 --- a/.gitignore +++ b/.gitignore @@ -31,8 +31,10 @@ Release/ .dotnet/ # libtrx artefacts -subprojects/packagecache/ -subprojects/dwarfstack-*/ -subprojects/dwarfstack.wrap -subprojects/uthash-*/ -subprojects/uthash.wrap +**/subprojects/packagecache/ +**/subprojects/dwarfstack-*/ +**/subprojects/uthash-*/ +src/tr1/subprojects/dwarfstack.wrap +src/tr1/subprojects/uthash.wrap +src/tr2/subprojects/dwarfstack.wrap +src/tr2/subprojects/uthash.wrap diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 52a3f03a3..fa09f458e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,12 +8,6 @@ repos: language: system files: \.[ch](pp)?$ - - id: update-gameflow - name: Update game flow files - entry: tools/update_gameflow - language: python - stages: [commit] - - id: additional-lint name: Run additional linters entry: tools/additional_lint @@ -25,3 +19,9 @@ repos: entry: tools/sort_imports language: system files: \.[ch](pp)?$ + + - id: update-gameflow + name: Update game flow files + entry: tools/update_gameflow + language: python + stages: [commit] diff --git a/README.md b/README.md index b64320915..35e846453 100644 --- a/README.md +++ b/README.md @@ -1,711 +1,128 @@ -

-TR1X logo -TR1X logo -

+
+

TRX – Tomb Raider I & II: Community Edition

-This is an open source implementation of the classic Tomb Raider I game (1996), -made by reverse engineering the TombATI / GLRage variant of the original game -and replacing proprietary audio/video libraries with open source variants. + + + + + + +
-See the [Tomb Raider Forums -topic](https://www.tombraiderforums.com/showthread.php?p=8286101). +
-## Showcase +> [!NOTE] +> +> We've merged TR1X and TR2X into a single codebase, now renamed **TRX**. +> +> - TR1 and TR2 remain distinct with their own release cycles. +> - Config tools and TR1 installer remain separate. +> - Simplifies internal code merges and feature sharing, replacing **libtrx**. +> +> Thanks for your support! + +Welcome to **TRX** – an open-source reimplementation of **Tomb Raider 1** and **Tomb Raider 2**, respectively. Both projects aim to enhance these classic games through decompilation and the implementation of open-source alternatives to proprietary components. + +## TR1X - Tomb Raider 1 +### Showcase - - - -
Restored braid - + Enemy health bar and UI scaling - +
3D pickups - + - Improved stats - -
Skybox support - - - Customizable draw distance - +
- Fly cheat - + Customizable draw distance + Developer console - +
Free camera - + PS1 UI and new graphics options - +
-## Windows / Linux +### Download +Download the latest release: -### Installing (simplified) + + + -1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases -2. Download the installer. Your browser may complain that the .exe is unsafe, but it's OK to ignore this alert. -3. Mark the installer EXE as safe to run by right-clicking on the .exe, going to properties and clicking "Unblock". -4. Run the installer and proceed with the steps. +See [the changelog](docs/tr1/CHANGELOG.md). -We hope that eventually these alerts will go away as the popularity of the project rises. +### Install Instructions +Please refer to the [detailed documentation](docs/tr1/). -### Installing (advanced / manual) +## TR2X - Tomb Raider 2 -1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases -2. Download the zip file. -3. Extract the zip file into a directory of your choice. - Make sure you choose to overwrite existing directories and files - (`cfg/TR1X_config.json5` can remain, but new features will not be configurable). -4. (First time installation) Put your original game files into the target directory. - 1. For Steam and GOG users, extract the original `GAME.BIN` file using a tool such as UltraISO to your target directory. - Note that neither the GOG nor the Steam releases ship the music files. You have a few options here: - - You can download the music files from the link below. - https://lostartefacts.dev/aux/tr1x/music.zip - The legality of this approach is disputable. - - Rip the assets yourself from a physical PlayStation/SegaSaturn disk. +### Overview +TR2X serves as a sequel to TR1X, currently focusing on the decompilation of Tomb Raider 2. - Optionally you can also install the Unfinished Business expansion pack files. - - Either one of these these variants: - - https://lostartefacts.dev/aux/tr1x/trub-music.zip (fan-made patch to include music triggers) - - https://lostartefacts.dev/aux/tr1x/trub-vanilla.zip (original level files, which do not include music triggers) - - Or the more manual link: https://archive.org/details/tomb-raider-i-unfinished-business-pc-eng-full-version_20201225 - 2. For TombATI users this means copying the `data`, `fmv` and `music` directories. -5. To play the game, run `TR1X.exe`. -6. To play the Unfinished Expansion pack, run `TR1X.exe -gold`. +### Decompilation Progress +![Decompilation Progress](docs/tr2/progress.svg) -If you install everything correctly, your game directory should look more or -less like this (click to expand): +### Download +Download the latest release: -
-

* Will not be present until the game has been launched.

-
-.
-├── cfg
-│   ├── TR1X.json5 *
-│   ├── TR1X_gameflow.json5
-│   ├── TR1X_gameflow_demo_pc.json5
-│   └── TR1X_gameflow_ub.json5
-├── data
-│   ├── cat.phd
-│   ├── cred0.pcx
-│   ├── cred1.pcx
-│   ├── cred2.pcx
-│   ├── cred3.pcx
-│   ├── cut1.phd
-│   ├── cut2.phd
-│   ├── cut3.phd
-│   ├── cut4.phd
-│   ├── egypt.phd
-│   ├── eidospc.pcx
-│   ├── end2.phd
-│   ├── end.pcx
-│   ├── end.phd
-│   ├── gym.phd
-│   ├── install.pcx
-│   ├── level10a.phd
-│   ├── level10b.phd
-│   ├── level10c.phd
-│   ├── level1.phd
-│   ├── level2.phd
-│   ├── level3a.phd
-│   ├── level3b.phd
-│   ├── level4.phd
-│   ├── level5.phd
-│   ├── level6.phd
-│   ├── level7a.phd
-│   ├── level7b.phd
-│   ├── level8a.phd
-│   ├── level8b.phd
-│   ├── level8c.phd
-│   ├── titleh.pcx
-│   ├── titleh_ub.pcx
-│   │── title.phd
-│   │── images
-│   │   ├── atlantis.webp
-│   │   ├── credits_1.webp
-│   │   ├── credits_2.webp
-│   │   ├── credits_3.webp
-│   │   ├── credits_3_alt.webp
-│   │   ├── credits_ps1.webp
-│   │   ├── egypt.webp
-│   │   ├── eidos.webp
-│   │   ├── end.webp
-│   │   ├── greece.webp
-│   │   ├── greece_saturn.webp
-│   │   ├── gym.webp
-│   │   ├── install.webp
-│   │   ├── peru.webp
-│   │   ├── title.webp
-│   │   ├── title_og_alt.webp
-│   │   └── title_ub.webp
-│   └── injections
-│       ├── atlantis_fd.bin
-│       ├── atlantis_textures.bin
-│       ├── backpac.bin
-│       └── etc...
-├── fmv
-│   ├── cafe.rpl
-│   ├── canyon.rpl
-│   ├── core.avi
-│   ├── end.rpl
-│   ├── escape.rpl
-│   ├── lift.rpl
-│   ├── mansion.rpl
-│   ├── prison.rpl
-│   ├── pyramid.rpl
-│   ├── snow.rpl
-│   └── vision.rpl
-├── music
-│   ├── track02.flac
-│   ├── track03.flac
-│   ├── track04.flac
-│   ├── track05.flac
-│   ├── track06.flac
-│   ├── track07.flac
-│   ├── track08.flac
-│   ├── track09.flac
-│   ├── track10.flac
-│   ├── track11.flac
-│   ├── track12.flac
-│   ├── track13.flac
-│   ├── track14.flac
-│   ├── track15.flac
-│   ├── track16.flac
-│   ├── track17.flac
-│   ├── track18.flac
-│   ├── track19.flac
-│   ├── track20.flac
-│   ├── track21.flac
-│   ├── track22.flac
-│   ├── track23.flac
-│   ├── track24.flac
-│   ├── track25.flac
-│   ├── track26.flac
-│   ├── track27.flac
-│   ├── track28.flac
-│   ├── track29.flac
-│   ├── track30.flac
-│   ├── track31.flac
-│   ├── track32.flac
-│   ├── track33.flac
-│   ├── track34.flac
-│   ├── track35.flac
-│   ├── track36.flac
-│   ├── track37.flac
-│   ├── track38.flac
-│   ├── track39.flac
-│   ├── track40.flac
-│   ├── track41.flac
-│   ├── track42.flac
-│   ├── track43.flac
-│   ├── track44.flac
-│   ├── track45.flac
-│   ├── track46.flac
-│   ├── track47.flac
-│   ├── track48.flac
-│   ├── track49.flac
-│   ├── track50.flac
-│   ├── track51.flac
-│   ├── track52.flac
-│   ├── track53.flac
-│   ├── track54.flac
-│   ├── track55.flac
-│   ├── track56.flac
-│   ├── track57.flac
-│   ├── track58.flac
-│   ├── track59.flac
-│   └── track60.flac
-├── shaders
-│   ├── 2d.glsl
-│   ├── 3d.glsl
-│   └── fbo.glsl
-├── TR1X.exe
-└── TR1X_ConfigTool.exe
-
-
+ + + -### Configuring +See [the changelog](docs/tr2/CHANGELOG.md). -To configure TR1X, run the `TR1X_ConfigTool.exe` application. All the -configuration is explained in this tool. Alternatively, after running the game -at least once, you can edit `TR1X.json5` manually in a text editor such -as Notepad. +### Install Instructions +Please refer to the [detailed documentation](docs/tr2/). -## macOS - -### Installing - -1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases -2. Download the `TR1X-Installer.dmg` installer image. Mount the image and drag TR1X to the Applications folder. -3. Run TR1X from the Applications folder. This will show you an error dialog about missing game data files. This is expected at this point, as you have not copied them in yet. However, it's important to run the app first to allow macOS to verify the app bundle's signature. -4. Find TR1X in your Applications folder. Right-click it and click "Show Package Contents". -5. Copy your Tomb Raider 1 game data files into `Contents/Resources`. (See the Windows / Linux instructions for retrieving game data from e.g. GOG.) - -If you install everything correctly, your game directory should look more or -less like this (click to expand): - -
-

* Will not be present until the game has been launched.

-
-.
-└── Contents
-    ├── _CodeSignature
-    ├── Framworks
-    ├── info.plist
-    ├── MacOS
-    └── Resources
-        ├── cfg
-        │   ├── TR1X.json5 *
-        │   ├── TR1X_gameflow.json5
-        │   ├── TR1X_gameflow_demo_pc.json5
-        │   └── TR1X_gameflow_ub.json5
-        ├── data
-        │   ├── cat.phd
-        │   ├── cred0.pcx
-        │   ├── cred1.pcx
-        │   ├── cred2.pcx
-        │   ├── cred3.pcx
-        │   ├── cut1.phd
-        │   ├── cut2.phd
-        │   ├── cut3.phd
-        │   ├── cut4.phd
-        │   ├── egypt.phd
-        │   ├── eidospc.pcx
-        │   ├── end2.phd
-        │   ├── end.pcx
-        │   ├── end.phd
-        │   ├── gym.phd
-        │   ├── install.pcx
-        │   ├── level10a.phd
-        │   ├── level10b.phd
-        │   ├── level10c.phd
-        │   ├── level1.phd
-        │   ├── level2.phd
-        │   ├── level3a.phd
-        │   ├── level3b.phd
-        │   ├── level4.phd
-        │   ├── level5.phd
-        │   ├── level6.phd
-        │   ├── level7a.phd
-        │   ├── level7b.phd
-        │   ├── level8a.phd
-        │   ├── level8b.phd
-        │   ├── level8c.phd
-        │   ├── titleh.pcx
-        │   ├── titleh_ub.pcx
-        │   │── title.phd
-        │   │── images
-        │   │   ├── atlantis.webp
-        │   │   ├── credits_1.webp
-        │   │   ├── credits_2.webp
-        │   │   ├── credits_3.webp
-        │   │   ├── credits_3_alt.webp
-        │   │   ├── credits_ps1.webp
-        │   │   ├── egypt.webp
-        │   │   ├── eidos.webp
-        │   │   ├── end.webp
-        │   │   ├── greece.webp
-        │   │   ├── greece_saturn.webp
-        │   │   ├── gym.webp
-        │   │   ├── install.webp
-        │   │   ├── peru.webp
-        │   │   ├── title.webp
-        │   │   ├── title_og_alt.webp
-        │   │   └── title_ub.webp
-        │   └── injections
-        │       ├── atlantis_fd.bin
-        │       ├── atlantis_textures.bin
-        │       ├── backpac.bin
-        │       └── etc...
-        ├── fmv
-        │   ├── cafe.rpl
-        │   ├── canyon.rpl
-        │   ├── core.avi
-        │   ├── end.rpl
-        │   ├── escape.rpl
-        │   ├── lift.rpl
-        │   ├── mansion.rpl
-        │   ├── prison.rpl
-        │   ├── pyramid.rpl
-        │   ├── snow.rpl
-        │   └── vision.rpl
-        ├── icon.icns
-        ├── music
-        │   ├── track02.flac
-        │   ├── track03.flac
-        │   ├── track04.flac
-        │   ├── track05.flac
-        │   ├── track06.flac
-        │   ├── track07.flac
-        │   ├── track08.flac
-        │   ├── track09.flac
-        │   ├── track10.flac
-        │   ├── track11.flac
-        │   ├── track12.flac
-        │   ├── track13.flac
-        │   ├── track14.flac
-        │   ├── track15.flac
-        │   ├── track16.flac
-        │   ├── track17.flac
-        │   ├── track18.flac
-        │   ├── track19.flac
-        │   ├── track20.flac
-        │   ├── track21.flac
-        │   ├── track22.flac
-        │   ├── track23.flac
-        │   ├── track24.flac
-        │   ├── track25.flac
-        │   ├── track26.flac
-        │   ├── track27.flac
-        │   ├── track28.flac
-        │   ├── track29.flac
-        │   ├── track30.flac
-        │   ├── track31.flac
-        │   ├── track32.flac
-        │   ├── track33.flac
-        │   ├── track34.flac
-        │   ├── track35.flac
-        │   ├── track36.flac
-        │   ├── track37.flac
-        │   ├── track38.flac
-        │   ├── track39.flac
-        │   ├── track40.flac
-        │   ├── track41.flac
-        │   ├── track42.flac
-        │   ├── track43.flac
-        │   ├── track44.flac
-        │   ├── track45.flac
-        │   ├── track46.flac
-        │   ├── track47.flac
-        │   ├── track48.flac
-        │   ├── track49.flac
-        │   ├── track50.flac
-        │   ├── track51.flac
-        │   ├── track52.flac
-        │   ├── track53.flac
-        │   ├── track54.flac
-        │   ├── track55.flac
-        │   ├── track56.flac
-        │   ├── track57.flac
-        │   ├── track58.flac
-        │   ├── track59.flac
-        │   └── track60.flac
-        └── shaders
-            ├── 2d.glsl
-            ├── 3d.glsl
-            └── fbo.glsl
-
-
- -## Improvements over original game - -Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for details. - -#### UI -- added proper UI and bar scaling -- added enemy health bars -- added PS1 style UI -- added fade effects to displayed images -- added an option to use PS1 loading screens -- added a wireframe mode -- improved support for windowed mode - -#### Gameplay -- added ability to set user-defined FOV -- added ability to select weapons / using items with numeric keys -- added ability to look around while running -- added ability to forward and backward jump while looking -- added ability to look up and down while hanging -- added ability to sidestep like in TR3 -- added ability to jump-twist and somersault like in TR2+ -- added ability to cancel ledge-swinging animation like in TR2+ -- added ability to jump at any point while running like in TR2+ -- added ability to automatically walk to items when nearby -- added ability to roll while underwater like in TR2+ -- added ability to use Lara's underwater swimming physics from TR2+ -- added a pause screen -- added a choice whether to play NG or NG+ without having to play the entire game -- added Japanese mode (guns deal twice the damage, inspired by JP release of TR3); available for both NG and NG+ -- added ability to restart level on death -- added ability to restart the adventure from any level when loading a game -- added the "Story so far..." option in the select level menu to view cutscenes and FMVs -- added graphics effects, lava emitters, flame emitters, and waterfalls to the savegame so they now persist on load -- added an option to restore the mummy in City of Khamoon room 25, similar to the PS version -- added a flag indicating if new game plus is unlocked to the player config which allows the player to select new game plus or not when making a new game -- added weapons to Lara's empty holsters on pickup -- added options to quiet or mute music while underwater -- changed weapon pickup behavior when unarmed to set any weapon as the default weapon, not just pistols -- fixed keys and items not working when drawing guns immediately after using them -- fixed counting the secret in The Great Pyramid -- fixed running out of ammo forcing Lara to equip pistols even if she doesn't carry them -- fixed a crash when Lara is on fire and goes too far away from where she caught fire -- fixed flames not being drawn when Lara is on fire and leaves the room where she caught fire -- fixed settings not being saved when exiting the game with Alt+F4 -- fixed settings not persisting chosen layout (default vs. user keys) -- fixed the infamous Tihocan crocodile bug (integer overflow causing creatures to deal damage across the entire level) -- fixed missiles damaging Lara when she is far beyond their damage range -- fixed Lara not being able to grab parts of some bridges -- fixed Lara voiding if a badly placed timed door closes on her (doesn't occur in OG levels) -- fixed bats being positioned too high -- fixed alligators dealing no damage if Lara remains still in the water -- fixed shotgun shooting when a locked target moves out of Lara's sight -- fixed shotgun shooting too fast when not aiming at a target -- fixed Lara grabbing ledges she shouldn't in stacked rooms (mainly St. Francis Folly tower) -- fixed rare cases of Lara getting set on fire on a bridge over lava -- fixed saving the game near Bacon Lara breaking her movement -- fixed Lara glitching through static objects into a black void -- fixed Lara pushing blocks through doors -- fixed Lara switching to pistols when completing a level with other guns -- fixed empty mutant shells in Unfinished Business spawning Lara's hips -- fixed gun pickups disappearing in rare circumstances on save load (#406) -- fixed broken dart ricochet effect -- fixed exploded mutant pods sometimes appearing unhatched on reload -- fixed bridges at floor level appearing under the floor -- fixed underwater currents breaking in rare cases -- fixed Lara loading inside a movable block if she's on a stack near a room portal -- fixed a game crash on shutdown if the action button is held down -- fixed Scion 1 respawning on load -- fixed triggered flip effects not working if there are no sound devices -- fixed ceiling heights at times being miscalculated, resulting in camera issues and Lara being able to jump into the ceiling -- fixed the camera being thrown through doors for one frame when looked at from fixed camera positions -- fixed the ape not performing the vault animation when climbing -- fixed Natla's gun moving while she is in her semi death state -- fixed the bear pat attack so it does not miss Lara -- fixed dead centaurs exploding again after saving and reloading -- fixed the following floor data issues: - - **St. Francis' Folly**: moved the music trigger for track 3 in room 4 behind the Neptune door, and restored track 15 to play after using the 4 keys - - **The Cistern**: missing trigger in room 56 which could result in a softlock - - **Tomb of Tihocan**: missing trigger in room 62 for enemy 34 - - **City of Khamoon**: incorrect trapdoor trigger types in rooms 31 and 34 - - **Obelisk of Khamoon**: missing switch trigger type in room 66 - - **Natla's Mines**: incorrect flipmap indices in room 85 - - **Atlantean Stronghold**: fixed poorly configured portals between rooms 74 and 12 -- fixed various bugs with falling movable blocks -- fixed bugs when trying to stack multiple movable blocks -- fixed Midas's touch having unrestricted vertical range -- fixed Lara saying "no" when taking valid actions in front of a key item receptacle -- fixed Lara not saying "no" when using the Scion incorrectly -- fixed flickering in bats' death animations and rapid shooting if Lara continues to fire when they are killed -- fixed looking forward too far causing an upside down camera frame -- fixed the Scion being extremely difficult to shoot with the shotgun -- fixed collision issues with drawbridges, trapdoors, and bridges when stacked over each other, over slopes, and near the ground -- fixed a potential softlock when killing the Torso boss in Great Pyramid - -#### Cheats -- added a fly cheat -- added a level skip cheat -- added a door open cheat (while in fly mode) -- added a cheat to increase the game speed -- added a cheat to explode Lara like in TR2 and TR3 - -#### Input -- added ability to move camera around with W,A,S,D -- added additional custom control schemes -- added the ability to unbind unessential keys -- added the ability to reset control schemes to default -- added customizable controller support -- added an inverted look camera option -- added the ability to move the look camera while targeting an enemy in combat -- fixed freeze when holding the Action key during end of level -- fixed inability to switch Control keys when shimmying -- fixed setting user keys being very difficult -- fixed skipping FMVs triggering inventory -- fixed skipping credits working too fast -- fixed not being able to close level stats with Escape -- fixed Lara jumping forever when alt+tabbing out of the game -- stopped the default controls from functioning when the user unbound them -- added the option to change weapon targets by tapping the new target change key -- added three targeting lock options: - - full lock: always keep target lock even if the enemy moves out of sight or dies (OG TR1) - - semi lock: keep target lock if the enemy moves out of sight but lose target lock if the enemy dies - - no lock: lose target lock if the enemy goes out of sight or dies (TR4+) - -#### Statistics -- added ability to keep timer on in inventory -- added optional compass level stats -- added optional final statistics screen -- added optional deaths counter -- added optional total pickups and kills per level -- added unobtainable pickups, kills, and secrets stats support in the gameflow - -#### Visuals -- added optional shotgun flash sprites -- added optional rendering of pickups on the ground as 3D meshes -- added Lara's braid to each level -- added support for displaying more than 3 pickup sprites -- added more control over when to show health bar and air bar -- added customizable health bar and air bar -- added rounded shadows (instead of the default octagon) -- added adjustable in-game brightness -- added support for HD FMVs -- added fanmade 16:9 menu backgrounds -- added optional fade effects -- added a vsync option -- added contextual arrows to menu options -- added support for animated room sprites, which also restores intended behavior in, for example, The Cistern room 0 -- added skybox support, with a default option provided for Lost Valley, Colosseum and Obelisk of Khamoon; custom level builders can use object slot `184` -- added reflections of Midas Hand death animation and savegame crystals -- changed the Scion in The Great Pyramid from spawning blood when hit to a richochet effect -- fixed thin black lines between polygons -- fixed black screen flashing when navigating the inventory -- fixed detail levels text flashing with any option change -- fixed underwater caustics animating at 2x speed -- fixed inconsistencies in some enemy textures -- fixed the animation of Lara's left arm when the shotgun is equipped -- fixed the following room texture issues: - - **Gym**: incorrect textures in room 9 - - **Caves**: an incorrect texture in room 6 and missing textures in rooms 1, 10, 14 and 30 - - **City of Vilcabamba**: an incorrect texture in room 26, and a missing texture and a stretched texture in room 15 - - **Lost Valley**: incorrect textures in rooms 6, 9, 16, 34 and 35, missing textures in rooms 6, 9, 25, 26, 27, 51, and 90, and stretched textures in room 63 - - **Tomb of Qualopec**: an incorrect and missing textures in room 8, and a misaligned texture in room 5 - - **St. Francis' Folly**: incorrect textures in rooms 1, 4, 18 and 35, and a misaligned texture in room 3 - - **Colosseum**: incorrect Midas textures appearing at the roof, incorrect textures in rooms 37, 67, 75 and 82, and missing textures in rooms 2 and 7 - - **Palace Midas**: incorrect textures in rooms 31, 34, 40 and 45, missing textures in rooms 2, 5, 9, 13, 30, and 53, and stretched textures in rooms 7 and 20 - - **The Cistern**: missing textures in rooms 3 and 9 and a stretched texture in room 102 - - **Tomb of Tihocan**: incorrect textures in rooms 75 and 89 and a misaligned texture in room 104 - - **City of Khamoon**: incorrect textures in rooms 47, 48, 51, 60 and 64, and a missing texture in room 58 - - **Obelisk of Khamoon**: incorrect textures in rooms 22, 23, 42 and 65; added shading to the gaps into City of Khamoon in rooms 8 and 20/21 - - **Sanctuary of the Scion**: missing textures in rooms 1, 11, 21, 52, 53, and 54 - - **Natla's Mines**: a missing texture in room 35, overlapping textures in room 55, an incorrect texture in room 69, and stretched textures in rooms 23 and 24 - - **Pre-Atlantis Cutscene**: stretched textures in rooms 6 and 21 - - **Atlantis**: incorrect textures in rooms 5, 18, 36, 43, 50, 52, 53, 54, 58, 78, 85 and 87, a missing texture in room 27, and stretched textures in rooms 13, 49 and 50 - - **Atlantis Cutscene**: incorrect and stretched textures in room 16 - - **The Great Pyramid**: incorrect textures in rooms 2, 5, 31, 36, 50, 52, 53, 54, 65 and 66, missing textures in rooms 21, 25, 26, and 66, and stretched textures in rooms 49 and 50 - - **Return to Egypt**: incorrect textures in rooms 46 and 47, a missing texture in room 98, and a stretched texture in room 47 - - **Temple of the Cat**: incorrect textures in rooms 50, 70, 71, 76, 78, 87 and 96, and a missing texture in 75 - - **Atlantean Stronghold**: incorrect textures in rooms 2, 6, 7 and 75, and missing textures in rooms 5, 13, 19, 63 and 74 - - **The Hive**: incorrect textures in room 8, 13 and 18 -- improved vertex movement when looking through water portals - -#### Audio -- added music during the credits -- added an option to turn off sound effect pitching -- added an option to use the PlayStation Uzi sound effects -- added the current music track and timestamp to the savegame so they now persist on load -- added the triggered music tracks to the savegame so one shot tracks don't replay on load -- added detection for animation commands to play SFX on land, water or both -- fixed the sound of collecting a secret killing the music -- fixed audio mixer stopping playing sounds on big explosions -- fixed game audio not muting when game is minimized -- fixed underwater ambient sound effect not playing -- fixed sound effects playing rapidly in sound menu if input held down -- fixed sounds stopping instead of pausing when using the inventory or pausing -- fixed the following music triggers: - - **Caves**: converted track 9 in room 34 to one shot - - **Tomb of Qualopec**: converted track 17 in room 25 to one shot - - **St. Francis' Folly**: converted track 7 in room 18 to one shot - - **Obelisk of Khamoon**: converted track 3 in room 12 and track 4 in room 32 to one shot - - **Sanctuary of the Scion**: converted track 10 in room 0 to one shot - - **Natla's Mines**: converted track 3 in room 86 to one shot - - **Atlantis**: converted track 8 in room 59 to one shot - - **The Great Pyramid**: converted track 8 in room 36 to one shot - - **Return to Egypt**: converted track 19 in room 0, track 14 in room 15, track 15 in room 19, track 16 in room 22, track 6 in room 61, and track 11 in room 93 to one shot - - **Temple of the Cat**: converted track 12 in room 14, track 7 in room 98, and track 20 in room 100 to one shot - - **Atlantean Stronghold**: converted track 20 in room 4, track 19 in room 13, track 11 in room 17, track 15 in room 20, and track 12 in room 25 to one shot - - **The Hive**: converted track 9 in room 8, track 6 in room 18, track 12 in room 30, track 18 in room 31, track 3 in room 32, and track 20 in room 35 to one shot - -#### Mods -- added developer console (accessible with `/`, see [COMMANDS.md] for details) -- added ability to adjust Lara's starting health (easy no damage mod) -- added ability to disable healing between levels -- added ability to disable certain item pickups (all medpacks, shotgun, Magnums and/or UZIs) -- added ability to disable main menu demos, FMVs and/or cutscenes -- added external game flow (no longer 2 different .exes for TR1 and TR1UB). Refer to [GAMEFLOW.md](GAMEFLOW.md) for details -- added automatic calculation of secret counts (no longer having to fiddle with the .exe to get correct secret stats) -- added save game crystals game mode (enabled via gameflow) -- added per-level customizable water color (with customizable blue component) -- added per-level customizable fog distance -- added deadly water feature from TR2+ - -#### Miscellaneous -- added Linux builds -- added macOS builds -- added .jpeg/.png screenshots -- added an option to pause sound in the inventory screen -- added ability to skip FMVs with the Action key -- added ability to make freshly triggered (runaway) Pierre replace an already existing (runaway) Pierre -- expanded internal game memory limit from 3.5 MB to 16 MB -- expanded moveable limit from 256 to 10240 -- expanded maximum textures from 2048 to 8192 -- expanded maximum texture pages from 32 to 128 -- expanded maximum vertices of a single drawable object from 1500 to unlimited -- expanded the number of visible enemies from 8 to 32 -- ported audio decoding library to ffmpeg -- ported video decoding library to ffmpeg -- ported image decoding library to ffmpeg -- ported audio output library to SDL -- ported input method to SDL -- changed saves to be put in the saves/ directory -- fixed playing the secret sound in Tomb of Tihocan -- fixed reading user settings not restoring the volume ## Q&A 1. **Is the game fully playable from beginning to the end?** - Yes. If you encounter a bug, please file a ticket. + By all means! If you encounter a bug, please file a ticket. 2. **Can we get HD textures? Reflections? Other visual updates?** - Eventually, probably yes, but we'd really appreciate help with these. - -3. **Can we get braid in every level? Skyboxes? Flyby cameras? New animations? etc.** - - The difficulty here is that these features often require inserting a - completely new animation, a textured mesh or a sound file and pretend - they're always been a part of the original game. Work is underway on an - injection framework, and the braid is now supported in each level. + We hope so! Being able to introduce skyboxes to TR1 showed that quite + literally sky is the limit. But great stuff takes time. 4. **Can I play this on Mac, Linux, Android...?** - Currently supported platforms include Windows, Linux and macOS. In the - future, it might be possible to run the game on Android as well – - contributions are welcome! + Currently supported platforms include Windows, Linux and macOS for TR1X, + and Windows for TR2X. 5. **What's the relation to TR2Main?** - Initially established as TR1Main in 2021, our project's development paths - deviated, leading us to recognize the need for a distinct name. As a - result, we rebranded the project as Tomb1Main. However, to further - differentiate ourselves, we underwent another rebranding in 2023, - ultimately adopting the name TR1X. TR2Main is a separate project with its - own unique trajectory and not directly related to our development efforts. - -## License - -This project is licensed under the GNU General Public License - see the -[COPYING.md](COPYING.md) file for details. - -## Copyright - -(c) 2021 Marcin Kurczewski. All rights reserved. Original game is created by -Core Design Ltd. in 1996. Lara Croft and Tomb Raider are trademarks of Square -Enix Ltd. Title image by Kidd Bowyer. Loading screens and high quality images -by goblan_oldnewpixel and Posix. + Originally founded as TR1Main in 2021, our project flourished independently + without sharing the code, with the shared brand concept existing only as an + idea. To better represent this, we rebranded to Tomb1Main. In 2023, we + further refined our identity by adopting the name TR1X. Meanwhile, TR2Main + follows a completely separate and unique path, unconnected to our + development work. diff --git a/data/download_tr1x.svg b/data/download_tr1x.svg new file mode 100644 index 000000000..cb8ae6e39 --- /dev/null +++ b/data/download_tr1x.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Download +TR1X + diff --git a/data/download_tr2x.svg b/data/download_tr2x.svg new file mode 100644 index 000000000..e44e4f7ab --- /dev/null +++ b/data/download_tr2x.svg @@ -0,0 +1,13 @@ + + + + + + + + + + +Download +TR2X + diff --git a/data/tr1/ship/cfg/TR1X_gameflow.json5 b/data/tr1/ship/cfg/TR1X_gameflow.json5 index 64d3e0291..b9556f847 100644 --- a/data/tr1/ship/cfg/TR1X_gameflow.json5 +++ b/data/tr1/ship/cfg/TR1X_gameflow.json5 @@ -1,7 +1,7 @@ { // NOTE: bad changes to this file may result in crashes. // Lines starting with double slashes are comments and are ignored. - // Refer to https://github.com/LostArtefacts/TR1X/blob/stable/GAMEFLOW.md + // Refer to https://github.com/LostArtefacts/TRX/blob/stable/docs/tr1/GAMEFLOW.md // for usage. "main_menu_picture": "data/images/title.webp", diff --git a/data/tr1/ship/cfg/TR1X_gameflow_ub.json5 b/data/tr1/ship/cfg/TR1X_gameflow_ub.json5 index cfcfc7708..65427cfb9 100644 --- a/data/tr1/ship/cfg/TR1X_gameflow_ub.json5 +++ b/data/tr1/ship/cfg/TR1X_gameflow_ub.json5 @@ -1,7 +1,7 @@ { // NOTE: bad changes to this file may result in crashes. // Lines starting with double slashes are comments and are ignored. - // Refer to https://github.com/LostArtefacts/TR1X/blob/stable/GAMEFLOW.md + // Refer to https://github.com/LostArtefacts/TRX/blob/stable/docs/tr1/GAMEFLOW.md // for usage. "main_menu_picture": "data/images/title_ub.webp", diff --git a/CONTRIBUTING.md b/docs/CONTRIBUTING.md similarity index 83% rename from CONTRIBUTING.md rename to docs/CONTRIBUTING.md index 2faca56b3..0c74398e2 100644 --- a/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -2,17 +2,9 @@ ## Build workflow -Initial build: - - Compile the project (described in the next section) -- Copy all executable files from `build/` to your game directory -- Copy the contents of `data/tr1/ship/` to your game directory - -Subsequent builds: - -- Compile the project -- Copy all executable files from `build/` to your game directory - (we recommend making a script file to do this) +- Copy all .dll and .exe files from `build/` to your game directory +- Copy the contents of `data/…/ship/` to your game directory @@ -22,14 +14,16 @@ Subsequent builds: - **With Docker**: - Make sure to install Docker and [just](https://github.com/casey/just), then - run `just`. The binaries should appear in the `build/` directory. - To see list of possible build targets, run `just -l`. + Make sure to install Docker and [just](https://github.com/casey/just). + To see the list of all possible build targets, run `just -l`. To build the + images, use the `just *-build-*` commands relevant to the game and platform + you want to build for. The binaries should appear in the `build/` + directory. - **Without Docker**: This scenario is not officially supported, but you can see how it's done by - examining the files in the `tools/docker/` directory for the external + examining the files in the `tools/*/docker/` directory for the external dependencies and `meson.build` for the local files, then tailoring your system to match the process. @@ -53,8 +47,7 @@ Subsequent builds: Please be advised that any build systems that are not the one we use for automating releases (= mingw-w64) come at user's own risk. They might crash or -even refuse to compile. Pull requests are welcome, but those other toolchains -will be always considered supplementary. +even refuse to compile. @@ -100,8 +93,9 @@ guidelines: - Variables are `lower_snake_case` - Global variables are `g_PascalCase` -- Module variables are `m_PascalCase` +- Module-scoped global variables are `m_PascalCase` and static - Function names are `Module_PascalCase` +- Module-scoped function names are `M_PascalCase` and static - Macros are `UPPER_SNAKE_CASE` - Struct names are `UPPER_SNAKE_CASE` - Struct members are `lower_snake_case` @@ -151,29 +145,27 @@ a review from the interested parties. ### Changelog -We keep a changelog in `CHANGELOG.md`. Anything other than an internal change +We keep a changelog for each game in a relevant `CHANGELOG.md` file. Anything other than an internal change or refactor needs an entry there. Furthermore, new features and OG bugfixes -should be documented in README as well. If your change modifies gameflow -behavior, make sure to update `GAMEFLOW.md` as appropriate. +should be documented in the `README.md` file as well. ### Commit scope -Either you can make a lot of throwaway commits such as 'test' 'continue -testing' 'fix 1' 'fix 2' 'fix of the fix' and then squash your pull request as -a whole, or you can craft a nice history with proper commit messages and then -merge-rebase. The first case is mostly acceptable for isolated features, but in -general we prefer the latter approach. As a principle, refactors should made in -separate commits. Code review changes are best made incrementally and then -squashed prior to merging, for easing the review process. +There are two options for handling commits. One approach involves making +temporary commits with messages like 'test,' 'continue testing,' 'fix 1,' 'fix +2,' and 'fix of the fix,' followed by squashing all of them when creating a +pull request. The other approach is to maintain a clean commit history with +meaningful messages and use merge-rebase. While the first approach can be +suitable for isolated features, the latter is generally preferred. ### Commit messages **The most important thing to remember:** bug fixes and feature implementations should always include the phrase `Resolves #123`. If there's no ticket and the -pull request you're making contains player-facing changes, a ticket needs -to be created first – no exceptions. +submitted pull request contains player-facing changes, a ticket needs to be +created first. Anything else is just for consistency and general neatness. Our commit messages aim to respect the 50/72 rule and have the following form: @@ -259,7 +251,7 @@ released ahead of unpublished work in `develop` are merged directly to ### Tooling We try to code all of our internal tools in a reasonably recent version of -Python. Avoid bash, shell and other similar languages. +Python and tend to avoid bash, shell and other similar languages. ### Releasing a new version @@ -273,13 +265,16 @@ a specific version. See git history for details. ## Glossary + - Tomb1Main: the previous name of this project - T1M: short hand of Tomb1Main - OG: original game, most often TombATI -- PS: PlayStation version of the game - Vole: a rat that swims - Pod: a mutant egg (including the big egg) - Cabin: the room with the pistols from Natla's Mines - Statue: centaur statues from the entrance of Tihocan's Tomb - Bacon Lara: the doppelgänger Lara in the Atlantis level - Torso/Adam: the big boss mutant from The Great Pyramid level +- UK Box: the version of TR2 released on discs in the UK +- Multipatch: the version of TR2 released on Steam +- PS: PlayStation version of the game diff --git a/CHANGELOG.md b/docs/tr1/CHANGELOG.md similarity index 99% rename from CHANGELOG.md rename to docs/tr1/CHANGELOG.md index 4aafd8018..d4de27c27 100644 --- a/CHANGELOG.md +++ b/docs/tr1/CHANGELOG.md @@ -1,4 +1,4 @@ -## [Unreleased](https://github.com/LostArtefacts/TR1X/compare/stable...develop) - ××××-××-×× +## [Unreleased](https://github.com/LostArtefacts/TRX/compare/stable...develop) - ××××-××-×× - added `/sfx` command - added `/nextlevel` alias to `/endlevel` console command - added `/quit` alias to `/exit` console command diff --git a/COMMANDS.md b/docs/tr1/COMMANDS.md similarity index 100% rename from COMMANDS.md rename to docs/tr1/COMMANDS.md diff --git a/GAMEFLOW.md b/docs/tr1/GAMEFLOW.md similarity index 99% rename from GAMEFLOW.md rename to docs/tr1/GAMEFLOW.md index a9cdad2dd..2819c7ad5 100644 --- a/GAMEFLOW.md +++ b/docs/tr1/GAMEFLOW.md @@ -1412,6 +1412,14 @@ provided with the game achieves. PS1 version but not the PC. + + + explosion.bin + + + Injects explosion sprites for certain console commands. + + lara_animations.bin diff --git a/docs/tr1/README.md b/docs/tr1/README.md new file mode 100644 index 000000000..576dcc176 --- /dev/null +++ b/docs/tr1/README.md @@ -0,0 +1,604 @@ +

+TR1X logo +TR1X logo +

+ +## Windows / Linux + +### Installing (simplified) + +1. Head over to GitHub releases: https://github.com/LostArtefacts/TRX/releases +2. Download the TR1X installer. Your browser may complain that the .exe is unsafe, but it's OK to ignore this alert. +3. Mark the installer EXE as safe to run by right-clicking on the .exe, going to properties and clicking "Unblock". +4. Run the installer and proceed with the steps. + +We hope that eventually these alerts will go away as the popularity of the project rises. + +### Installing (advanced / manual) + +1. Head over to GitHub releases: https://github.com/LostArtefacts/TRX/releases +2. Download the TR1X zip file. +3. Extract the zip file into a directory of your choice. + Make sure you choose to overwrite existing directories and files. +4. (First time installation) Put your original game files into the target directory. + 1. For Steam and GOG users, extract the original `GAME.BIN` file using a tool such as UltraISO to your target directory. + Note that neither the GOG nor the Steam releases ship the music files. You have a few options here: + - You can download the music files from the link below. + https://lostartefacts.dev/aux/tr1x/music.zip + The legality of this approach is disputable. + - Rip the assets yourself from a physical PlayStation/SegaSaturn disk. + + Optionally you can also install the Unfinished Business expansion pack files. + - Either one of these these variants: + - https://lostartefacts.dev/aux/tr1x/trub-music.zip (fan-made patch to include music triggers) + - https://lostartefacts.dev/aux/tr1x/trub-vanilla.zip (original level files, which do not include music triggers) + - Or the more manual link: https://archive.org/details/tomb-raider-i-unfinished-business-pc-eng-full-version_20201225 + 2. For TombATI users this means copying the `data`, `fmv` and `music` directories. +5. To play the game, run `TR1X.exe`. +6. To play the Unfinished Expansion pack, run `TR1X.exe -gold`. + +If you install everything correctly, your game directory should look more or +less like this (click to expand): + +
+

* Will not be present until the game has been launched.

+
+.
+├── cfg
+│   ├── TR1X.json5 *
+│   ├── TR1X_gameflow.json5
+│   ├── TR1X_gameflow_demo_pc.json5
+│   └── TR1X_gameflow_ub.json5
+├── data
+│   ├── cat.phd
+│   ├── cred0.pcx
+│   ├── cred1.pcx
+│   ├── cred2.pcx
+│   ├── cred3.pcx
+│   ├── cut1.phd
+│   ├── cut2.phd
+│   ├── cut3.phd
+│   ├── cut4.phd
+│   ├── egypt.phd
+│   ├── eidospc.pcx
+│   ├── end2.phd
+│   ├── end.pcx
+│   ├── end.phd
+│   ├── gym.phd
+│   ├── install.pcx
+│   ├── level10a.phd
+│   ├── level10b.phd
+│   ├── level10c.phd
+│   ├── level1.phd
+│   ├── level2.phd
+│   ├── level3a.phd
+│   ├── level3b.phd
+│   ├── level4.phd
+│   ├── level5.phd
+│   ├── level6.phd
+│   ├── level7a.phd
+│   ├── level7b.phd
+│   ├── level8a.phd
+│   ├── level8b.phd
+│   ├── level8c.phd
+│   ├── titleh.pcx
+│   ├── titleh_ub.pcx
+│   │── title.phd
+│   │── images
+│   │   ├── atlantis.webp
+│   │   ├── credits_1.webp
+│   │   ├── credits_2.webp
+│   │   ├── credits_3.webp
+│   │   ├── credits_3_alt.webp
+│   │   ├── credits_ps1.webp
+│   │   ├── egypt.webp
+│   │   ├── eidos.webp
+│   │   ├── end.webp
+│   │   ├── greece.webp
+│   │   ├── greece_saturn.webp
+│   │   ├── gym.webp
+│   │   ├── install.webp
+│   │   ├── peru.webp
+│   │   ├── title.webp
+│   │   ├── title_og_alt.webp
+│   │   └── title_ub.webp
+│   └── injections
+│       ├── atlantis_fd.bin
+│       ├── atlantis_textures.bin
+│       ├── backpac.bin
+│       └── etc...
+├── fmv
+│   ├── cafe.rpl
+│   ├── canyon.rpl
+│   ├── core.avi
+│   ├── end.rpl
+│   ├── escape.rpl
+│   ├── lift.rpl
+│   ├── mansion.rpl
+│   ├── prison.rpl
+│   ├── pyramid.rpl
+│   ├── snow.rpl
+│   └── vision.rpl
+├── music
+│   ├── track02.flac
+│   ├── track03.flac
+│   ├── track04.flac
+│   ├── track05.flac
+│   ├── track06.flac
+│   ├── track07.flac
+│   ├── track08.flac
+│   ├── track09.flac
+│   ├── track10.flac
+│   ├── track11.flac
+│   ├── track12.flac
+│   ├── track13.flac
+│   ├── track14.flac
+│   ├── track15.flac
+│   ├── track16.flac
+│   ├── track17.flac
+│   ├── track18.flac
+│   ├── track19.flac
+│   ├── track20.flac
+│   ├── track21.flac
+│   ├── track22.flac
+│   ├── track23.flac
+│   ├── track24.flac
+│   ├── track25.flac
+│   ├── track26.flac
+│   ├── track27.flac
+│   ├── track28.flac
+│   ├── track29.flac
+│   ├── track30.flac
+│   ├── track31.flac
+│   ├── track32.flac
+│   ├── track33.flac
+│   ├── track34.flac
+│   ├── track35.flac
+│   ├── track36.flac
+│   ├── track37.flac
+│   ├── track38.flac
+│   ├── track39.flac
+│   ├── track40.flac
+│   ├── track41.flac
+│   ├── track42.flac
+│   ├── track43.flac
+│   ├── track44.flac
+│   ├── track45.flac
+│   ├── track46.flac
+│   ├── track47.flac
+│   ├── track48.flac
+│   ├── track49.flac
+│   ├── track50.flac
+│   ├── track51.flac
+│   ├── track52.flac
+│   ├── track53.flac
+│   ├── track54.flac
+│   ├── track55.flac
+│   ├── track56.flac
+│   ├── track57.flac
+│   ├── track58.flac
+│   ├── track59.flac
+│   └── track60.flac
+├── shaders
+│   ├── 2d.glsl
+│   ├── 3d.glsl
+│   └── fbo.glsl
+├── TR1X.exe
+└── TR1X_ConfigTool.exe
+
+
+ +### Configuring + +To configure TR1X, run the `TR1X_ConfigTool.exe` application. All the +configuration is explained in this tool. Alternatively, after running the game +at least once, you can edit `TR1X.json5` manually in a text editor such +as Notepad. + +## macOS + +### Installing + +1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases +2. Download the `TR1X-Installer.dmg` installer image. Mount the image and drag TR1X to the Applications folder. +3. Run TR1X from the Applications folder. This will show you an error dialog about missing game data files. This is expected at this point, as you have not copied them in yet. However, it's important to run the app first to allow macOS to verify the app bundle's signature. +4. Find TR1X in your Applications folder. Right-click it and click "Show Package Contents". +5. Copy your Tomb Raider 1 game data files into `Contents/Resources`. (See the Windows / Linux instructions for retrieving game data from e.g. GOG.) + +If you install everything correctly, your game directory should look more or +less like this (click to expand): + +
+

* Will not be present until the game has been launched.

+
+.
+└── Contents
+    ├── _CodeSignature
+    ├── Framworks
+    ├── info.plist
+    ├── MacOS
+    └── Resources
+        ├── cfg
+        │   ├── TR1X.json5 *
+        │   ├── TR1X_gameflow.json5
+        │   ├── TR1X_gameflow_demo_pc.json5
+        │   └── TR1X_gameflow_ub.json5
+        ├── data
+        │   ├── cat.phd
+        │   ├── cred0.pcx
+        │   ├── cred1.pcx
+        │   ├── cred2.pcx
+        │   ├── cred3.pcx
+        │   ├── cut1.phd
+        │   ├── cut2.phd
+        │   ├── cut3.phd
+        │   ├── cut4.phd
+        │   ├── egypt.phd
+        │   ├── eidospc.pcx
+        │   ├── end2.phd
+        │   ├── end.pcx
+        │   ├── end.phd
+        │   ├── gym.phd
+        │   ├── install.pcx
+        │   ├── level10a.phd
+        │   ├── level10b.phd
+        │   ├── level10c.phd
+        │   ├── level1.phd
+        │   ├── level2.phd
+        │   ├── level3a.phd
+        │   ├── level3b.phd
+        │   ├── level4.phd
+        │   ├── level5.phd
+        │   ├── level6.phd
+        │   ├── level7a.phd
+        │   ├── level7b.phd
+        │   ├── level8a.phd
+        │   ├── level8b.phd
+        │   ├── level8c.phd
+        │   ├── titleh.pcx
+        │   ├── titleh_ub.pcx
+        │   │── title.phd
+        │   │── images
+        │   │   ├── atlantis.webp
+        │   │   ├── credits_1.webp
+        │   │   ├── credits_2.webp
+        │   │   ├── credits_3.webp
+        │   │   ├── credits_3_alt.webp
+        │   │   ├── credits_ps1.webp
+        │   │   ├── egypt.webp
+        │   │   ├── eidos.webp
+        │   │   ├── end.webp
+        │   │   ├── greece.webp
+        │   │   ├── greece_saturn.webp
+        │   │   ├── gym.webp
+        │   │   ├── install.webp
+        │   │   ├── peru.webp
+        │   │   ├── title.webp
+        │   │   ├── title_og_alt.webp
+        │   │   └── title_ub.webp
+        │   └── injections
+        │       ├── atlantis_fd.bin
+        │       ├── atlantis_textures.bin
+        │       ├── backpac.bin
+        │       └── etc...
+        ├── fmv
+        │   ├── cafe.rpl
+        │   ├── canyon.rpl
+        │   ├── core.avi
+        │   ├── end.rpl
+        │   ├── escape.rpl
+        │   ├── lift.rpl
+        │   ├── mansion.rpl
+        │   ├── prison.rpl
+        │   ├── pyramid.rpl
+        │   ├── snow.rpl
+        │   └── vision.rpl
+        ├── icon.icns
+        ├── music
+        │   ├── track02.flac
+        │   ├── track03.flac
+        │   ├── track04.flac
+        │   ├── track05.flac
+        │   ├── track06.flac
+        │   ├── track07.flac
+        │   ├── track08.flac
+        │   ├── track09.flac
+        │   ├── track10.flac
+        │   ├── track11.flac
+        │   ├── track12.flac
+        │   ├── track13.flac
+        │   ├── track14.flac
+        │   ├── track15.flac
+        │   ├── track16.flac
+        │   ├── track17.flac
+        │   ├── track18.flac
+        │   ├── track19.flac
+        │   ├── track20.flac
+        │   ├── track21.flac
+        │   ├── track22.flac
+        │   ├── track23.flac
+        │   ├── track24.flac
+        │   ├── track25.flac
+        │   ├── track26.flac
+        │   ├── track27.flac
+        │   ├── track28.flac
+        │   ├── track29.flac
+        │   ├── track30.flac
+        │   ├── track31.flac
+        │   ├── track32.flac
+        │   ├── track33.flac
+        │   ├── track34.flac
+        │   ├── track35.flac
+        │   ├── track36.flac
+        │   ├── track37.flac
+        │   ├── track38.flac
+        │   ├── track39.flac
+        │   ├── track40.flac
+        │   ├── track41.flac
+        │   ├── track42.flac
+        │   ├── track43.flac
+        │   ├── track44.flac
+        │   ├── track45.flac
+        │   ├── track46.flac
+        │   ├── track47.flac
+        │   ├── track48.flac
+        │   ├── track49.flac
+        │   ├── track50.flac
+        │   ├── track51.flac
+        │   ├── track52.flac
+        │   ├── track53.flac
+        │   ├── track54.flac
+        │   ├── track55.flac
+        │   ├── track56.flac
+        │   ├── track57.flac
+        │   ├── track58.flac
+        │   ├── track59.flac
+        │   └── track60.flac
+        └── shaders
+            ├── 2d.glsl
+            ├── 3d.glsl
+            └── fbo.glsl
+
+
+ +## Improvements over original game + +Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for details. + +#### UI +- added proper UI and bar scaling +- added enemy health bars +- added PS1 style UI +- added fade effects to displayed images +- added an option to use PS1 loading screens +- added a wireframe mode +- improved support for windowed mode + +#### Gameplay +- added ability to set user-defined FOV +- added ability to select weapons / using items with numeric keys +- added ability to look around while running +- added ability to forward and backward jump while looking +- added ability to look up and down while hanging +- added ability to sidestep like in TR3 +- added ability to jump-twist and somersault like in TR2+ +- added ability to cancel ledge-swinging animation like in TR2+ +- added ability to jump at any point while running like in TR2+ +- added ability to automatically walk to items when nearby +- added ability to roll while underwater like in TR2+ +- added ability to use Lara's underwater swimming physics from TR2+ +- added a pause screen +- added a choice whether to play NG or NG+ without having to play the entire game +- added Japanese mode (guns deal twice the damage, inspired by JP release of TR3); available for both NG and NG+ +- added ability to restart level on death +- added ability to restart the adventure from any level when loading a game +- added the "Story so far..." option in the select level menu to view cutscenes and FMVs +- added graphics effects, lava emitters, flame emitters, and waterfalls to the savegame so they now persist on load +- added an option to restore the mummy in City of Khamoon room 25, similar to the PS version +- added a flag indicating if new game plus is unlocked to the player config which allows the player to select new game plus or not when making a new game +- added weapons to Lara's empty holsters on pickup +- added options to quiet or mute music while underwater +- changed weapon pickup behavior when unarmed to set any weapon as the default weapon, not just pistols +- fixed keys and items not working when drawing guns immediately after using them +- fixed counting the secret in The Great Pyramid +- fixed running out of ammo forcing Lara to equip pistols even if she doesn't carry them +- fixed a crash when Lara is on fire and goes too far away from where she caught fire +- fixed flames not being drawn when Lara is on fire and leaves the room where she caught fire +- fixed settings not being saved when exiting the game with Alt+F4 +- fixed settings not persisting chosen layout (default vs. user keys) +- fixed the infamous Tihocan crocodile bug (integer overflow causing creatures to deal damage across the entire level) +- fixed missiles damaging Lara when she is far beyond their damage range +- fixed Lara not being able to grab parts of some bridges +- fixed Lara voiding if a badly placed timed door closes on her (doesn't occur in OG levels) +- fixed bats being positioned too high +- fixed alligators dealing no damage if Lara remains still in the water +- fixed shotgun shooting when a locked target moves out of Lara's sight +- fixed shotgun shooting too fast when not aiming at a target +- fixed Lara grabbing ledges she shouldn't in stacked rooms (mainly St. Francis Folly tower) +- fixed rare cases of Lara getting set on fire on a bridge over lava +- fixed saving the game near Bacon Lara breaking her movement +- fixed Lara glitching through static objects into a black void +- fixed Lara pushing blocks through doors +- fixed Lara switching to pistols when completing a level with other guns +- fixed empty mutant shells in Unfinished Business spawning Lara's hips +- fixed gun pickups disappearing in rare circumstances on save load (#406) +- fixed broken dart ricochet effect +- fixed exploded mutant pods sometimes appearing unhatched on reload +- fixed bridges at floor level appearing under the floor +- fixed underwater currents breaking in rare cases +- fixed Lara loading inside a movable block if she's on a stack near a room portal +- fixed a game crash on shutdown if the action button is held down +- fixed Scion 1 respawning on load +- fixed triggered flip effects not working if there are no sound devices +- fixed ceiling heights at times being miscalculated, resulting in camera issues and Lara being able to jump into the ceiling +- fixed the camera being thrown through doors for one frame when looked at from fixed camera positions +- fixed the ape not performing the vault animation when climbing +- fixed Natla's gun moving while she is in her semi death state +- fixed the bear pat attack so it does not miss Lara +- fixed dead centaurs exploding again after saving and reloading +- fixed the following floor data issues: + - **St. Francis' Folly**: moved the music trigger for track 3 in room 4 behind the Neptune door, and restored track 15 to play after using the 4 keys + - **The Cistern**: missing trigger in room 56 which could result in a softlock + - **Tomb of Tihocan**: missing trigger in room 62 for enemy 34 + - **City of Khamoon**: incorrect trapdoor trigger types in rooms 31 and 34 + - **Obelisk of Khamoon**: missing switch trigger type in room 66 + - **Natla's Mines**: incorrect flipmap indices in room 85 + - **Atlantean Stronghold**: fixed poorly configured portals between rooms 74 and 12 +- fixed various bugs with falling movable blocks +- fixed bugs when trying to stack multiple movable blocks +- fixed Midas's touch having unrestricted vertical range +- fixed Lara saying "no" when taking valid actions in front of a key item receptacle +- fixed Lara not saying "no" when using the Scion incorrectly +- fixed flickering in bats' death animations and rapid shooting if Lara continues to fire when they are killed +- fixed looking forward too far causing an upside down camera frame +- fixed the Scion being extremely difficult to shoot with the shotgun +- fixed collision issues with drawbridges, trapdoors, and bridges when stacked over each other, over slopes, and near the ground +- fixed a potential softlock when killing the Torso boss in Great Pyramid + +#### Cheats +- added a fly cheat +- added a level skip cheat +- added a door open cheat (while in fly mode) +- added a cheat to increase the game speed +- added a cheat to explode Lara like in TR2 and TR3 + +#### Input +- added ability to move camera around with W,A,S,D +- added additional custom control schemes +- added the ability to unbind unessential keys +- added the ability to reset control schemes to default +- added customizable controller support +- added an inverted look camera option +- added the ability to move the look camera while targeting an enemy in combat +- fixed freeze when holding the Action key during end of level +- fixed inability to switch Control keys when shimmying +- fixed setting user keys being very difficult +- fixed skipping FMVs triggering inventory +- fixed skipping credits working too fast +- fixed not being able to close level stats with Escape +- fixed Lara jumping forever when alt+tabbing out of the game +- stopped the default controls from functioning when the user unbound them +- added the option to change weapon targets by tapping the new target change key +- added three targeting lock options: + - full lock: always keep target lock even if the enemy moves out of sight or dies (OG TR1) + - semi lock: keep target lock if the enemy moves out of sight but lose target lock if the enemy dies + - no lock: lose target lock if the enemy goes out of sight or dies (TR4+) + +#### Statistics +- added ability to keep timer on in inventory +- added optional compass level stats +- added optional final statistics screen +- added optional deaths counter +- added optional total pickups and kills per level +- added unobtainable pickups, kills, and secrets stats support in the gameflow + +#### Visuals +- added optional shotgun flash sprites +- added optional rendering of pickups on the ground as 3D meshes +- added Lara's braid to each level +- added support for displaying more than 3 pickup sprites +- added more control over when to show health bar and air bar +- added customizable health bar and air bar +- added rounded shadows (instead of the default octagon) +- added adjustable in-game brightness +- added support for HD FMVs +- added fanmade 16:9 menu backgrounds +- added optional fade effects +- added a vsync option +- added contextual arrows to menu options +- added support for animated room sprites, which also restores intended behavior in, for example, The Cistern room 0 +- added skybox support, with a default option provided for Lost Valley, Colosseum and Obelisk of Khamoon; custom level builders can use object slot `184` +- added reflections of Midas Hand death animation and savegame crystals +- changed the Scion in The Great Pyramid from spawning blood when hit to a richochet effect +- fixed thin black lines between polygons +- fixed black screen flashing when navigating the inventory +- fixed detail levels text flashing with any option change +- fixed underwater caustics animating at 2x speed +- fixed inconsistencies in some enemy textures +- fixed the animation of Lara's left arm when the shotgun is equipped +- fixed the following room texture issues: + - **Gym**: incorrect textures in room 9 + - **Caves**: an incorrect texture in room 6 and missing textures in rooms 1, 10, 14 and 30 + - **City of Vilcabamba**: an incorrect texture in room 26, and a missing texture and a stretched texture in room 15 + - **Lost Valley**: incorrect textures in rooms 6, 9, 16, 34 and 35, missing textures in rooms 6, 9, 25, 26, 27, 51, and 90, and stretched textures in room 63 + - **Tomb of Qualopec**: an incorrect and missing textures in room 8, and a misaligned texture in room 5 + - **St. Francis' Folly**: incorrect textures in rooms 1, 4, 18 and 35, and a misaligned texture in room 3 + - **Colosseum**: incorrect Midas textures appearing at the roof, incorrect textures in rooms 37, 67, 75 and 82, and missing textures in rooms 2 and 7 + - **Palace Midas**: incorrect textures in rooms 31, 34, 40 and 45, missing textures in rooms 2, 5, 9, 13, 30, and 53, and stretched textures in rooms 7 and 20 + - **The Cistern**: missing textures in rooms 3 and 9 and a stretched texture in room 102 + - **Tomb of Tihocan**: incorrect textures in rooms 75 and 89 and a misaligned texture in room 104 + - **City of Khamoon**: incorrect textures in rooms 47, 48, 51, 60 and 64, and a missing texture in room 58 + - **Obelisk of Khamoon**: incorrect textures in rooms 22, 23, 42 and 65; added shading to the gaps into City of Khamoon in rooms 8 and 20/21 + - **Sanctuary of the Scion**: missing textures in rooms 1, 11, 21, 52, 53, and 54 + - **Natla's Mines**: a missing texture in room 35, overlapping textures in room 55, an incorrect texture in room 69, and stretched textures in rooms 23 and 24 + - **Pre-Atlantis Cutscene**: stretched textures in rooms 6 and 21 + - **Atlantis**: incorrect textures in rooms 5, 18, 36, 43, 50, 52, 53, 54, 58, 78, 85 and 87, a missing texture in room 27, and stretched textures in rooms 13, 49 and 50 + - **Atlantis Cutscene**: incorrect and stretched textures in room 16 + - **The Great Pyramid**: incorrect textures in rooms 2, 5, 31, 36, 50, 52, 53, 54, 65 and 66, missing textures in rooms 21, 25, 26, and 66, and stretched textures in rooms 49 and 50 + - **Return to Egypt**: incorrect textures in rooms 46 and 47, a missing texture in room 98, and a stretched texture in room 47 + - **Temple of the Cat**: incorrect textures in rooms 50, 70, 71, 76, 78, 87 and 96, and a missing texture in 75 + - **Atlantean Stronghold**: incorrect textures in rooms 2, 6, 7 and 75, and missing textures in rooms 5, 13, 19, 63 and 74 + - **The Hive**: incorrect textures in room 8, 13 and 18 +- improved vertex movement when looking through water portals + +#### Audio +- added music during the credits +- added an option to turn off sound effect pitching +- added an option to use the PlayStation Uzi sound effects +- added the current music track and timestamp to the savegame so they now persist on load +- added the triggered music tracks to the savegame so one shot tracks don't replay on load +- added detection for animation commands to play SFX on land, water or both +- fixed the sound of collecting a secret killing the music +- fixed audio mixer stopping playing sounds on big explosions +- fixed game audio not muting when game is minimized +- fixed underwater ambient sound effect not playing +- fixed sound effects playing rapidly in sound menu if input held down +- fixed sounds stopping instead of pausing when using the inventory or pausing +- fixed the following music triggers: + - **Caves**: converted track 9 in room 34 to one shot + - **Tomb of Qualopec**: converted track 17 in room 25 to one shot + - **St. Francis' Folly**: converted track 7 in room 18 to one shot + - **Obelisk of Khamoon**: converted track 3 in room 12 and track 4 in room 32 to one shot + - **Sanctuary of the Scion**: converted track 10 in room 0 to one shot + - **Natla's Mines**: converted track 3 in room 86 to one shot + - **Atlantis**: converted track 8 in room 59 to one shot + - **The Great Pyramid**: converted track 8 in room 36 to one shot + - **Return to Egypt**: converted track 19 in room 0, track 14 in room 15, track 15 in room 19, track 16 in room 22, track 6 in room 61, and track 11 in room 93 to one shot + - **Temple of the Cat**: converted track 12 in room 14, track 7 in room 98, and track 20 in room 100 to one shot + - **Atlantean Stronghold**: converted track 20 in room 4, track 19 in room 13, track 11 in room 17, track 15 in room 20, and track 12 in room 25 to one shot + - **The Hive**: converted track 9 in room 8, track 6 in room 18, track 12 in room 30, track 18 in room 31, track 3 in room 32, and track 20 in room 35 to one shot + +#### Mods +- added developer console (accessible with `/`, see [COMMANDS.md](COMMANDS.md) for details) +- added ability to adjust Lara's starting health (easy no damage mod) +- added ability to disable healing between levels +- added ability to disable certain item pickups (all medpacks, shotgun, Magnums and/or UZIs) +- added ability to disable main menu demos, FMVs and/or cutscenes +- added external game flow (no longer 2 different .exes for TR1 and TR1UB). Refer to [GAMEFLOW.md](GAMEFLOW.md) for details +- added automatic calculation of secret counts (no longer having to fiddle with the .exe to get correct secret stats) +- added save game crystals game mode (enabled via gameflow) +- added per-level customizable water color (with customizable blue component) +- added per-level customizable fog distance +- added deadly water feature from TR2+ + +#### Miscellaneous +- added Linux builds +- added macOS builds +- added .jpeg/.png screenshots +- added an option to pause sound in the inventory screen +- added ability to skip FMVs with the Action key +- added ability to make freshly triggered (runaway) Pierre replace an already existing (runaway) Pierre +- expanded internal game memory limit from 3.5 MB to 16 MB +- expanded moveable limit from 256 to 10240 +- expanded maximum textures from 2048 to 8192 +- expanded maximum texture pages from 32 to 128 +- expanded maximum vertices of a single drawable object from 1500 to unlimited +- expanded the number of visible enemies from 8 to 32 +- ported audio decoding library to ffmpeg +- ported video decoding library to ffmpeg +- ported image decoding library to ffmpeg +- ported audio output library to SDL +- ported input method to SDL +- changed saves to be put in the saves/ directory +- fixed playing the secret sound in Tomb of Tihocan +- fixed reading user settings not restoring the volume diff --git a/SECRETS.md b/docs/tr1/SECRETS.md similarity index 100% rename from SECRETS.md rename to docs/tr1/SECRETS.md diff --git a/docs/showcase/3d_pickups.jpg b/docs/tr1/showcase/3d_pickups.jpg similarity index 100% rename from docs/showcase/3d_pickups.jpg rename to docs/tr1/showcase/3d_pickups.jpg diff --git a/docs/showcase/braid.jpg b/docs/tr1/showcase/braid.jpg similarity index 100% rename from docs/showcase/braid.jpg rename to docs/tr1/showcase/braid.jpg diff --git a/docs/showcase/compass_stats.jpg b/docs/tr1/showcase/compass_stats.jpg similarity index 100% rename from docs/showcase/compass_stats.jpg rename to docs/tr1/showcase/compass_stats.jpg diff --git a/docs/showcase/console.webp b/docs/tr1/showcase/console.webp similarity index 100% rename from docs/showcase/console.webp rename to docs/tr1/showcase/console.webp diff --git a/docs/showcase/draw_distance.webp b/docs/tr1/showcase/draw_distance.webp similarity index 100% rename from docs/showcase/draw_distance.webp rename to docs/tr1/showcase/draw_distance.webp diff --git a/docs/showcase/enemy_health_bar_and_scaling.jpg b/docs/tr1/showcase/enemy_health_bar_and_scaling.jpg similarity index 100% rename from docs/showcase/enemy_health_bar_and_scaling.jpg rename to docs/tr1/showcase/enemy_health_bar_and_scaling.jpg diff --git a/docs/showcase/fly_cheat.jpg b/docs/tr1/showcase/fly_cheat.jpg similarity index 100% rename from docs/showcase/fly_cheat.jpg rename to docs/tr1/showcase/fly_cheat.jpg diff --git a/docs/showcase/free_camera.jpg b/docs/tr1/showcase/free_camera.jpg similarity index 100% rename from docs/showcase/free_camera.jpg rename to docs/tr1/showcase/free_camera.jpg diff --git a/docs/showcase/ps1_ui_and_gfx.jpg b/docs/tr1/showcase/ps1_ui_and_gfx.jpg similarity index 100% rename from docs/showcase/ps1_ui_and_gfx.jpg rename to docs/tr1/showcase/ps1_ui_and_gfx.jpg diff --git a/docs/showcase/select_level.jpg b/docs/tr1/showcase/select_level.jpg similarity index 100% rename from docs/showcase/select_level.jpg rename to docs/tr1/showcase/select_level.jpg diff --git a/docs/showcase/skybox.jpg b/docs/tr1/showcase/skybox.jpg similarity index 100% rename from docs/showcase/skybox.jpg rename to docs/tr1/showcase/skybox.jpg diff --git a/docs/tr2/README.md b/docs/tr2/README.md index 545af2d10..f11a8aecf 100644 --- a/docs/tr2/README.md +++ b/docs/tr2/README.md @@ -1,55 +1,21 @@

-TR2X logo -TR2X logo +TR2X logo +TR2X logo

-TR2X is an open-source decompilation project for Tomb Raider 2, created as a -sequel to the successful [TR1X](https://github.com/LostArtefacts/TR1X/) project -for Tomb Raider 1. Our project is in the early stages, and our main focus is on -decompiling as much of the game as possible. - -## The situation - -TR2X draws inspiration from existing decompilation efforts, including the -achievements of [TR2Main](https://github.com/Arsunt/TR2Main) developed by -[Arsunt](https://github.com/Arsunt/). However, we emphasize that TR2X is an -independent project and does not directly copy code from TR2Main. - -## Our Mission - -Our mission is to fully decompile Tomb Raider 2 and to eventually enhance the -overall gameplay experience. - -### Key Goals - -- **Comprehensive Decompilation:** - Our primary objective is to achieve a thorough decompilation of Tomb Raider - 2, enabling a deeper understanding of its inner workings. - -- **Enhancement of UK Box Version:** - Our work is based on the UK Box version of Tomb Raider 2. Eventually we hope - to reach feature parity with the Steam/multipatch version, and possibly - TR2Main as well. - -- **Cross-platform Compatibility:** - We are committed to making Tomb Raider 2 run natively on Linux, with the - possibility of supporting Mac systems in the future. - -- **Transparent Development Process:** - We value transparency and aim to maintain a transparent development process, - ensuring regular updates and engagement with the community. - -- **Changelog and Progress Updates:** - We strive to provide detailed and reliable changelogs, documenting - significant updates and changes made to TR2X. Additionally, we will keep - the community informed about the progress of the project. - TR2X is currently in the early stages of development, focusing on the decompilation process. We recognize that there is much work to be done. -## Decompilation progress +## Windows -![](docs/progress.svg) +### Installing (manual) + +1. Head over to GitHub releases: https://github.com/LostArtefacts/TRX/releases +2. Download the TR2X zip file. +3. Extract the TR2X zip file into a directory of your choice. + Make sure you choose to overwrite existing directories and files. +4. (First time installation) Put your original game files into the target directory. +5. To play the game, run `TR2X.exe`. ## Improvements over original game @@ -58,6 +24,10 @@ decompilation process. We recognize that there is much work to be done. - added an option to fix M16 accuracy while running - fixed killing the T-Rex with a grenade launcher crashing the game - fixed secret rewards not displaying shotgun ammo +- fixed numeric keys interfering with the demos +- fixed explosions sometimes being drawn too dark +- fixed controls dialog remapping being too sensitive +- fixed the distorted skybox in room 5 of Barkhang Monastery #### Visuals @@ -68,14 +38,4 @@ decompilation process. We recognize that there is much work to be done. - fixed music not playing with certain game versions #### Mods -- added developer console (accessible with `/`, see [COMMANDS.md] for details) - -## Contributions - -Please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) file for more -information on how to contribute to the project. - -## License - -TR2X follows the GPL license. Please refer to the [COPYING.md](COPYING.md) file -for further details. +- added developer console (accessible with `/`, see [COMMANDS.md](COMMANDS.md) for details) diff --git a/justfile b/justfile index f6f395099..b28996e7b 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,7 @@ CWD := `pwd` HOST_USER_UID := `id -u` HOST_USER_GID := `id -g` -default: (build-win "debug") +default: (tr1-build-win "debug") _docker_push tag: docker push {{tag}} @@ -10,7 +10,7 @@ _docker_push tag: _docker_build dockerfile tag force="0": #!/usr/bin/env sh if [ "{{force}}" = "0" ]; then - docker images --format '{''{.Repository}}' | grep '^{{tag}}$' + docker images --format '{''{.Repository}}' | grep '^{{tag}}$' >/dev/null if [ $? -eq 0 ]; then echo "Docker image {{tag}} found" exit 0 @@ -41,34 +41,48 @@ _docker_run *args: {{args}} -image-linux force="1": (_docker_build "tools/tr1/docker/game-linux/Dockerfile" "rrdash/tr1x-linux" force) -image-win force="1": (_docker_build "tools/tr1/docker/game-win/Dockerfile" "rrdash/tr1x" force) -image-win-config force="1": (_docker_build "tools/tr1/docker/config/Dockerfile" "rrdash/tr1x-config" force) -image-win-installer force="1": (_docker_build "tools/tr1/docker/installer/Dockerfile" "rrdash/tr1x-installer" force) +tr1-image-linux force="1": (_docker_build "tools/tr1/docker/game-linux/Dockerfile" "rrdash/tr1x-linux" force) +tr1-image-win force="1": (_docker_build "tools/tr1/docker/game-win/Dockerfile" "rrdash/tr1x" force) +tr1-image-win-config force="1": (_docker_build "tools/tr1/docker/config/Dockerfile" "rrdash/tr1x-config" force) +tr1-image-win-installer force="1": (_docker_build "tools/tr1/docker/installer/Dockerfile" "rrdash/tr1x-installer" force) -push-image-linux: (image-linux "0") (_docker_push "rrdash/tr1x-linux") -push-image-win: (image-win "0") (_docker_push "rrdash/tr1x") +tr1-push-image-linux: (tr1-image-linux "0") (_docker_push "rrdash/tr1x-linux") +tr1-push-image-win: (tr1-image-win "0") (_docker_push "rrdash/tr1x") -build-linux target='debug': (image-linux "0") (_docker_run "-e" "TARGET="+target "rrdash/tr1x-linux") -build-win target='debug': (image-win "0") (_docker_run "-e" "TARGET="+target "rrdash/tr1x") -build-win-config: (image-win-config "0") (_docker_run "rrdash/tr1x-config") -build-win-installer: (image-win-installer "0") (_docker_run "rrdash/tr1x-installer") +tr1-build-linux target='debug': (tr1-image-linux "0") (_docker_run "-e" "TARGET="+target "rrdash/tr1x-linux") +tr1-build-win target='debug': (tr1-image-win "0") (_docker_run "-e" "TARGET="+target "rrdash/tr1x") +tr1-build-win-config: (tr1-image-win-config "0") (_docker_run "rrdash/tr1x-config") +tr1-build-win-installer: (tr1-image-win-installer "0") (_docker_run "rrdash/tr1x-installer") -package-linux: (build-linux "release") (_docker_run "rrdash/tr1x-linux" "package") -package-win: (build-win "release") (_docker_run "rrdash/tr1x" "package") -package-win-all: (build-win "release") (build-win-config) (_docker_run "rrdash/tr1x" "package") -package-win-installer: (build-win "release") (build-win-config) (_docker_run "rrdash/tr1x" "package" "-o" "tools/tr1/installer/Installer/Resources/release.zip") (build-win-installer) +tr1-package-linux: (tr1-build-linux "release") (_docker_run "rrdash/tr1x-linux" "package") +tr1-package-win: (tr1-build-win "release") (_docker_run "rrdash/tr1x" "package") +tr1-package-win-all: (tr1-build-win "release") (tr1-build-win-config) (_docker_run "rrdash/tr1x" "package") +tr1-package-win-installer: (tr1-build-win "release") (tr1-build-win-config) (_docker_run "rrdash/tr1x" "package" "-o" "tools/tr1/installer/Installer/Resources/release.zip") (tr1-build-win-installer) #!/bin/sh git checkout "tools/tr1/installer/Installer/Resources/release.zip" - exe_name=TR1X-$(tools/get_version)-Installer.exe + exe_name=TR1X-$(tools/get_version 1)-Installer.exe cp tools/tr1/installer/out/TR1X_Installer.exe "${exe_name}" echo "Created ${exe_name}" -output-current-version: - tools/get_version +tr2-image-win force="1": (_docker_build "tools/tr2/docker/game-win/Dockerfile" "rrdash/tr2x" force) +tr2-image-win-config force="1": (_docker_build "tools/tr2/docker/config/Dockerfile" "rrdash/tr2x-config" force) -output-current-changelog: - tools/output_current_changelog +tr2-push-image-win: (tr2-image-win "0") (_docker_push "rrdash/tr2x") + +tr2-build-win target='debug': (tr2-image-win "0") (_docker_run "-e" "TARGET="+target "rrdash/tr2x") +tr2-build-win-config: (tr2-image-win-config "0") (_docker_run "rrdash/tr2x-config") + +tr2-package-win target='release': (tr2-build-win target) (_docker_run "rrdash/tr2x" "package") +tr2-package-win-all target='release': (tr2-build-win target) (tr2-build-win-config) (_docker_run "rrdash/tr2x" "package") + +output-release-name tr_version: + tools/output_release_name {{tr_version}} + +output-current-version tr_version: + tools/get_version {{tr_version}} + +output-current-changelog tr_version: + tools/output_current_changelog {{tr_version}} clean: -find build/ -type f -delete diff --git a/meson.build b/meson.build deleted file mode 100644 index 4478625f6..000000000 --- a/meson.build +++ /dev/null @@ -1,319 +0,0 @@ -project( - 'TR1X', - 'c', - default_options: [ - 'c_std=c2x', - 'warning_level=2', - ], -) - -if host_machine.system() == 'darwin' - gfx_gl_default_backend = 'GFX_GL_33C' -else - gfx_gl_default_backend = 'GFX_GL_21' -endif - -trx = subproject('libtrx', default_options: { - 'tr_version': '1', - 'gfx_gl_default_backend': gfx_gl_default_backend, -}) -c_compiler = meson.get_compiler('c') - -build_opts = [ - '-Wno-unused', - '-DMESON_BUILD', - '-DTR_VERSION=1', - '-fno-omit-frame-pointer', - '-ffile-prefix-map=../../src/tr1/=', -] + trx.get_variable('defines') - -add_project_arguments(build_opts, language: 'c') - -staticdeps = get_option('staticdeps') - -# Always dynamically link on macOS -if host_machine.system() == 'darwin' - staticdeps = false -endif - -null_dep = dependency('', required: false) - -dep_trx = trx.get_variable('dep_trx') -dep_avcodec = dependency('libavcodec', static: staticdeps) -dep_avformat = dependency('libavformat', static: staticdeps) -dep_avutil = dependency('libavutil', static: staticdeps) -dep_mathlibrary = c_compiler.find_library('m', static: staticdeps, required : false) -dep_swscale = dependency('libswscale', static: staticdeps) -dep_swresample = dependency('libswresample', static: staticdeps) -dep_sdl2 = dependency('SDL2', static: staticdeps) -dep_zlib = null_dep - -if not staticdeps - dep_zlib = dependency('zlib', static: staticdeps) -endif - -# autogenerated files -resources = [] -python3 = find_program('python3', required: true) -git = find_program('git', required: true) - -init = custom_target( - 'fake_init', - output: ['init.c'], - command: [python3, meson.source_root() + '/tools/generate_init', '-o', meson.current_build_dir() / '@OUTPUT0@'], - build_always_stale: true, -) - -version_rc = custom_target( - 'fake_version', - output: ['version.rc'], - command: [python3, meson.source_root() + '/tools/generate_rcfile', '-o', '@OUTPUT0@'], - build_always_stale: true, -) - -icon_rc = custom_target( - 'fake_icon', - output: ['icon.rc'], - command: [python3, meson.source_root() + '/tools/generate_rcfile', '-o', '@OUTPUT0@'], -) - -link_args = [] - -if host_machine.system() == 'windows' - windows = import('windows') - - resources = [ - windows.compile_resources(version_rc), - windows.compile_resources(icon_rc), - ] - - link_args += ['-static'] -endif - -sources = [ - init, - 'src/tr1/config.c', - 'src/tr1/config_map.c', - 'src/tr1/game/anim.c', - 'src/tr1/game/backpack.c', - 'src/tr1/game/box.c', - 'src/tr1/game/camera.c', - 'src/tr1/game/carrier.c', - 'src/tr1/game/clock.c', - 'src/tr1/game/collide.c', - 'src/tr1/game/console/cmd/easy_config.c', - 'src/tr1/game/console/cmd/speed.c', - 'src/tr1/game/console/common.c', - 'src/tr1/game/console/setup.c', - 'src/tr1/game/creature.c', - 'src/tr1/game/effect_routines/bubbles.c', - 'src/tr1/game/effect_routines/chain_block.c', - 'src/tr1/game/effect_routines/dino_stomp.c', - 'src/tr1/game/effect_routines/earthquake.c', - 'src/tr1/game/ui/common.c', - 'src/tr1/game/ui/widgets/label.c', - 'src/tr1/game/ui/widgets/prompt.c', - 'src/tr1/game/ui/widgets/window.c', - 'src/tr1/game/effect_routines/explosion.c', - 'src/tr1/game/effect_routines/finish_level.c', - 'src/tr1/game/effect_routines/flicker.c', - 'src/tr1/game/effect_routines/flipmap.c', - 'src/tr1/game/effect_routines/flood.c', - 'src/tr1/game/effect_routines/lara_effects.c', - 'src/tr1/game/effect_routines/powerup.c', - 'src/tr1/game/effect_routines/raising_block.c', - 'src/tr1/game/effect_routines/sand.c', - 'src/tr1/game/effect_routines/stairs2slope.c', - 'src/tr1/game/effect_routines/turn_180.c', - 'src/tr1/game/effects.c', - 'src/tr1/game/effects/blood.c', - 'src/tr1/game/effects/exploding_death.c', - 'src/tr1/game/effects/gun.c', - 'src/tr1/game/effects/gunshot.c', - 'src/tr1/game/fmv.c', - 'src/tr1/game/game/game.c', - 'src/tr1/game/game/game_draw.c', - 'src/tr1/game/game/game_main_menu.c', - 'src/tr1/game/game_string.c', - 'src/tr1/game/gamebuf.c', - 'src/tr1/game/gameflow.c', - 'src/tr1/game/gun/gun.c', - 'src/tr1/game/gun/gun_misc.c', - 'src/tr1/game/gun/gun_pistols.c', - 'src/tr1/game/gun/gun_rifle.c', - 'src/tr1/game/inject.c', - 'src/tr1/game/input.c', - 'src/tr1/game/interpolation.c', - 'src/tr1/game/inventory/inventory.c', - 'src/tr1/game/inventory/inventory_func.c', - 'src/tr1/game/inventory/inventory_ring.c', - 'src/tr1/game/inventory/inventory_vars.c', - 'src/tr1/game/items.c', - 'src/tr1/game/lara/cheat.c', - 'src/tr1/game/lara/col.c', - 'src/tr1/game/lara/common.c', - 'src/tr1/game/lara/control.c', - 'src/tr1/game/lara/draw.c', - 'src/tr1/game/lara/hair.c', - 'src/tr1/game/lara/look.c', - 'src/tr1/game/lara/misc.c', - 'src/tr1/game/lara/state.c', - 'src/tr1/game/level.c', - 'src/tr1/game/los.c', - 'src/tr1/game/lot.c', - 'src/tr1/game/music.c', - 'src/tr1/game/objects/common.c', - 'src/tr1/game/objects/creatures/ape.c', - 'src/tr1/game/objects/creatures/bacon_lara.c', - 'src/tr1/game/objects/creatures/baldy.c', - 'src/tr1/game/objects/creatures/bat.c', - 'src/tr1/game/objects/creatures/bear.c', - 'src/tr1/game/objects/creatures/centaur.c', - 'src/tr1/game/objects/creatures/cowboy.c', - 'src/tr1/game/objects/creatures/crocodile.c', - 'src/tr1/game/objects/creatures/cutscene_player.c', - 'src/tr1/game/objects/creatures/larson.c', - 'src/tr1/game/objects/creatures/lion.c', - 'src/tr1/game/objects/creatures/mummy.c', - 'src/tr1/game/objects/creatures/mutant.c', - 'src/tr1/game/objects/creatures/natla.c', - 'src/tr1/game/objects/creatures/pierre.c', - 'src/tr1/game/objects/creatures/pod.c', - 'src/tr1/game/objects/creatures/raptor.c', - 'src/tr1/game/objects/creatures/rat.c', - 'src/tr1/game/objects/creatures/skate_kid.c', - 'src/tr1/game/objects/creatures/statue.c', - 'src/tr1/game/objects/creatures/torso.c', - 'src/tr1/game/objects/creatures/trex.c', - 'src/tr1/game/objects/creatures/wolf.c', - 'src/tr1/game/objects/effects/blood.c', - 'src/tr1/game/objects/effects/body_part.c', - 'src/tr1/game/objects/effects/bubble.c', - 'src/tr1/game/objects/effects/dart_effect.c', - 'src/tr1/game/objects/effects/ember.c', - 'src/tr1/game/objects/effects/explosion.c', - 'src/tr1/game/objects/effects/flame.c', - 'src/tr1/game/objects/effects/gunshot.c', - 'src/tr1/game/objects/effects/missile.c', - 'src/tr1/game/objects/effects/natla_gun.c', - 'src/tr1/game/objects/effects/ricochet.c', - 'src/tr1/game/objects/effects/splash.c', - 'src/tr1/game/objects/effects/twinkle.c', - 'src/tr1/game/objects/general/boat.c', - 'src/tr1/game/objects/general/bridge_common.c', - 'src/tr1/game/objects/general/bridge_flat.c', - 'src/tr1/game/objects/general/bridge_tilt1.c', - 'src/tr1/game/objects/general/bridge_tilt2.c', - 'src/tr1/game/objects/general/cabin.c', - 'src/tr1/game/objects/general/camera_target.c', - 'src/tr1/game/objects/general/cog.c', - 'src/tr1/game/objects/general/door.c', - 'src/tr1/game/objects/general/drawbridge.c', - 'src/tr1/game/objects/general/earthquake.c', - 'src/tr1/game/objects/general/keyhole.c', - 'src/tr1/game/objects/general/moving_bar.c', - 'src/tr1/game/objects/general/pickup.c', - 'src/tr1/game/objects/general/puzzle_hole.c', - 'src/tr1/game/objects/general/save_crystal.c', - 'src/tr1/game/objects/general/scion1.c', - 'src/tr1/game/objects/general/scion2.c', - 'src/tr1/game/objects/general/scion3.c', - 'src/tr1/game/objects/general/scion4.c', - 'src/tr1/game/objects/general/scion_holder.c', - 'src/tr1/game/objects/general/switch.c', - 'src/tr1/game/objects/general/trapdoor.c', - 'src/tr1/game/objects/general/waterfall.c', - 'src/tr1/game/objects/setup.c', - 'src/tr1/game/objects/traps/damocles_sword.c', - 'src/tr1/game/objects/traps/dart.c', - 'src/tr1/game/objects/traps/dart_emitter.c', - 'src/tr1/game/objects/traps/ember_emitter.c', - 'src/tr1/game/objects/traps/falling_block.c', - 'src/tr1/game/objects/traps/falling_ceiling.c', - 'src/tr1/game/objects/traps/flame_emitter.c', - 'src/tr1/game/objects/traps/lava_wedge.c', - 'src/tr1/game/objects/traps/lightning_emitter.c', - 'src/tr1/game/objects/traps/midas_touch.c', - 'src/tr1/game/objects/traps/movable_block.c', - 'src/tr1/game/objects/traps/pendulum.c', - 'src/tr1/game/objects/traps/rolling_ball.c', - 'src/tr1/game/objects/traps/sliding_pillar.c', - 'src/tr1/game/objects/traps/spikes.c', - 'src/tr1/game/objects/traps/teeth_trap.c', - 'src/tr1/game/objects/traps/thors_hammer_handle.c', - 'src/tr1/game/objects/traps/thors_hammer_head.c', - 'src/tr1/game/objects/vars.c', - 'src/tr1/game/option/option.c', - 'src/tr1/game/option/option_compass.c', - 'src/tr1/game/option/option_control.c', - 'src/tr1/game/option/option_control_pick.c', - 'src/tr1/game/option/option_graphics.c', - 'src/tr1/game/option/option_passport.c', - 'src/tr1/game/option/option_sound.c', - 'src/tr1/game/output.c', - 'src/tr1/game/overlay.c', - 'src/tr1/game/packer.c', - 'src/tr1/game/phase/phase.c', - 'src/tr1/game/phase/phase_cutscene.c', - 'src/tr1/game/phase/phase_demo.c', - 'src/tr1/game/phase/phase_game.c', - 'src/tr1/game/phase/phase_inventory.c', - 'src/tr1/game/phase/phase_pause.c', - 'src/tr1/game/phase/phase_picture.c', - 'src/tr1/game/phase/phase_stats.c', - 'src/tr1/game/random.c', - 'src/tr1/game/requester.c', - 'src/tr1/game/room.c', - 'src/tr1/game/room_draw.c', - 'src/tr1/game/savegame/savegame.c', - 'src/tr1/game/savegame/savegame_bson.c', - 'src/tr1/game/savegame/savegame_legacy.c', - 'src/tr1/game/screen.c', - 'src/tr1/game/shell.c', - 'src/tr1/game/sound.c', - 'src/tr1/game/stats.c', - 'src/tr1/game/text.c', - 'src/tr1/game/viewport.c', - 'src/tr1/global/enum_map.c', - 'src/tr1/global/vars.c', - 'src/tr1/math/math.c', - 'src/tr1/math/math_misc.c', - 'src/tr1/math/matrix.c', - 'src/tr1/specific/s_clock.c', - 'src/tr1/specific/s_fmv.c', - 'src/tr1/specific/s_input.c', - 'src/tr1/specific/s_output.c', - 'src/tr1/specific/s_shell.c', - resources, -] - -dependencies = [ - dep_trx, - dep_avcodec, - dep_avformat, - dep_avutil, - dep_mathlibrary, - dep_sdl2, - dep_swresample, - dep_swscale, - dep_zlib, -] - -executable( - 'TR1X', - sources, - include_directories: ['src/tr1/'], - dependencies: dependencies, - link_args: link_args, - gui_app: true, - install: true, -) - -if host_machine.system() == 'darwin' - install_subdir('data/tr1/ship/cfg', install_dir : 'Contents/Resources') - install_subdir('data/tr1/ship/data', install_dir : 'Contents/Resources') - install_subdir('data/tr1/ship/shaders', install_dir : 'Contents/Resources') - install_data('data/tr1/mac/icon.icns', install_dir : 'Contents/Resources') - install_data('data/tr1/mac/Info.plist', install_dir : 'Contents') - meson.add_install_script('tools/tr1/mac/bundle_dylibs') -endif diff --git a/src/libtrx/meson.build b/src/libtrx/meson.build new file mode 100644 index 000000000..872d9eaca --- /dev/null +++ b/src/libtrx/meson.build @@ -0,0 +1,166 @@ +project( + 'libtrx', + 'c', + default_options: [ + 'c_std=c11', + 'warning_level=2', + ], +) + +staticdeps = get_option('staticdeps') +tr_version = get_option('tr_version') +gfx_gl_default_backend = get_option('gfx_gl_default_backend') + +fs = import('fs') +c_compiler = meson.get_compiler('c') +relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) +build_opts = [ + '-fmacro-prefix-map=@0@/=libtrx/'.format(relative_dir), + '-Wno-unused', + '-Wno-address-of-packed-member', + '-DMESON_BUILD', + '-DDWST_STATIC', + '-DPCRE2_STATIC', + '-DPCRE2_CODE_UNIT_WIDTH=8', + '-DTR_VERSION=' + tr_version.to_string(), + '-DGFX_GL_DEFAULT_BACKEND=' + gfx_gl_default_backend, +] +set_variable('defines', ['-DTR_VERSION=' + tr_version.to_string()]) + +add_project_arguments(build_opts, language: 'c') + +# Always dynamically link on macOS +if host_machine.system() == 'darwin' + staticdeps = false +endif + +uthash = subproject('uthash', default_options: ['warning_level=0']) + +null_dep = dependency('', required: false) +dep_avcodec = dependency('libavcodec', static: staticdeps) +dep_avformat = dependency('libavformat', static: staticdeps) +dep_avutil = dependency('libavutil', static: staticdeps) +dep_sdl2 = dependency('SDL2', static: staticdeps) +dep_pcre2 = dependency('libpcre2-8', static: staticdeps) +dep_backtrace = c_compiler.find_library('backtrace', static: true, required: false) +dep_swscale = dependency('libswscale', static: staticdeps) +dep_swresample = dependency('libswresample', static: staticdeps) + +dep_zlib = null_dep +if not staticdeps + dep_zlib = dependency('zlib', static: staticdeps) +endif + +if host_machine.system() == 'windows' + dep_opengl = c_compiler.find_library('opengl32') +else + dep_opengl = dependency('GL') +endif + +sources = [ + 'benchmark.c', + 'config/common.c', + 'config/file.c', + 'engine/audio.c', + 'engine/audio_sample.c', + 'engine/audio_stream.c', + 'engine/image.c', + 'enum_map.c', + 'event_manager.c', + 'filesystem.c', + 'game/backpack.c', + 'game/console/cmd/config.c', + 'game/console/cmd/die.c', + 'game/console/cmd/end_level.c', + 'game/console/cmd/exit_game.c', + 'game/console/cmd/exit_to_title.c', + 'game/console/cmd/flipmap.c', + 'game/console/cmd/fly.c', + 'game/console/cmd/give_item.c', + 'game/console/cmd/heal.c', + 'game/console/cmd/kill.c', + 'game/console/cmd/load_game.c', + 'game/console/cmd/play_demo.c', + 'game/console/cmd/play_level.c', + 'game/console/cmd/pos.c', + 'game/console/cmd/save_game.c', + 'game/console/cmd/set_health.c', + 'game/console/cmd/sfx.c', + 'game/console/cmd/teleport.c', + 'game/console/common.c', + 'game/game_string.c', + 'game/items.c', + 'game/objects/names.c', + 'game/ui/common.c', + 'game/ui/events.c', + 'game/ui/widgets/console.c', + 'game/ui/widgets/prompt.c', + 'game/ui/widgets/spacer.c', + 'game/ui/widgets/stack.c', + 'gfx/2d/2d_renderer.c', + 'gfx/2d/2d_surface.c', + 'gfx/3d/3d_renderer.c', + 'gfx/3d/vertex_stream.c', + 'gfx/context.c', + 'gfx/gl/buffer.c', + 'gfx/gl/gl_core_3_3.c', + 'gfx/gl/program.c', + 'gfx/gl/sampler.c', + 'gfx/gl/texture.c', + 'gfx/gl/utils.c', + 'gfx/gl/vertex_array.c', + 'gfx/renderers/fbo_renderer.c', + 'gfx/renderers/legacy_renderer.c', + 'gfx/screenshot.c', + 'json/bson_parse.c', + 'json/bson_write.c', + 'json/json_base.c', + 'json/json_parse.c', + 'json/json_write.c', + 'log.c', + 'memory.c', + 'strings/common.c', + 'strings/fuzzy_match.c', + 'vector.c', + 'virtual_file.c', +] + +dependencies = [ + dep_avcodec, + dep_avformat, + dep_avutil, + dep_sdl2, + dep_pcre2, + dep_backtrace, + dep_swresample, + dep_swscale, + dep_zlib, + dep_opengl, + uthash.get_variable('uthash_dep'), +] + +if dep_backtrace.found() and host_machine.system() == 'linux' + sources += ['log_linux.c'] +elif host_machine.system() == 'windows' + sources += ['log_windows.c'] + dwarfstack = subproject('dwarfstack', default_options: ['warning_level=0']) + dep_dwarfstack = dwarfstack.get_variable('dep_dwarfstack') + dep_dbghelp = c_compiler.find_library('dbghelp') + dependencies += [dep_dbghelp, dep_dwarfstack] +else + sources += ['log_unknown.c'] +endif + +libtrx = static_library( + 'libtrx', + sources, + dependencies: dependencies, + include_directories: ['.', 'include/libtrx/'], +) + +dep_trx = declare_dependency( + link_whole: libtrx, + include_directories: [ + include_directories('include/', is_system: true) + ] +) diff --git a/src/libtrx/meson.options b/src/libtrx/meson.options new file mode 100644 index 000000000..557d1ba27 --- /dev/null +++ b/src/libtrx/meson.options @@ -0,0 +1,20 @@ +option( + 'staticdeps', + type: 'boolean', + value: true, + description: 'Try to build against static dependencies. default: true' +) + +option( + 'tr_version', + type: 'integer', + min: 1, + max: 2, + description: 'Which engine version to compile for' +) + +option( + 'gfx_gl_default_backend', + type: 'string', + description: 'Which OpenGL version to target' +) diff --git a/src/tr1/meson.build b/src/tr1/meson.build new file mode 100644 index 000000000..bcaf80e9a --- /dev/null +++ b/src/tr1/meson.build @@ -0,0 +1,320 @@ +project( + 'TR1X', + 'c', + default_options: [ + 'c_std=c2x', + 'warning_level=2', + ], +) + +if host_machine.system() == 'darwin' + gfx_gl_default_backend = 'GFX_GL_33C' +else + gfx_gl_default_backend = 'GFX_GL_21' +endif + +trx = subproject('libtrx', default_options: { + 'tr_version': '1', + 'gfx_gl_default_backend': gfx_gl_default_backend, +}) +c_compiler = meson.get_compiler('c') + +fs = import('fs') +relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) +build_opts = [ + '-Wno-unused', + '-DMESON_BUILD', + '-DTR_VERSION=1', + '-fno-omit-frame-pointer', + '-ffile-prefix-map=@0@/='.format(relative_dir), +] + trx.get_variable('defines') + +add_project_arguments(build_opts, language: 'c') + +staticdeps = get_option('staticdeps') + +# Always dynamically link on macOS +if host_machine.system() == 'darwin' + staticdeps = false +endif + +null_dep = dependency('', required: false) + +dep_trx = trx.get_variable('dep_trx') +dep_avcodec = dependency('libavcodec', static: staticdeps) +dep_avformat = dependency('libavformat', static: staticdeps) +dep_avutil = dependency('libavutil', static: staticdeps) +dep_mathlibrary = c_compiler.find_library('m', static: staticdeps, required : false) +dep_swscale = dependency('libswscale', static: staticdeps) +dep_swresample = dependency('libswresample', static: staticdeps) +dep_sdl2 = dependency('SDL2', static: staticdeps) +dep_zlib = null_dep + +if not staticdeps + dep_zlib = dependency('zlib', static: staticdeps) +endif + +# autogenerated files +resources = [] +python3 = find_program('python3', required: true) +git = find_program('git', required: true) + +init = custom_target( + 'fake_init', + output: ['init.c'], + command: [python3, meson.source_root() + '/../../tools/tr1/generate_init', '-o', meson.current_build_dir() / '@OUTPUT0@'], + build_always_stale: true, +) + +version_rc = custom_target( + 'fake_version', + output: ['version.rc'], + command: [python3, meson.source_root() + '/../../tools/tr1/generate_rcfile', '-o', '@OUTPUT0@'], + build_always_stale: true, +) + +icon_rc = custom_target( + 'fake_icon', + output: ['icon.rc'], + command: [python3, meson.source_root() + '/../../tools/tr1/generate_rcfile', '-o', '@OUTPUT0@'], +) + +link_args = [] + +if host_machine.system() == 'windows' + windows = import('windows') + + resources = [ + windows.compile_resources(version_rc), + windows.compile_resources(icon_rc), + ] + + link_args += ['-static'] +endif + +sources = [ + init, + 'config.c', + 'config_map.c', + 'game/anim.c', + 'game/backpack.c', + 'game/box.c', + 'game/camera.c', + 'game/carrier.c', + 'game/clock.c', + 'game/collide.c', + 'game/console/cmd/easy_config.c', + 'game/console/cmd/speed.c', + 'game/console/common.c', + 'game/console/setup.c', + 'game/creature.c', + 'game/effect_routines/bubbles.c', + 'game/effect_routines/chain_block.c', + 'game/effect_routines/dino_stomp.c', + 'game/effect_routines/earthquake.c', + 'game/ui/common.c', + 'game/ui/widgets/label.c', + 'game/ui/widgets/prompt.c', + 'game/ui/widgets/window.c', + 'game/effect_routines/explosion.c', + 'game/effect_routines/finish_level.c', + 'game/effect_routines/flicker.c', + 'game/effect_routines/flipmap.c', + 'game/effect_routines/flood.c', + 'game/effect_routines/lara_effects.c', + 'game/effect_routines/powerup.c', + 'game/effect_routines/raising_block.c', + 'game/effect_routines/sand.c', + 'game/effect_routines/stairs2slope.c', + 'game/effect_routines/turn_180.c', + 'game/effects.c', + 'game/effects/blood.c', + 'game/effects/exploding_death.c', + 'game/effects/gun.c', + 'game/effects/gunshot.c', + 'game/fmv.c', + 'game/game/game.c', + 'game/game/game_draw.c', + 'game/game/game_main_menu.c', + 'game/game_string.c', + 'game/gamebuf.c', + 'game/gameflow.c', + 'game/gun/gun.c', + 'game/gun/gun_misc.c', + 'game/gun/gun_pistols.c', + 'game/gun/gun_rifle.c', + 'game/inject.c', + 'game/input.c', + 'game/interpolation.c', + 'game/inventory/inventory.c', + 'game/inventory/inventory_func.c', + 'game/inventory/inventory_ring.c', + 'game/inventory/inventory_vars.c', + 'game/items.c', + 'game/lara/cheat.c', + 'game/lara/col.c', + 'game/lara/common.c', + 'game/lara/control.c', + 'game/lara/draw.c', + 'game/lara/hair.c', + 'game/lara/look.c', + 'game/lara/misc.c', + 'game/lara/state.c', + 'game/level.c', + 'game/los.c', + 'game/lot.c', + 'game/music.c', + 'game/objects/common.c', + 'game/objects/creatures/ape.c', + 'game/objects/creatures/bacon_lara.c', + 'game/objects/creatures/baldy.c', + 'game/objects/creatures/bat.c', + 'game/objects/creatures/bear.c', + 'game/objects/creatures/centaur.c', + 'game/objects/creatures/cowboy.c', + 'game/objects/creatures/crocodile.c', + 'game/objects/creatures/cutscene_player.c', + 'game/objects/creatures/larson.c', + 'game/objects/creatures/lion.c', + 'game/objects/creatures/mummy.c', + 'game/objects/creatures/mutant.c', + 'game/objects/creatures/natla.c', + 'game/objects/creatures/pierre.c', + 'game/objects/creatures/pod.c', + 'game/objects/creatures/raptor.c', + 'game/objects/creatures/rat.c', + 'game/objects/creatures/skate_kid.c', + 'game/objects/creatures/statue.c', + 'game/objects/creatures/torso.c', + 'game/objects/creatures/trex.c', + 'game/objects/creatures/wolf.c', + 'game/objects/effects/blood.c', + 'game/objects/effects/body_part.c', + 'game/objects/effects/bubble.c', + 'game/objects/effects/dart_effect.c', + 'game/objects/effects/ember.c', + 'game/objects/effects/explosion.c', + 'game/objects/effects/flame.c', + 'game/objects/effects/gunshot.c', + 'game/objects/effects/missile.c', + 'game/objects/effects/natla_gun.c', + 'game/objects/effects/ricochet.c', + 'game/objects/effects/splash.c', + 'game/objects/effects/twinkle.c', + 'game/objects/general/boat.c', + 'game/objects/general/bridge_common.c', + 'game/objects/general/bridge_flat.c', + 'game/objects/general/bridge_tilt1.c', + 'game/objects/general/bridge_tilt2.c', + 'game/objects/general/cabin.c', + 'game/objects/general/camera_target.c', + 'game/objects/general/cog.c', + 'game/objects/general/door.c', + 'game/objects/general/drawbridge.c', + 'game/objects/general/earthquake.c', + 'game/objects/general/keyhole.c', + 'game/objects/general/moving_bar.c', + 'game/objects/general/pickup.c', + 'game/objects/general/puzzle_hole.c', + 'game/objects/general/save_crystal.c', + 'game/objects/general/scion1.c', + 'game/objects/general/scion2.c', + 'game/objects/general/scion3.c', + 'game/objects/general/scion4.c', + 'game/objects/general/scion_holder.c', + 'game/objects/general/switch.c', + 'game/objects/general/trapdoor.c', + 'game/objects/general/waterfall.c', + 'game/objects/setup.c', + 'game/objects/traps/damocles_sword.c', + 'game/objects/traps/dart.c', + 'game/objects/traps/dart_emitter.c', + 'game/objects/traps/ember_emitter.c', + 'game/objects/traps/falling_block.c', + 'game/objects/traps/falling_ceiling.c', + 'game/objects/traps/flame_emitter.c', + 'game/objects/traps/lava_wedge.c', + 'game/objects/traps/lightning_emitter.c', + 'game/objects/traps/midas_touch.c', + 'game/objects/traps/movable_block.c', + 'game/objects/traps/pendulum.c', + 'game/objects/traps/rolling_ball.c', + 'game/objects/traps/sliding_pillar.c', + 'game/objects/traps/spikes.c', + 'game/objects/traps/teeth_trap.c', + 'game/objects/traps/thors_hammer_handle.c', + 'game/objects/traps/thors_hammer_head.c', + 'game/objects/vars.c', + 'game/option/option.c', + 'game/option/option_compass.c', + 'game/option/option_control.c', + 'game/option/option_control_pick.c', + 'game/option/option_graphics.c', + 'game/option/option_passport.c', + 'game/option/option_sound.c', + 'game/output.c', + 'game/overlay.c', + 'game/packer.c', + 'game/phase/phase.c', + 'game/phase/phase_cutscene.c', + 'game/phase/phase_demo.c', + 'game/phase/phase_game.c', + 'game/phase/phase_inventory.c', + 'game/phase/phase_pause.c', + 'game/phase/phase_picture.c', + 'game/phase/phase_stats.c', + 'game/random.c', + 'game/requester.c', + 'game/room.c', + 'game/room_draw.c', + 'game/savegame/savegame.c', + 'game/savegame/savegame_bson.c', + 'game/savegame/savegame_legacy.c', + 'game/screen.c', + 'game/shell.c', + 'game/sound.c', + 'game/stats.c', + 'game/text.c', + 'game/viewport.c', + 'global/enum_map.c', + 'global/vars.c', + 'math/math.c', + 'math/math_misc.c', + 'math/matrix.c', + 'specific/s_clock.c', + 'specific/s_fmv.c', + 'specific/s_input.c', + 'specific/s_output.c', + 'specific/s_shell.c', + resources, +] + +dependencies = [ + dep_trx, + dep_avcodec, + dep_avformat, + dep_avutil, + dep_mathlibrary, + dep_sdl2, + dep_swresample, + dep_swscale, + dep_zlib, +] + +executable( + 'TR1X', + sources, + dependencies: dependencies, + link_args: link_args, + gui_app: true, + install: true, +) + +if host_machine.system() == 'darwin' + install_subdir('../../data/ship/cfg', install_dir : 'Contents/Resources') + install_subdir('../../data/ship/data', install_dir : 'Contents/Resources') + install_subdir('../../data/ship/shaders', install_dir : 'Contents/Resources') + install_data('../../data/tr1/mac/icon.icns', install_dir : 'Contents/Resources') + install_data('../../data/tr1/mac/Info.plist', install_dir : 'Contents') + meson.add_install_script('../../tools/tr1/mac/bundle_dylibs') +endif diff --git a/src/tr2/meson.build b/src/tr2/meson.build new file mode 100644 index 000000000..9568ac512 --- /dev/null +++ b/src/tr2/meson.build @@ -0,0 +1,203 @@ +project('TR2X', ['c'], + default_options: [ + 'c_std=c17', + 'warning_level=2', + ], +) + +if host_machine.system() == 'darwin' + gfx_gl_default_backend = 'GFX_GL_33C' +else + gfx_gl_default_backend = 'GFX_GL_21' +endif + +trx = subproject('libtrx', default_options: { + 'tr_version': '2', + 'gfx_gl_default_backend': gfx_gl_default_backend, +}) +c_compiler = meson.get_compiler('c') + +fs = import('fs') +relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) +build_opts = [ + '-Wno-unused', + '-Wno-address-of-packed-member', + '-DMESON_BUILD', + '-fno-omit-frame-pointer', + '-ffile-prefix-map=@0@/='.format(relative_dir), +] + trx.get_variable('defines') + +add_project_arguments(build_opts, language: 'c') + +staticdeps = get_option('staticdeps') + +null_dep = dependency('', required: false) +dep_trx = trx.get_variable('dep_trx') +dep_sdl2 = dependency('SDL2', static: staticdeps) +dep_mathlibrary = c_compiler.find_library('m', static: staticdeps, required : false) + +# autogenerated files +exe_resources = [] +dll_resources = [] +python3 = find_program('python3', required: true) +git = find_program('git', required: true) + +init = custom_target( + 'fake_init', + output: ['init.c'], + command: [python3, meson.source_root() + '/../../tools/tr2/generate_init', '-o', meson.current_build_dir() / '@OUTPUT0@'], + build_always_stale: true, +) + +version_rc = custom_target( + 'fake_version', + output: ['version.rc'], + command: [python3, meson.source_root() + '/../../tools/tr2/generate_rcfile', '-o', '@OUTPUT0@'], + build_always_stale: true, +) + +icon_rc = custom_target( + 'fake_icon', + output: ['icon.rc'], + command: [python3, meson.source_root() + '/../../tools/tr2/generate_rcfile', '-o', '@OUTPUT0@'], +) + +link_args = [] + +if host_machine.system() == 'windows' + windows = import('windows') + + version_resource = windows.compile_resources(version_rc) + icon_resource = windows.compile_resources(icon_rc) + exe_resources = [version_resource, icon_resource] + dll_resources = [version_resource] + + link_args += ['-static'] +endif + +exe_sources = [ + 'main_exe.c', + exe_resources, +] + +dll_sources = [ + init, + 'config.c', + 'config_map.c', + 'decomp/decomp.c', + 'decomp/effects.c', + 'decomp/stats.c', + 'game/background.c', + 'game/backpack.c', + 'game/box.c', + 'game/camera.c', + 'game/clock.c', + 'game/collide.c', + 'game/console/common.c', + 'game/console/setup.c', + 'game/creature.c', + 'game/demo.c', + 'game/effects.c', + 'game/game.c', + 'game/game_string.c', + 'game/gameflow.c', + 'game/gameflow/gameflow_new.c', + 'game/gameflow/reader.c', + 'game/gun/gun.c', + 'game/gun/gun_misc.c', + 'game/gun/gun_pistols.c', + 'game/gun/gun_rifle.c', + 'game/hwr.c', + 'game/input.c', + 'game/inventory/backpack.c', + 'game/inventory/common.c', + 'game/inventory/ring.c', + 'game/inventory/vars.c', + 'game/items.c', + 'game/lara/cheat.c', + 'game/lara/col.c', + 'game/lara/common.c', + 'game/lara/control.c', + 'game/lara/draw.c', + 'game/lara/look.c', + 'game/lara/misc.c', + 'game/lara/state.c', + 'game/level.c', + 'game/los.c', + 'game/lot.c', + 'game/math.c', + 'game/math_misc.c', + 'game/matrix.c', + 'game/music/music_backend_cdaudio.c', + 'game/music/music_backend_files.c', + 'game/music/music_main.c', + 'game/objects/common.c', + 'game/objects/creatures/bird.c', + 'game/objects/creatures/diver.c', + 'game/objects/effects/ember.c', + 'game/objects/effects/flame.c', + 'game/objects/general/body_part.c', + 'game/objects/general/door.c', + 'game/objects/general/final_level_counter.c', + 'game/objects/traps/ember_emitter.c', + 'game/objects/traps/flame_emitter.c', + 'game/objects/vars.c', + 'game/objects/vehicles/boat.c', + 'game/option/option.c', + 'game/option/option_compass.c', + 'game/option/option_controls.c', + 'game/option/option_detail.c', + 'game/option/option_passport.c', + 'game/option/option_sound.c', + 'game/output.c', + 'game/overlay.c', + 'game/random.c', + 'game/requester.c', + 'game/room.c', + 'game/room_draw.c', + 'game/savegame/common.c', + 'game/shell.c', + 'game/sound.c', + 'game/text.c', + 'game/ui/common.c', + 'game/ui/controllers/controls.c', + 'game/ui/widgets/controls_column.c', + 'game/ui/widgets/controls_dialog.c', + 'game/ui/widgets/controls_input_selector.c', + 'game/ui/widgets/controls_layout_selector.c', + 'game/ui/widgets/label.c', + 'game/ui/widgets/prompt.c', + 'game/ui/widgets/window.c', + 'global/enum_map.c', + 'global/vars.c', + 'inject_exec.c', + 'inject_util.c', + 'lib/winmm.c', + 'main_dll.c', + 'specific/s_audio_sample.c', + 'specific/s_flagged_string.c', + 'specific/s_input.c', + dll_resources, +] + +dll_dependencies = [ + dep_trx, + dep_sdl2, + dep_mathlibrary, +] + +executable( + 'TR2X', + exe_sources, + name_prefix: '', + link_args: link_args, + gui_app: true, +) + +library( + 'TR2X', + dll_sources, + name_prefix: '', + dependencies: dll_dependencies, + link_args: link_args, +) diff --git a/meson.options b/src/tr2/meson.options similarity index 100% rename from meson.options rename to src/tr2/meson.options diff --git a/src/tr2/subprojects/libtrx b/src/tr2/subprojects/libtrx new file mode 120000 index 000000000..48e3489cf --- /dev/null +++ b/src/tr2/subprojects/libtrx @@ -0,0 +1 @@ +../../libtrx \ No newline at end of file diff --git a/subprojects/libtrx b/subprojects/libtrx deleted file mode 160000 index ee2f6107f..000000000 --- a/subprojects/libtrx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ee2f6107f96c31607d2347e24f6813792c39707f diff --git a/tools/additional_lint b/tools/additional_lint index 66bd2c100..10c883063 100755 --- a/tools/additional_lint +++ b/tools/additional_lint @@ -1,5 +1,73 @@ #!/usr/bin/env python3 -from libtrx.cli.additional_lint import run_script -from tr1x.paths import TR1X_REPO_DIR +import argparse +import sys +from collections.abc import Iterable +from fnmatch import fnmatch +from pathlib import Path -run_script(root_dir=TR1X_REPO_DIR, ignored_patterns=["*.patch", "*.bin"]) +from shared.files import find_versioned_files, is_binary_file +from shared.linting import LintContext, lint_bulk_files, lint_file +from shared.paths import SRC_DIR + +IGNORED_PATTERNS = ["*.patch", "*.bin", "gl_core_3_3.h"] + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("path", type=Path, nargs="*") + parser.add_argument("-D", "--debug", action="store_true") + return parser.parse_args() + + +def filter_files( + files: Iterable[Path], ignored_patterns: list[str] | None, debug: bool +) -> Iterable[Path]: + for path in files: + if is_binary_file(path): + if debug: + print(f"{path} is a binary file, ignoring", file=sys.stderr) + continue + if ignored_patterns and any( + fnmatch(path.name, pattern) for pattern in ignored_patterns + ): + if debug: + print( + f"{path} has a prohibited extension, ignoring", + file=sys.stderr, + ) + continue + yield path + + +def main(root_dir: Path, ignored_patterns: list[str] | None = None) -> None: + args = parse_args() + + context = LintContext( + root_dir=SRC_DIR, + versioned_files=find_versioned_files(root_dir=SRC_DIR), + ) + if args.path: + files = args.path + else: + files = context.versioned_files + files = list( + filter_files( + files, ignored_patterns=IGNORED_PATTERNS, debug=args.debug + ) + ) + + exit_code = 0 + for file in files: + if args.debug: + print(f"Checking {file}...", file=sys.stderr) + for lint_warning in lint_file(context, file): + print(str(lint_warning), file=sys.stderr) + exit_code = 1 + + if args.debug: + print(f"Checking files in bulk {file}...", file=sys.stderr) + for lint_warning in lint_bulk_files(context, files): + print(str(lint_warning), file=sys.stderr) + exit_code = 1 + + exit(exit_code) diff --git a/tools/check_config_defaults b/tools/check_config_defaults index 6c90fbbca..6235a7c59 100755 --- a/tools/check_config_defaults +++ b/tools/check_config_defaults @@ -3,22 +3,27 @@ import argparse from pathlib import Path import pyjson5 -from tr1x.paths import TR1X_TOOLS_DIR - -CONFIG_TOOL_SPEC_PATH = ( - TR1X_TOOLS_DIR / "config/TR1X_ConfigTool/Resources/specification.json" -) +from shared.paths import PROJECT_PATHS def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description=( - "Compares a given TR1X.json5 file with the default specifications " + "Compares a given configuration file with the default specifications " "of the config tool to find changes in the default values" ) ) parser.add_argument( - "path", type=Path, help="path to the freshly written TR1X.json5" + "-v", + "--version", + type=int, + help="game version to check the configuration file for", + required=True, + ) + parser.add_argument( + "path", + type=Path, + help="path to the freshly written configuration file", ) return parser.parse_args() @@ -26,7 +31,11 @@ def parse_args() -> argparse.Namespace: def main() -> None: args = parse_args() game_config = pyjson5.loads(args.path.read_text()) - tool_spec = pyjson5.loads(CONFIG_TOOL_SPEC_PATH.read_text()) + tool_spec_path = ( + PROJECT_PATHS[args.version].tools_dir + / "config/TR1X_ConfigTool/Resources/specification.json" + ) + tool_spec = pyjson5.loads(tool_spec_path.read_text()) spec_map = { option["Field"]: option["DefaultValue"] diff --git a/tools/generate_icon b/tools/generate_icon new file mode 100755 index 000000000..e499e648b --- /dev/null +++ b/tools/generate_icon @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# regenerate the .ICO file from .PSD. +import argparse +from pathlib import Path + +from shared.icons import generate_icon + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("path", type=Path) + parser.add_argument("-o", "--output", type=Path, required=True) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + if args.output.exists(): + args.output.unlink() + generate_icon(args.path, args.output) + + +if __name__ == "__main__": + main() diff --git a/tools/generate_images b/tools/generate_images index 3c7245848..b614367e3 100755 --- a/tools/generate_images +++ b/tools/generate_images @@ -3,10 +3,11 @@ import argparse from pathlib import Path from subprocess import check_call -from tr1x.paths import TR1X_DATA_DIR +from shared.paths import TR1Paths -SOURCE_DIR = TR1X_DATA_DIR / "images" -TARGET_DIR = TR1X_DATA_DIR / "ship/data/images" +SOURCE_DIR = TR1Paths.data_dir / "images" +TARGET_DIR = TR1Paths.shipped_data_dir / "data/images" +print(SOURCE_DIR) def format_size(size: int) -> str: diff --git a/tools/get_version b/tools/get_version index 877abaa53..3098131e5 100755 --- a/tools/get_version +++ b/tools/get_version @@ -1,4 +1,19 @@ #!/usr/bin/env python3 -from libtrx.versioning import generate_version +import argparse -print(generate_version(), end="") +from shared.versioning import generate_version + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("version", choices=[1, 2], type=int) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + print(generate_version(args.version), end="") + + +if __name__ == "__main__": + main() diff --git a/tools/libtrx b/tools/libtrx deleted file mode 120000 index f9343f13c..000000000 --- a/tools/libtrx +++ /dev/null @@ -1 +0,0 @@ -../subprojects/libtrx/tools/libtrx \ No newline at end of file diff --git a/tools/output_current_changelog b/tools/output_current_changelog index 1c75d19ae..90ba4eecf 100755 --- a/tools/output_current_changelog +++ b/tools/output_current_changelog @@ -1,5 +1,43 @@ #!/usr/bin/env python3 -from libtrx.changelog import get_current_version_changelog -from tr1x.paths import TR1X_REPO_DIR +import argparse -print(get_current_version_changelog(TR1X_REPO_DIR / "CHANGELOG.md")) +from shared.git import Git +from shared.versioning import generate_version +from shared.changelog import get_current_version_changelog +from shared.paths import PROJECT_PATHS + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("version", choices=['1', '2', 'all']) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + + commit_hash = Git().get_current_commit_hash() + + if args.version == 'all': + print(f"**Commit: {commit_hash}** ") + print() + for version, project_paths in PROJECT_PATHS.items(): + commit_tag = generate_version(version) + print(f'### TR{version}X changes') + print() + print(f'**Tag: {commit_tag}**') + print() + print(get_current_version_changelog(project_paths.changelog_path)) + print() + else: + project_paths = PROJECT_PATHS.get(int(args.version)) + commit_tag = generate_version(int(args.version)) + print(f"**Commit: {commit_hash}** ") + print(f"**Tag: {commit_tag}**") + print() + print("### Changes") + print(get_current_version_changelog(project_paths.changelog_path)) + + +if __name__ == "__main__": + main() diff --git a/tools/output_release_name b/tools/output_release_name new file mode 100755 index 000000000..3dd7318d2 --- /dev/null +++ b/tools/output_release_name @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import argparse + +from shared.versioning import generate_version + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("game_version", choices=["1", "2", "all"]) + return parser.parse_args() + + +def get_release_name(game_version: str) -> str: + match str(game_version): + case "1": + return f"TR1X {generate_version(1)}" + case "2": + return f"TR2X {generate_version(2)}" + case "all": + return "Development snapshot" + return "Unknown release name" + + +def main() -> None: + args = parse_args() + print(get_release_name(args.game_version)) + + +if __name__ == "__main__": + main() diff --git a/tools/release b/tools/release new file mode 100755 index 000000000..96f892d00 --- /dev/null +++ b/tools/release @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +import argparse +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from shared.changelog import update_changelog_to_new_version +from shared.git import Git +from shared.paths import PROJECT_PATHS + + +@dataclass +class Options: + stable_branch: str = "stable" + develop_branch: str = "develop" + + +class BaseCommand: + name: str = NotImplemented + help: str = NotImplemented + + def __init__(self, git: Git) -> None: + self.git = git + + def decorate_parser(self, parser: argparse.ArgumentParser) -> None: + parser.add_argument("game_version", choices=[1, 2], type=int) + parser.add_argument("version") + + def run(self, args: argparse.Namespace, options: Options) -> None: + raise NotImplementedError("not implemented") + + +class CommitCommand(BaseCommand): + name = "commit" + help = "Create and tag a commit with the release information" + + def decorate_parser(self, parser: argparse.ArgumentParser) -> None: + super().decorate_parser(parser) + parser.add_argument( + "-d", + "--dry-run", + action="store_true", + help="only output the changelog to stdout, do not commit anything", + ) + + def run(self, args: argparse.Namespace, options: Options) -> None: + # self.git.checkout_branch("develop") + old_tag = self.git.get_branch_version("origin/stable") + new_tag = f"tr{args.game_version}-{args.version}" + + changelog_path = PROJECT_PATHS[args.game_version].changelog_path + old_changelog = changelog_path.read_text() + new_changelog = update_changelog_to_new_version( + old_changelog, + old_tag=old_tag, + new_tag=new_tag, + new_version_name=args.version, + stable_branch=options.stable_branch, + develop_branch=options.develop_branch, + ) + if old_changelog == new_changelog: + return + + if args.dry_run: + print(new_changelog, end="") + return + + changelog_path.write_text(new_changelog) + self.git.add(changelog_path) + self.git.commit(f"docs/tr{args.game_version}: release {args.version}") + self.git.delete_tag(new_tag) + self.git.create_tag(new_tag) + + +class BranchCommand(BaseCommand): + name = "branch" + help = "Merge branch to the specified tag" + + def run(self, args: argparse.Namespace, options: Options) -> None: + new_tag = f"tr{args.game_version}-{args.version}" + self.git.checkout_branch("stable") + self.git.reset(new_tag, hard=True) + self.git.checkout_branch("develop") + + +class PushCommand(BaseCommand): + name = "push" + help = ( + "Push the develop and stable branches, and the version tag to GitHub" + ) + + def decorate_parser(self, parser: argparse.ArgumentParser) -> None: + super().decorate_parser(parser) + parser.add_argument( + "-f", + "--force", + action="store_true", + help="force push all targets", + ) + + def run(self, args: argparse.Namespace, options: Options) -> None: + new_tag = f"tr{args.game_version}-{args.version}" + self.git.push( + "origin", + ["develop", "stable", new_tag], + force=args.force, + force_with_lease=not args.force, + ) + + +def parse_args(commands: list[BaseCommand]) -> None: + parser = argparse.ArgumentParser( + description="Argument parser with subcommands" + ) + subparsers = parser.add_subparsers(title="subcommands", dest="subcommand") + for command in commands: + subparser = subparsers.add_parser(command.name, help=command.help) + command.decorate_parser(subparser) + subparser.set_defaults(command=command) + + result = parser.parse_args() + if not hasattr(result, "command"): + parser.error("missing command") + return result + + +def main() -> None: + git = Git() + commands = [ + command_cls(git=git) for command_cls in BaseCommand.__subclasses__() + ] + options = Options() + args = parse_args(commands) + args.command.run(args, options) + + +if __name__ == "__main__": + main() diff --git a/tools/tr1x/__init__.py b/tools/shared/__init__.py similarity index 100% rename from tools/tr1x/__init__.py rename to tools/shared/__init__.py diff --git a/tools/shared/changelog.py b/tools/shared/changelog.py new file mode 100644 index 000000000..070e1590d --- /dev/null +++ b/tools/shared/changelog.py @@ -0,0 +1,39 @@ +import re +from datetime import datetime +from pathlib import Path + + +def get_current_version_changelog(changelog_path: Path) -> str: + sections = [ + section + for section in changelog_path.read_text().split("\n\n") + if re.search(r"- \w", section) + ] + if sections: + section = sections[0] + return "\n".join( + line for line in section.splitlines() if not line.startswith("#") + ) + + +def update_changelog_to_new_version( + changelog: str, + old_tag: str, + new_tag: str, + new_version_name: str, + stable_branch: str | None = "stable", + develop_branch: str = "develop", +) -> str: + if f"[{new_version_name}]" in changelog: + return changelog + changelog = re.sub("Unreleased", new_version_name, changelog, count=1) + changelog = re.sub(stable_branch, old_tag, changelog, count=1) + changelog = re.sub(develop_branch, new_tag, changelog, count=1) + changelog = re.sub( + "××××-××-××", datetime.now().strftime("%Y-%m-%d"), changelog + ) + changelog = ( + f"## [Unreleased](https://github.com/LostArtefacts/TRX/compare/{stable_branch or new_tag}...{develop_branch}) - ××××-××-××\n\n" + + changelog + ) + return changelog diff --git a/tools/tr2/tr2x/__init__.py b/tools/shared/cli/__init__.py similarity index 100% rename from tools/tr2/tr2x/__init__.py rename to tools/shared/cli/__init__.py diff --git a/tools/shared/cli/game_docker_entrypoint.py b/tools/shared/cli/game_docker_entrypoint.py new file mode 100644 index 000000000..76b38c8a1 --- /dev/null +++ b/tools/shared/cli/game_docker_entrypoint.py @@ -0,0 +1,139 @@ +import argparse +import os +from dataclasses import dataclass +from pathlib import Path +from subprocess import check_call, run +from typing import Any + +from shared.packaging import create_zip +from shared.versioning import generate_version + + +@dataclass +class Options: + version: int + platform: str + compile_args: list[str] + release_zip_files: list[tuple[Path, str]] + strip_tool = "strip" + upx_tool = "upx" + target = os.environ.get("TARGET", "debug") + compressable_exes: list[Path] | None = None + + @property + def ship_dir(self) -> Path: + return Path(f"/app/data/tr{self.version}/ship/") + + @property + def build_root(self) -> Path: + return Path(f"/app/build/tr{self.version}/{self.platform}/") + + @property + def build_target(self) -> str: + return f"src/tr{self.version}" + + @property + def release_zip_filename_fmt(self) -> str: + platform = self.platform + if platform == "win": + platform = "windows" + return f"TR{self.version}X-{{version}}-{platform.title()}.zip" + + +def compress_exe(options: Options, path: Path) -> None: + if run([options.upx_tool, "-t", str(path)]).returncode != 0: + check_call([options.strip_tool, str(path)]) + check_call([options.upx_tool, str(path)]) + + +class BaseCommand: + name: str = NotImplemented + help: str = NotImplemented + + def decorate_parser(self, parser: argparse.ArgumentParser) -> None: + pass + + def run(self, args: argparse.Namespace) -> None: + raise NotImplementedError("not implemented") + + +class CompileCommand(BaseCommand): + name = "compile" + + def run(self, args: argparse.Namespace, options: Options) -> None: + pkg_config_path = os.environ.get("PKG_CONFIG_PATH") + + if not (options.build_root / "build.ninja").exists(): + command = [ + "meson", + "setup", + "--buildtype", + options.target, + *options.compile_args, + options.build_root, + options.build_target, + ] + if pkg_config_path: + command.extend(["--pkg-config-path", pkg_config_path]) + check_call(command) + + check_call(["meson", "compile"], cwd=options.build_root) + + if options.target == "release": + for exe_path in options.compressable_exes: + compress_exe(options, exe_path) + + +class PackageCommand(BaseCommand): + name = "package" + + def decorate_parser(self, parser: argparse.ArgumentParser) -> None: + parser.add_argument("-o", "--output", type=Path) + + def run(self, args: argparse.Namespace, options: Options) -> None: + if args.output: + zip_path = args.output + else: + zip_path = Path( + options.release_zip_filename_fmt.format( + version=generate_version(options.version) + ) + ) + + source_files = [ + *[ + (path, path.relative_to(options.ship_dir)) + for path in options.ship_dir.rglob("*") + if path.is_file() + ], + *options.release_zip_files, + ] + + create_zip(zip_path, source_files) + print(f"Created {zip_path}") + + +def parse_args(commands: dict[str, BaseCommand]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Docker entrypoint") + subparsers = parser.add_subparsers(dest="action", help="Subcommands") + parser.set_defaults(action="compile", command=commands["compile"]) + + for command in commands.values(): + subparser = subparsers.add_parser(command.name, help=command.help) + command.decorate_parser(subparser) + subparser.set_defaults(command=command) + result = parser.parse_args() + # if not hasattr(result, "command"): + # args.action = "compile" + # args.command = CompileCommand + return result + + +def run_script(**kwargs: Any) -> None: + commands = { + command_cls.name: command_cls() + for command_cls in BaseCommand.__subclasses__() + } + args = parse_args(commands) + options = Options(**kwargs) + args.command.run(args, options) diff --git a/tools/shared/files.py b/tools/shared/files.py new file mode 100644 index 000000000..9aec4de10 --- /dev/null +++ b/tools/shared/files.py @@ -0,0 +1,53 @@ +import re +from collections.abc import Iterable +from pathlib import Path +from subprocess import check_output + + +def find_files( + root_dir: Path, + extensions: list[str] | None = None, + exception_patterns: list[re.Pattern[str]] | None = None, +) -> Iterable[Path]: + stack: list[Path] = [root_dir] + + while stack: + parent_dir = stack.pop() + for path in parent_dir.iterdir(): + rel_path = path.relative_to(root_dir) + if exception_patterns and any( + pattern.match(str(rel_path)) for pattern in exception_patterns + ): + continue + + if path.is_dir(): + stack.append(path) + continue + + if not path.is_file(): + continue + + if extensions and path.suffix not in extensions: + continue + + yield path + + +def find_versioned_files(root_dir: Path | None = None) -> Iterable[Path]: + for line in check_output( + ["git", "ls-files"], cwd=root_dir, text=True + ).splitlines(): + path = Path(line) + if not path.is_dir(): + if root_dir: + yield root_dir / path + else: + yield path + + +def is_binary_file(path: Path) -> bool: + try: + path.read_text(encoding="utf-8") + except UnicodeDecodeError: + return True + return False diff --git a/tools/shared/git.py b/tools/shared/git.py new file mode 100644 index 000000000..a520051cc --- /dev/null +++ b/tools/shared/git.py @@ -0,0 +1,77 @@ +from pathlib import Path +from subprocess import check_output, run + + +class Git: + def __init__(self, repo_dir: Path | None = None) -> None: + self.repo_dir = repo_dir + + def checkout_branch(self, branch_name: str) -> None: + if self.check_output(["git", "diff", "--cached", "--name-only"]): + raise RuntimeError("Staged files") + self.check_output(["git", "checkout", branch_name]) + + def reset(self, target: str, hard: bool = False) -> None: + self.check_output( + ["git", "reset", "develop", *(["--hard"] if hard else [])] + ) + + def delete_tag(self, tag_name: str) -> None: + self.grab_output(["git", "tag", "-d", tag_name]) + + def create_tag(self, tag_name: str) -> None: + self.check_output(["git", "tag", tag_name]) + + def add(self, target: str) -> None: + self.check_output(["git", "add", target]) + + def commit(self, message: str) -> None: + self.check_output(["git", "commit", "-m", message]) + + def push( + self, + upstream: str, + targets: list[str], + force_with_lease: bool = False, + force: bool = False, + ) -> None: + self.check_output( + [ + "git", + "push", + upstream, + *targets, + *(["--force-with-lease"] if force else []), + *(["--force"] if force else []), + ] + ) + + def get_current_commit_hash(self) -> str: + return self.grab_output( + ["git", "log", "-1", "--pretty=format:%H"] + ).stdout.strip() + + def get_branch_version( + self, pattern: str | None = None, branch: str | None = None + ) -> str: + return self.grab_output( + [ + "git", + "describe", + *([branch] if branch else ["--dirty"]), + "--always", + "--abbrev=7", + "--tags", + "--exclude", + "latest", + *(["--match", pattern] if pattern else []), + ] + ).stdout.strip() + + def grab_output(self, *args, **kwargs): + return run( + *args, **kwargs, capture_output=True, text=True, cwd=self.repo_dir + ) + + def check_output(self, *args, **kwargs): + return check_output(*args, **kwargs, cwd=self.repo_dir) diff --git a/tools/shared/icons.py b/tools/shared/icons.py new file mode 100644 index 000000000..6361e1a97 --- /dev/null +++ b/tools/shared/icons.py @@ -0,0 +1,78 @@ +import tempfile +from dataclasses import dataclass +from pathlib import Path +from subprocess import check_call + + +@dataclass +class IconSpec: + size: int + type: str + + +SPECS = [ + IconSpec(size=32, type="bmp"), + IconSpec(size=16, type="bmp"), + IconSpec(size=256, type="png"), + IconSpec(size=128, type="png"), + IconSpec(size=64, type="png"), + IconSpec(size=48, type="png"), + IconSpec(size=32, type="png"), + IconSpec(size=16, type="png"), +] + + +def resize_transformer(path: Path, spec: IconSpec) -> None: + check_call( + [ + "convert", + f"{path}[0]", + "-filter", + "lanczos", + "-resize", + f"{spec.size}x{spec.size}", + f"PNG:{path}", + ] + ) + + +def quantize_transformer(path: Path, spec: IconSpec) -> None: + quantized_path = path.with_stem(f"{path.stem}-quantized") + check_call(["pngquant", path, "--output", quantized_path]) + path.write_bytes(quantized_path.read_bytes()) + quantized_path.unlink() + + +def optimize_transformer(path: Path, spec: IconSpec) -> None: + check_call(["zopflipng", "-y", path, path]) + + +def convert_transformer(path: Path, spec: IconSpec) -> None: + if spec.type != "png": + check_call(["convert", path, f"{spec.type.upper()}:{path}"]) + + +TRANSFORMERS = [ + resize_transformer, + quantize_transformer, + optimize_transformer, + convert_transformer, +] + + +def generate_icon(source_path: Path, target_path: Path) -> None: + aux_paths = [] + + with tempfile.TemporaryDirectory() as tmpdir: + tmp_path = Path(tmpdir) + for spec in SPECS: + aux_path = tmp_path / f"{spec.size}-{spec.type}.tmp" + aux_path.write_bytes(source_path.read_bytes()) + for transform in TRANSFORMERS: + transform(aux_path, spec) + + aux_paths.append(aux_path) + + # NOTE: image order is important for certain software. + check_call(["identify", *aux_paths]) + check_call(["convert", *aux_paths, target_path]) diff --git a/tools/tr2/tr2x/ida_progress.py b/tools/shared/ida_progress.py similarity index 100% rename from tools/tr2/tr2x/ida_progress.py rename to tools/shared/ida_progress.py diff --git a/tools/shared/import_sorter.py b/tools/shared/import_sorter.py new file mode 100644 index 000000000..50e212cd4 --- /dev/null +++ b/tools/shared/import_sorter.py @@ -0,0 +1,96 @@ +import functools +import re +from pathlib import Path +from shutil import which +from subprocess import run + +from shared.files import find_versioned_files + + +def custom_sort(source: list[str], forced_order: list[str]) -> list[str]: + def key_func(item: str) -> tuple[int, int, str]: + if item in forced_order: + return (forced_order[0], forced_order.index(item)) + return (item, 0) + + return sorted(source, key=key_func) + + +def sort_imports( + path: Path, + root_dir: Path, + own_include_map: dict[str, str], + fix_map: dict[str, str], + forced_order: list[str], +) -> None: + source = path.read_text() + try: + rel_path = path.relative_to(root_dir) + except ValueError: + return + + own_include = str(rel_path.with_suffix(".h")) + own_include = own_include_map.get(str(rel_path), own_include) + + for key, value in fix_map.items(): + source = re.sub( + r'(#include ["<])' + re.escape(key) + '([">])', + r"\1" + value + r"\2", + source, + ) + + def cb(match): + includes = re.findall(r'#include (["<][^"<>]+[">])', match.group(0)) + groups = { + "self": set(), + "local": set(), + "shared": set(), + "external": set(), + } + for include in includes: + if include.strip('"') == own_include: + groups["self"].add(include) + elif include.startswith(" None: + for path in paths: + sort_imports( + path, + root_dir=root_dir, + own_include_map=own_include_map, + fix_map=fix_map, + forced_order=forced_order, + ) diff --git a/tools/shared/linting.py b/tools/shared/linting.py new file mode 100644 index 000000000..c6078a398 --- /dev/null +++ b/tools/shared/linting.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +import json +import re +from collections.abc import Callable, Iterable +from dataclasses import dataclass +from pathlib import Path + + +@dataclass +class LintContext: + root_dir: Path + versioned_files: list[Path] + + +@dataclass +class LintWarning: + path: Path + message: str + line: int | None = None + + def __str__(self) -> str: + prefix = str(self.path) + if self.line is not None: + prefix += f":{self.line}" + return f"{prefix}: {self.message}" + + +def lint_json_validity( + context: LintContext, path: Path +) -> Iterable[LintWarning]: + if path.suffix != ".json": + return + try: + json.loads(path.read_text()) + except json.JSONDecodeError as ex: + yield LintWarning(path, f"malformed JSON: {ex!s}") + + +def lint_newlines(context: LintContext, path: Path) -> Iterable[LintWarning]: + text = path.read_text(encoding="utf-8") + if not text: + return + if not text.endswith("\n"): + yield LintWarning(path, "missing newline character at end of file") + if text.endswith("\n\n"): + yield LintWarning(path, "extra newline character at end of file") + + +def lint_trailing_whitespace( + context: LintContext, path: Path +) -> Iterable[LintWarning]: + if path.suffix == ".md": + return + for i, line in enumerate(path.open("r"), 1): + if line.rstrip("\n").endswith(" "): + yield LintWarning(path, "trailing whitespace", line=i) + + +def lint_const_primitives( + context: LintContext, path: Path +) -> Iterable[LintWarning]: + if path.suffix != ".h": + return + for i, line in enumerate(path.open("r"), 1): + if re.search(r"const (int[a-z0-9_]*|bool)\b\s*[a-z]", line): + yield LintWarning(path, "useless const", line=i) + if re.search(r"\*\s*const", line): + yield LintWarning(path, "useless const", line=i) + + +def lint_game_strings( + context: LintContext, paths: list[Path] +) -> Iterable[LintWarning]: + def_paths = list(context.root_dir.rglob("**/game_string.def")) + defs = [ + match.group(1) + for path in def_paths + for match in re.finditer( + r"GS_DEFINE\(([A-Z_]+),.*\)", path.read_text() + ) + ] + if not defs: + return + + path_hints = " or ".join( + str(path.relative_to(context.root_dir)) for path in def_paths + ) + + for path in paths: + if path.suffix != ".c": + continue + for i, line in enumerate(path.open("r"), 1): + for match in re.finditer(r"GS\(([A-Z_]+)\)", line): + def_ = match.group(1) + if def_ in defs: + continue + + yield LintWarning( + path, + f"undefined game string: {def_}. " + f"Make sure it's defined in {path_hints}.", + i, + ) + + +ALL_LINTERS: list[Callable[[LintContext, Path], Iterable[LintWarning]]] = [ + lint_json_validity, + lint_newlines, + lint_trailing_whitespace, + lint_const_primitives, +] + +ALL_BULK_LINTERS: list[ + Callable[[LintContext, list[Path]], Iterable[LintWarning]] +] = [ + lint_game_strings, +] + + +def lint_file(context: LintContext, file: Path) -> Iterable[LintWarning]: + for linter_func in ALL_LINTERS: + yield from linter_func(context, file) + + +def lint_bulk_files( + context: LintContext, files: list[Path] +) -> Iterable[LintWarning]: + for linter_func in ALL_BULK_LINTERS: + yield from linter_func(context, files) diff --git a/tools/shared/packaging.py b/tools/shared/packaging.py new file mode 100644 index 000000000..69cef7d8f --- /dev/null +++ b/tools/shared/packaging.py @@ -0,0 +1,17 @@ +import sys +import zipfile +from collections.abc import Iterable +from pathlib import Path + + +def create_zip( + output_path: Path, source_files: Iterable[tuple[Path, str]] +) -> None: + with zipfile.ZipFile(output_path, "w") as handle: + for source_path, archive_name in source_files: + if not source_path.exists(): + print( + f"WARNING: {source_path} does not exist", file=sys.stderr + ) + continue + handle.write(source_path, archive_name) diff --git a/tools/shared/paths.py b/tools/shared/paths.py new file mode 100644 index 000000000..633355103 --- /dev/null +++ b/tools/shared/paths.py @@ -0,0 +1,34 @@ +from pathlib import Path + +REPO_DIR = Path(__file__).parent +while REPO_DIR.parent != REPO_DIR and not (REPO_DIR / ".git").exists(): + REPO_DIR = REPO_DIR.parent + +TOOLS_DIR = REPO_DIR / "tools" +SRC_DIR = REPO_DIR / "src" +DATA_DIR = REPO_DIR / "data" +DOCS_DIR = REPO_DIR / "docs" + +SHARED_INCLUDE_DIR = SRC_DIR / "libtrx/include/libtrx" +SHARED_SRC_DIR = SRC_DIR / "libtrx" + + +class ProjectPaths: + def __init__(self, folder_name: str) -> None: + self.folder_name = folder_name + + self.data_dir = DATA_DIR / folder_name + self.shipped_data_dir = self.data_dir / "ship" + self.src_dir = SRC_DIR / folder_name + self.tools_dir = TOOLS_DIR / folder_name + self.docs_dir = DOCS_DIR / folder_name + self.changelog_path = self.docs_dir / "CHANGELOG.md" + + +TR1Paths = ProjectPaths(folder_name="tr1") + +TR2Paths = ProjectPaths(folder_name="tr2") +TR2Paths.progress_file = TR2Paths.docs_dir / "progress.txt" +TR2Paths.progress_svg = TR2Paths.docs_dir / "progress.svg" + +PROJECT_PATHS = {1: TR1Paths, 2: TR2Paths} diff --git a/tools/shared/versioning.py b/tools/shared/versioning.py new file mode 100644 index 000000000..c0bd0b18f --- /dev/null +++ b/tools/shared/versioning.py @@ -0,0 +1,22 @@ +import re +from pathlib import Path + +from shared.git import Git + +PATTERN_MAP = { + 1: "tr1-*", + 2: "tr2-*", +} + + +def generate_version(version: int, repo_dir: Path | None = None) -> str: + git = Git(repo_dir=repo_dir) + pattern = PATTERN_MAP[version] + return ( + re.sub( + "^tr[0-9]-", + "", + git.get_branch_version(pattern=pattern, branch=None), + ) + or "?" + ) diff --git a/tools/sort_imports b/tools/sort_imports new file mode 100755 index 000000000..bb7ed9d3b --- /dev/null +++ b/tools/sort_imports @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import argparse + +from shared.import_sorter import sort_imports +from shared.paths import SHARED_SRC_DIR, SRC_DIR, TR1Paths, TR2Paths + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("path", type=Path, nargs="*") + return parser.parse_args() + + +def main() -> None: + args = parse_args() + if args.path: + paths = [path.absolute() for path in args.path] + else: + paths = [ + path + for path in find_versioned_files(root_dir) + if path.suffix in [".c", ".h"] + ] + + sort_imports( + paths=[path for path in paths if path.is_relative_to(TR1Paths.src_dir)], + root_dir=TR1Paths.src_dir, + system_include_dirs=[SRC_DIR], + own_include_map={ + "game/game/game.c": "game/game.h", + "game/game/game_cutscene.c": "game/game.h", + "game/game/game_demo.c": "game/game.h", + "game/game/game_draw.c": "game/game.h", + "game/game/game_pause.c": "game/game.h", + "game/gun/gun.c": "game/gun.h", + "game/inventry.c": "game/inv.h", + "game/invfunc.c": "game/inv.h", + "game/invvars.c": "game/inv.h", + "game/lara/lara.c": "game/lara.h", + "game/option/option.c": "game/option.h", + "game/savegame/savegame.c": "game/savegame.h", + }, + fix_map={"libavcodec/version_major.h": "libavcodec/version.h"}, + forced_order=[], + ) + + sort_imports( + paths=[path for path in paths if path.is_relative_to(TR2Paths.src_dir)], + root_dir=TR2Paths.src_dir, + system_include_dirs=[SRC_DIR], + own_include_map={ + "game/music/music_main.c": "game/music.h", + }, + fix_map={}, + forced_order=["", ""], + ) + + sort_imports( + paths=[path for path in paths if path.is_relative_to(SHARED_SRC_DIR)], + root_dir=SHARED_SRC_DIR, + system_include_dirs=[], + own_include_map={ + "json/bson_write.c": "bson.h", + "json/bson_parse.c": "bson.h", + "json/json_base.c": "json.h", + "json/json_write.c": "json.h", + "json/json_parse.c": "json.h", + "log_unknown.c": "log.h", + "log_linux.c": "log.h", + "log_windows.c": "log.h", + "engine/audio.c": "audio.h", + "engine/audio_sample.c": "audio.h", + "engine/audio_stream.c": "audio.h", + }, + fix_map={}, + forced_order=[ + "", + "", + "", + ], + ) diff --git a/tools/tr1/__init__.py b/tools/tr1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/tr1/config/TR1X_ConfigTool/TR1X_ConfigTool.csproj b/tools/tr1/config/TR1X_ConfigTool/TR1X_ConfigTool.csproj index fa89ea83c..e6a7a199a 100644 --- a/tools/tr1/config/TR1X_ConfigTool/TR1X_ConfigTool.csproj +++ b/tools/tr1/config/TR1X_ConfigTool/TR1X_ConfigTool.csproj @@ -67,7 +67,7 @@ - ..\..\..\..\subprojects\libtrx\tools\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll + ..\..\..\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll diff --git a/tools/tr1/docker/config/entrypoint.sh b/tools/tr1/docker/config/entrypoint.sh index 7b19a53f9..42cf78b6f 100755 --- a/tools/tr1/docker/config/entrypoint.sh +++ b/tools/tr1/docker/config/entrypoint.sh @@ -7,7 +7,7 @@ echo $HOME shopt -s globstar # Build the common lib DLL -cd /app/subprojects/libtrx/tools/config/ +cd /app/tools/config/ rm -rf **/bin **/obj dotnet restore -p:EnableWindowsTargeting=true dotnet publish -c Release -p:EnableWindowsTargeting=true diff --git a/tools/tr1/docker/game-linux/entrypoint.sh b/tools/tr1/docker/game-linux/entrypoint.sh index 69d3f572f..ca4da0764 100755 --- a/tools/tr1/docker/game-linux/entrypoint.sh +++ b/tools/tr1/docker/game-linux/entrypoint.sh @@ -1,17 +1,16 @@ #!/usr/bin/env python3 from pathlib import Path -from libtrx.cli.game_docker_entrypoint import run_script +from shared.cli.game_docker_entrypoint import run_script run_script( - ship_dir=Path("/app/data/tr1/ship/"), - build_root=Path("/app/build/linux/"), + version=1, + platform="linux", compile_args=[], - release_zip_filename="TR1X-{version}-Linux.zip", release_zip_files=[ - (Path("/app/build/linux/TR1X"), "TR1X"), + (Path("/app/build/tr1/linux/TR1X"), "TR1X"), ], compressable_exes=[ - Path("/app/build/linux/TR1X"), + Path("/app/build/tr1/linux/TR1X"), ], ) diff --git a/tools/tr1/docker/game-win/entrypoint.sh b/tools/tr1/docker/game-win/entrypoint.sh index 8993bb74a..d5b5ef3f9 100755 --- a/tools/tr1/docker/game-win/entrypoint.sh +++ b/tools/tr1/docker/game-win/entrypoint.sh @@ -1,24 +1,23 @@ #!/usr/bin/env python3 from pathlib import Path -from libtrx.cli.game_docker_entrypoint import run_script +from shared.cli.game_docker_entrypoint import run_script run_script( - ship_dir=Path("/app/data/tr1/ship/"), - build_root=Path("/app/build/win/"), + version=1, + platform="win", compile_args=[ "--cross", "/app/tools/tr1/docker/game-win/meson_linux_mingw32.txt", ], - release_zip_filename="TR1X-{version}-Windows.zip", release_zip_files=[ - (Path("/app/build/win/TR1X.exe"), "TR1X.exe"), + (Path("/app/build/tr1/win/TR1X.exe"), "TR1X.exe"), ( Path("/app/tools/tr1/config/out/TR1X_ConfigTool.exe"), "TR1X_ConfigTool.exe", ), ], compressable_exes=[ - Path("/app/build/win/TR1X.exe"), + Path("/app/build/tr1/win/TR1X.exe"), ], ) diff --git a/tools/generate_init b/tools/tr1/generate_init similarity index 83% rename from tools/generate_init rename to tools/tr1/generate_init index e1673b361..a24577ca8 100755 --- a/tools/generate_init +++ b/tools/tr1/generate_init @@ -2,7 +2,7 @@ import argparse from pathlib import Path -from libtrx.versioning import generate_version +from shared.versioning import generate_version TEMPLATE = """ const char *g_TR1XVersion = "TR1X {version}"; @@ -16,7 +16,7 @@ def parse_args() -> argparse.Namespace: def get_init_c() -> str: - return TEMPLATE.format(version=generate_version()) + return TEMPLATE.format(version=generate_version(1)) def update_init_c(output_path: Path) -> None: @@ -30,7 +30,7 @@ def main() -> None: if args.output: update_init_c(output_path=args.output) else: - print(args.outptu) + print(get_init_c(), end="") if __name__ == "__main__": diff --git a/tools/generate_rcfile b/tools/tr1/generate_rcfile old mode 100644 new mode 100755 similarity index 67% rename from tools/generate_rcfile rename to tools/tr1/generate_rcfile index 8f354dff1..ffdc02c09 --- a/tools/generate_rcfile +++ b/tools/tr1/generate_rcfile @@ -2,8 +2,8 @@ import argparse from pathlib import Path -from libtrx.versioning import generate_version -from tr1x.paths import TR1X_DATA_DIR +from shared.paths import TR1Paths +from shared.versioning import generate_version def parse_args() -> argparse.Namespace: @@ -17,17 +17,19 @@ def write_rc_template( ) -> None: template = input_path.read_text() template = template.replace("{version}", version) - template = template.replace("{icon_path}", str(TR1X_DATA_DIR / "icon.ico")) + template = template.replace( + "{icon_path}", str(TR1Paths.data_dir / "icon.ico") + ) output_path.write_text(template) def main() -> None: args = parse_args() - version = generate_version() + version = generate_version(1) - for output_path in args.output: + for output_path in args.output or []: write_rc_template( - input_path=TR1X_DATA_DIR / output_path.name, + input_path=TR1Paths.data_dir / output_path.name, output_path=output_path, version=version, ) diff --git a/tools/tr1/inspect_save b/tools/tr1/inspect_save new file mode 100755 index 000000000..d1c8afec3 --- /dev/null +++ b/tools/tr1/inspect_save @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +import argparse +import json +import struct +import zlib +from pathlib import Path + +import bson + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("path", type=Path) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + with args.path.open("rb") as handle: + magic = handle.read(4) + version, compressed_size, uncompressed_size = struct.unpack( + "III", handle.read(12) + ) + data = bson.loads(zlib.decompress(handle.read(uncompressed_size))) + print(json.dumps(data, indent=4)) + + +if __name__ == "__main__": + main() diff --git a/tools/tr1/release b/tools/tr1/release deleted file mode 100755 index 14ca8fc5a..000000000 --- a/tools/tr1/release +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -from libtrx.cli.release import run_script -from tr1x.paths import TR1X_REPO_DIR - -run_script( - project_name="TR1X", - changelog_path=TR1X_REPO_DIR / "CHANGELOG.md", -) diff --git a/tools/tr1/shared b/tools/tr1/shared new file mode 120000 index 000000000..8fba6b66a --- /dev/null +++ b/tools/tr1/shared @@ -0,0 +1 @@ +../shared \ No newline at end of file diff --git a/tools/tr1/sort_imports b/tools/tr1/sort_imports deleted file mode 100755 index 3c049e2bc..000000000 --- a/tools/tr1/sort_imports +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -from libtrx.cli.sort_imports import run_script -from tr1x.paths import TR1X_REPO_DIR, TR1X_SRC_DIR - -run_script( - root_dir=TR1X_SRC_DIR, - include_dirs=[ - TR1X_SRC_DIR, - TR1X_REPO_DIR / "build/linux", - TR1X_REPO_DIR / "build/windows", - ], - system_include_dirs=[TR1X_REPO_DIR / "subprojects/libtrx/include"], - own_include_map={ - "game/game/game.c": "game/game.h", - "game/game/game_cutscene.c": "game/game.h", - "game/game/game_demo.c": "game/game.h", - "game/game/game_draw.c": "game/game.h", - "game/game/game_pause.c": "game/game.h", - "game/gun/gun.c": "game/gun.h", - "game/inventry.c": "game/inv.h", - "game/invfunc.c": "game/inv.h", - "game/invvars.c": "game/inv.h", - "game/lara/lara.c": "game/lara.h", - "game/option/option.c": "game/option.h", - "game/savegame/savegame.c": "game/savegame.h", - }, - fix_map={"libavcodec/version_major.h": "libavcodec/version.h"}, - forced_order=[], -) diff --git a/tools/tr1/update_gameflow b/tools/tr1/update_gameflow deleted file mode 100755 index 8828edc4a..000000000 --- a/tools/tr1/update_gameflow +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -import json -import re -from pathlib import Path - -from tr1x.paths import LIBTRX_INCLUDE_DIR, TR1X_DATA_DIR, TR1X_SRC_DIR - -SHIP_DIR = TR1X_DATA_DIR / "ship" -GAME_STRING_DEF_PATHS = [ - TR1X_SRC_DIR / "game/game_string.def", - LIBTRX_INCLUDE_DIR / "game/game_string.def", -] - - -def get_default_strings_map(paths: list[Path]) -> dict[str, str]: - result: dict[str, str] = {} - for path in paths: - for line in path.read_text().splitlines(): - if match := re.match( - r'^GS_DEFINE\((\w+),\s*"([^"]+)"\)$', line.strip() - ): - result[match.group(1)] = match.group(2) - return result - - -def postprocess_gameflow(gameflow: str, strings_map: dict[str, str]) -> str: - gameflow = re.sub( - r'^( "strings": {)[^}]*(})', - ' "strings": {\n' - + "\n".join( - f" {json.dumps(key)}: {json.dumps(value)}," - for key, value in sorted(strings_map.items(), key=lambda kv: kv[0]) - ) - + "\n }", - gameflow, - flags=re.M | re.DOTALL, - ) - return gameflow - - -def main() -> None: - strings_map = get_default_strings_map(GAME_STRING_DEF_PATHS) - assert strings_map - - for gameflow_path in SHIP_DIR.rglob("*gameflow*.json*"): - old_gameflow = gameflow_path.read_text() - new_gameflow = postprocess_gameflow(old_gameflow, strings_map) - if new_gameflow != old_gameflow: - gameflow_path.write_text(new_gameflow) - - -if __name__ == "__main__": - main() diff --git a/tools/tr1x/paths.py b/tools/tr1x/paths.py deleted file mode 100644 index 0d846cb94..000000000 --- a/tools/tr1x/paths.py +++ /dev/null @@ -1,10 +0,0 @@ -from pathlib import Path - -TR1X_TOOLS_DIR = Path(__file__).parent.parent / "tr1" -TR1X_REPO_DIR = TR1X_TOOLS_DIR.parent -TR1X_DATA_DIR = TR1X_REPO_DIR / "data/tr1" -TR1X_SRC_DIR = TR1X_REPO_DIR / "src/tr1" - -LIBTRX_REPO_DIR = TR1X_REPO_DIR / "subprojects/libtrx" -LIBTRX_SRC_DIR = LIBTRX_REPO_DIR / "src" -LIBTRX_INCLUDE_DIR = LIBTRX_REPO_DIR / "include/libtrx" diff --git a/tools/tr2/__init__.py b/tools/tr2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/tr2/config/TR2X_ConfigTool/TR2X_ConfigTool.csproj b/tools/tr2/config/TR2X_ConfigTool/TR2X_ConfigTool.csproj index 6c86ddc4c..9ec10c3b2 100644 --- a/tools/tr2/config/TR2X_ConfigTool/TR2X_ConfigTool.csproj +++ b/tools/tr2/config/TR2X_ConfigTool/TR2X_ConfigTool.csproj @@ -59,7 +59,7 @@ - ..\..\..\subprojects\libtrx\tools\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll + ..\..\..\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll diff --git a/tools/tr2/docker/config/Dockerfile b/tools/tr2/docker/config/Dockerfile index b84c495ae..7e074954c 100644 --- a/tools/tr2/docker/config/Dockerfile +++ b/tools/tr2/docker/config/Dockerfile @@ -2,4 +2,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env ENV HOME /app WORKDIR /app -ENTRYPOINT ["/app/tools/docker/config/entrypoint.sh"] +ENTRYPOINT ["/app/tools/tr2/docker/config/entrypoint.sh"] diff --git a/tools/tr2/docker/config/entrypoint.sh b/tools/tr2/docker/config/entrypoint.sh index f11d7eb39..4d18d4e91 100755 --- a/tools/tr2/docker/config/entrypoint.sh +++ b/tools/tr2/docker/config/entrypoint.sh @@ -7,13 +7,13 @@ echo $HOME shopt -s globstar # Build the common lib DLL -cd /app/subprojects/libtrx/tools/config/ +cd /app/tools/config/ rm -rf **/bin **/obj dotnet restore -p:EnableWindowsTargeting=true dotnet publish -c Release -p:EnableWindowsTargeting=true # Build the main executable -cd /app/tools/config/ +cd /app/tools/tr2/config/ rm -rf **/bin **/obj **/out/* dotnet restore dotnet publish -c Release -o out diff --git a/tools/tr2/docker/game-win/Dockerfile b/tools/tr2/docker/game-win/Dockerfile index 4e3a684e5..130f8e3dd 100644 --- a/tools/tr2/docker/game-win/Dockerfile +++ b/tools/tr2/docker/game-win/Dockerfile @@ -125,4 +125,4 @@ RUN apt-get install -y \ ninja ENV PYTHONPATH=/app/tools/ -ENTRYPOINT ["/app/tools/docker/game-win/entrypoint.sh"] +ENTRYPOINT ["/app/tools/tr2/docker/game-win/entrypoint.sh"] diff --git a/tools/tr2/docker/game-win/entrypoint.sh b/tools/tr2/docker/game-win/entrypoint.sh index c9f4bf5e6..3ab62dd9b 100755 --- a/tools/tr2/docker/game-win/entrypoint.sh +++ b/tools/tr2/docker/game-win/entrypoint.sh @@ -1,26 +1,25 @@ #!/usr/bin/env python3 from pathlib import Path -from libtrx.cli.game_docker_entrypoint import run_script +from shared.cli.game_docker_entrypoint import run_script run_script( - ship_dir=Path("/app/data/ship/"), - build_root=Path("/app/build/win/"), + version=2, + platform="win", compile_args=[ "--cross", - "/app/tools/docker/game-win/meson_linux_mingw32.txt", + "/app/tools/tr2/docker/game-win/meson_linux_mingw32.txt", ], - release_zip_filename="TR2X-{version}-Windows.zip", release_zip_files=[ - (Path("/app/build/win/TR2X.exe"), "TR2X.exe"), - (Path("/app/build/win/TR2X.dll"), "TR2X.dll"), + (Path("/app/build/tr2/win/TR2X.exe"), "TR2X.exe"), + (Path("/app/build/tr2/win/TR2X.dll"), "TR2X.dll"), ( - Path("/app/tools/config/out/TR2X_ConfigTool.exe"), + Path("/app/tools/tr2/config/out/TR2X_ConfigTool.exe"), "TR2X_ConfigTool.exe", ), ], compressable_exes=[ - Path("/app/build/win/TR2X.exe"), - Path("/app/build/win/TR2X.dll"), + Path("/app/build/tr2/win/TR2X.exe"), + Path("/app/build/tr2/win/TR2X.dll"), ], ) diff --git a/tools/tr2/ffmpeg_flags.txt b/tools/tr2/ffmpeg_flags.txt deleted file mode 120000 index b10b00a4f..000000000 --- a/tools/tr2/ffmpeg_flags.txt +++ /dev/null @@ -1 +0,0 @@ -../subprojects/libtrx/tools/ffmpeg_flags.txt \ No newline at end of file diff --git a/tools/tr2/generate_funcs b/tools/tr2/generate_funcs index 8d3c63eea..e8823a5d6 100755 --- a/tools/tr2/generate_funcs +++ b/tools/tr2/generate_funcs @@ -1,13 +1,18 @@ #!/usr/bin/env python3 -from pathlib import Path import re +from pathlib import Path -from tr2x.ida_progress import FUNC_PTR_RE, VAR_RE, Symbol, parse_progress_file -from tr2x.paths import TR2X_PROGRESS_FILE, TR2X_SRC_DIR +from shared.ida_progress import ( + FUNC_PTR_RE, + VAR_RE, + Symbol, + parse_progress_file, +) +from shared.paths import TR2Paths -FUNCS_H_FILE = TR2X_SRC_DIR / "global/funcs.h" -VARS_H_FILE = TR2X_SRC_DIR / "global/vars_decomp.h" -TYPES_H_FILE = TR2X_SRC_DIR / "global/types.h" +FUNCS_H_FILE = TR2Paths.src_dir / "global/funcs.h" +VARS_H_FILE = TR2Paths.src_dir / "global/vars_decomp.h" +TYPES_H_FILE = TR2Paths.src_dir / "global/types.h" COMMON_HEADER = [ @@ -145,7 +150,13 @@ def make_types_h(types: list[str]) -> None: "\n".join( [ *header, - "\n\n".join([definition.strip() for definition in types if '// decompiled' not in definition.strip()]), + "\n\n".join( + [ + definition.strip() + for definition in types + if "// decompiled" not in definition.strip() + ] + ), *footer, ] ) @@ -154,7 +165,7 @@ def make_types_h(types: list[str]) -> None: def main() -> None: - progress_file = parse_progress_file(TR2X_PROGRESS_FILE) + progress_file = parse_progress_file(TR2Paths.progress_file) make_funcs_h(progress_file.functions) make_vars_h(progress_file.variables) diff --git a/tools/tr2/generate_ida_importer b/tools/tr2/generate_ida_importer index 8b38094ef..0973930bc 100755 --- a/tools/tr2/generate_ida_importer +++ b/tools/tr2/generate_ida_importer @@ -10,14 +10,15 @@ import tempfile from pathlib import Path import regex -from tr2x.ida_progress import ( + +from shared.ida_progress import ( FUNC_PTR_RE, FUNC_RE, VAR_RE, Symbol, parse_progress_file, ) -from tr2x.paths import TR2X_PROGRESS_FILE +from shared.paths import TR2Paths def generate_types(types: list[str], file) -> None: @@ -109,7 +110,7 @@ def parse_args() -> argparse.Namespace: def main(): args = parse_args() - progress_file = parse_progress_file(TR2X_PROGRESS_FILE) + progress_file = parse_progress_file(TR2Paths.progress_file) output = Path(args.output) with output.open("w") as file: diff --git a/tools/tr2/generate_init b/tools/tr2/generate_init index 8f2e62ed6..461cb0de9 100755 --- a/tools/tr2/generate_init +++ b/tools/tr2/generate_init @@ -2,7 +2,7 @@ import argparse from pathlib import Path -from libtrx.versioning import generate_version +from shared.versioning import generate_version TEMPLATE = """ const char *g_TR2XVersion = "TR2X {version}"; @@ -16,7 +16,7 @@ def parse_args() -> argparse.Namespace: def get_init_c() -> str: - return TEMPLATE.format(version=generate_version()) + return TEMPLATE.format(version=generate_version(2)) def update_init_c(output_path: Path) -> None: @@ -30,7 +30,7 @@ def main() -> None: if args.output: update_init_c(output_path=args.output) else: - print(args.output) + print(get_init_c(), end="") if __name__ == "__main__": diff --git a/tools/tr2/generate_rcfile b/tools/tr2/generate_rcfile index 745d1574f..7028c8312 100755 --- a/tools/tr2/generate_rcfile +++ b/tools/tr2/generate_rcfile @@ -2,8 +2,8 @@ import argparse from pathlib import Path -from libtrx.versioning import generate_version -from tr2x.paths import TR2X_DATA_DIR +from shared.paths import TR2Paths +from shared.versioning import generate_version def parse_args() -> argparse.Namespace: @@ -17,17 +17,19 @@ def write_rc_template( ) -> None: template = input_path.read_text() template = template.replace("{version}", version) - template = template.replace("{icon_path}", str(TR2X_DATA_DIR / "icon.ico")) + template = template.replace( + "{icon_path}", str(TR2Paths.data_dir / "icon.ico") + ) output_path.write_text(template) def main() -> None: args = parse_args() - version = generate_version() + version = generate_version(2) - for output_path in args.output: + for output_path in args.output or []: write_rc_template( - input_path=TR2X_DATA_DIR / output_path.name, + input_path=TR2Paths.data_dir / output_path.name, output_path=output_path, version=version, ) diff --git a/tools/tr2/libtrx b/tools/tr2/libtrx deleted file mode 120000 index f9343f13c..000000000 --- a/tools/tr2/libtrx +++ /dev/null @@ -1 +0,0 @@ -../subprojects/libtrx/tools/libtrx \ No newline at end of file diff --git a/tools/tr2/render_progress b/tools/tr2/render_progress index 384a72021..47bd4653c 100755 --- a/tools/tr2/render_progress +++ b/tools/tr2/render_progress @@ -8,8 +8,8 @@ from itertools import groupby from pathlib import Path from typing import Any -from tr2x.ida_progress import Symbol, SymbolStatus, parse_progress_file -from tr2x.paths import TR2X_PROGRESS_FILE +from shared.ida_progress import Symbol, SymbolStatus, parse_progress_file +from shared.paths import TR2Paths DOCUMENT_MARGIN = Decimal(2) GRID_MAX_SQUARES = 50 @@ -23,9 +23,6 @@ LEGEND_MARGIN = Decimal(15) TEXT_SIZE = Decimal(15) TEXT_MARGIN = Decimal(5) SECTION_MARGIN = GRID_SQUARE_SIZE + LEGEND_MARGIN -DOCS_DIR = Path(__file__).parent.parent / "docs" -PROGRESS_TXT_FILE = DOCS_DIR / "progress.txt" -PROGRESS_SVG_FILE = DOCS_DIR / "progress.svg" GRID_WIDTH = ( GRID_MAX_SQUARES * (GRID_SQUARE_SIZE + GRID_SQUARE_MARGIN) - GRID_SQUARE_MARGIN @@ -204,7 +201,7 @@ def squarify( def collect_functions() -> Iterable[Symbol]: in_functions = False - for line in PROGRESS_TXT_FILE.open(): + for line in TR2Paths.progress_file.open(): line = line.strip() if line == "# FUNCTIONS": in_functions = True @@ -698,9 +695,9 @@ class ProgressSVG(Container): def main() -> None: - progress_file = parse_progress_file(TR2X_PROGRESS_FILE) + progress_file = parse_progress_file(TR2Paths.progress_file) - with PROGRESS_SVG_FILE.open("w") as handle: + with TR2Paths.progress_svg.open("w") as handle: svg = ProgressSVG(progress_file.functions) print(svg.render(), file=handle) diff --git a/tools/tr2/shared b/tools/tr2/shared new file mode 120000 index 000000000..8fba6b66a --- /dev/null +++ b/tools/tr2/shared @@ -0,0 +1 @@ +../shared \ No newline at end of file diff --git a/tools/tr2/tr2x/paths.py b/tools/tr2/tr2x/paths.py deleted file mode 100644 index 759504b2c..000000000 --- a/tools/tr2/tr2x/paths.py +++ /dev/null @@ -1,13 +0,0 @@ -from pathlib import Path - -TR2X_TOOLS_DIR = Path(__file__).parent.parent -TR2X_REPO_DIR = TR2X_TOOLS_DIR.parent -TR2X_DOCS_DIR = TR2X_REPO_DIR / "docs" -TR2X_DATA_DIR = TR2X_REPO_DIR / "data" -TR2X_SRC_DIR = TR2X_REPO_DIR / "src" - -LIBTRX_REPO_DIR = TR2X_REPO_DIR / "subprojects/libtrx" -LIBTRX_SRC_DIR = LIBTRX_REPO_DIR / "src" -LIBTRX_INCLUDE_DIR = LIBTRX_REPO_DIR / "include/libtrx" - -TR2X_PROGRESS_FILE = TR2X_DOCS_DIR / "progress.txt" diff --git a/tools/tr2/update_gameflow b/tools/tr2/update_gameflow deleted file mode 100755 index dff295a8b..000000000 --- a/tools/tr2/update_gameflow +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -import json -import re -from pathlib import Path - -from tr2x.paths import LIBTRX_INCLUDE_DIR, TR2X_DATA_DIR, TR2X_SRC_DIR - -SHIP_DIR = TR2X_DATA_DIR / "ship" -GAME_STRING_DEF_PATHS = [ - TR2X_SRC_DIR / "game/game_string.def", - LIBTRX_INCLUDE_DIR / "game/game_string.def", -] -OBJECT_STRINGS_DEF_PATH = LIBTRX_INCLUDE_DIR / "game/objects/names_tr2.def" - - -def get_strings_map(paths: list[Path]) -> dict[str, str]: - result: dict[str, str] = {} - for path in paths: - for line in path.read_text().splitlines(): - if match := re.match( - r'^\w+DEFINE\((\w+),\s*"([^"]+)"\)$', line.strip() - ): - result[match.group(1)] = match.group(2) - return result - - -def postprocess_gameflow( - gameflow: str, - object_strings_map: dict[str, str], - game_strings_map: dict[str, str], -) -> str: - gameflow = re.sub( - r'^( "object_strings": {)[^}]*(})', - ' "object_strings": {\n' - + "\n".join( - f" {json.dumps(key)}: {json.dumps(value)}," - for key, value in object_strings_map.items() - ) - + "\n }", - gameflow, - flags=re.M | re.DOTALL, - ) - - gameflow = re.sub( - r'^( "game_strings": {)[^}]*(})', - ' "game_strings": {\n' - + "\n".join( - f" {json.dumps(key)}: {json.dumps(value)}," - for key, value in sorted( - game_strings_map.items(), key=lambda kv: kv[0] - ) - ) - + "\n }", - gameflow, - flags=re.M | re.DOTALL, - ) - return gameflow - - -def main() -> None: - object_strings_map = get_strings_map([OBJECT_STRINGS_DEF_PATH]) - game_strings_map = get_strings_map(GAME_STRING_DEF_PATHS) - assert object_strings_map - assert game_strings_map - - for gameflow_path in SHIP_DIR.rglob("*gameflow*.json*"): - old_gameflow = gameflow_path.read_text() - new_gameflow = postprocess_gameflow( - old_gameflow, object_strings_map, game_strings_map - ) - if new_gameflow != old_gameflow: - gameflow_path.write_text(new_gameflow) - - -if __name__ == "__main__": - main() diff --git a/tools/update_gameflow b/tools/update_gameflow new file mode 100755 index 000000000..9991adf9e --- /dev/null +++ b/tools/update_gameflow @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +import json +import re +from pathlib import Path + +from shared.paths import SHARED_INCLUDE_DIR, TR1Paths, TR2Paths + +GAME_STRING_DEF_PATHS = [ + TR1Paths.src_dir / "game/game_string.def", + SHARED_INCLUDE_DIR / "game/game_string.def", +] + + +def get_strings_map(paths: list[Path]) -> dict[str, str]: + result: dict[str, str] = {} + for path in paths: + for line in path.read_text().splitlines(): + if match := re.match( + r'^\w+DEFINE\((\w+),\s*"([^"]+)"\)$', line.strip() + ): + result[match.group(1)] = match.group(2) + return result + + +def process_tr1() -> None: + strings_map = get_strings_map(GAME_STRING_DEF_PATHS) + assert strings_map + + def postprocess_gameflow( + gameflow: str, strings_map: dict[str, str] + ) -> str: + gameflow = re.sub( + r'^( "strings": {)[^}]*(})', + ' "strings": {\n' + + "\n".join( + f" {json.dumps(key)}: {json.dumps(value)}," + for key, value in sorted( + strings_map.items(), key=lambda kv: kv[0] + ) + ) + + "\n }", + gameflow, + flags=re.M | re.DOTALL, + ) + return gameflow + + for gameflow_path in TR1Paths.shipped_data_dir.rglob("*gameflow*.json*"): + old_gameflow = gameflow_path.read_text() + new_gameflow = postprocess_gameflow(old_gameflow, strings_map) + if new_gameflow != old_gameflow: + gameflow_path.write_text(new_gameflow) + + +def process_tr2() -> None: + GAME_STRING_DEF_PATHS = [ + TR2Paths.src_dir / "game/game_string.def", + SHARED_INCLUDE_DIR / "game/game_string.def", + ] + OBJECT_STRINGS_DEF_PATH = SHARED_INCLUDE_DIR / "game/objects/names_tr2.def" + + def postprocess_gameflow( + gameflow: str, + object_strings_map: dict[str, str], + game_strings_map: dict[str, str], + ) -> str: + gameflow = re.sub( + r'^( "object_strings": {)[^}]*(})', + ' "object_strings": {\n' + + "\n".join( + f" {json.dumps(key)}: {json.dumps(value)}," + for key, value in object_strings_map.items() + ) + + "\n }", + gameflow, + flags=re.M | re.DOTALL, + ) + + gameflow = re.sub( + r'^( "game_strings": {)[^}]*(})', + ' "game_strings": {\n' + + "\n".join( + f" {json.dumps(key)}: {json.dumps(value)}," + for key, value in sorted( + game_strings_map.items(), key=lambda kv: kv[0] + ) + ) + + "\n }", + gameflow, + flags=re.M | re.DOTALL, + ) + return gameflow + + object_strings_map = get_strings_map([OBJECT_STRINGS_DEF_PATH]) + game_strings_map = get_strings_map(GAME_STRING_DEF_PATHS) + assert object_strings_map + assert game_strings_map + + for gameflow_path in TR2Paths.shipped_data_dir.rglob("*gameflow*.json*"): + old_gameflow = gameflow_path.read_text() + new_gameflow = postprocess_gameflow( + old_gameflow, object_strings_map, game_strings_map + ) + if new_gameflow != old_gameflow: + gameflow_path.write_text(new_gameflow) + + +def main() -> None: + process_tr1() + process_tr2() + + +if __name__ == "__main__": + main()