misc: merge tr2 and libtrx codebases

This commit is contained in:
Marcin Kurczewski 2024-10-02 10:23:23 +02:00
parent 605c1815ed
commit ff559580bf
96 changed files with 3026 additions and 1421 deletions

View file

@ -10,8 +10,12 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- platform: win - game_version: tr1
- platform: linux platform: win
- game_version: tr1
platform: linux
- game_version: tr2
platform: win
steps: steps:
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v1 uses: docker/login-action@v1
@ -29,5 +33,5 @@ jobs:
- name: Build Docker image (${{ matrix.platform }}) - name: Build Docker image (${{ matrix.platform }})
run: | run: |
just image-${{ matrix.platform }} just ${{ matrix.game_version }}-image-${{ matrix.platform }}
just push-image-${{ matrix.platform }} just ${{ matrix.game_version }}-push-image-${{ matrix.platform }}

View file

@ -1,4 +1,4 @@
name: Build the game and the installer name: Build TR1X and the installer
on: on:
workflow_call: workflow_call:
@ -12,11 +12,11 @@ jobs:
matrix: matrix:
include: include:
- platform: linux - platform: linux
just_target: package-linux just_target: tr1-package-linux
- platform: win - platform: win
just_target: package-win-all just_target: tr1-package-win-all
- platform: win-installer - platform: win-installer
just_target: package-win-installer just_target: tr1-package-win-installer
steps: steps:
- name: Install dependencies - name: Install dependencies
@ -30,7 +30,7 @@ jobs:
- id: vars - id: vars
name: Prepare variables 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 }}) - name: Package asset (${{ matrix.platform }})
run: just ${{ matrix.just_target }} run: just ${{ matrix.just_target }}
@ -38,7 +38,7 @@ jobs:
- name: Upload the artifact - name: Upload the artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ github.event.repository.name }}-${{ steps.vars.outputs.version }}-${{ matrix.platform }} name: TR1X-${{ steps.vars.outputs.version }}-${{ matrix.platform }}
path: | path: |
*.zip *.zip
*.exe *.exe

View file

@ -1,4 +1,4 @@
name: Build the game and the installer (macOS) name: Build TR1X and the installer (macOS)
on: on:
workflow_call: workflow_call:
@ -225,14 +225,14 @@ jobs:
- name: Build arm64 and create app bundle - name: Build arm64 and create app bundle
run: | run: |
BUILD_DIR=build-arm64 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 setup $BUILD_DIR $BUILD_OPTIONS
meson install -C $BUILD_DIR meson install -C $BUILD_DIR
- name: Build x86-64 - name: Build x86-64
run: | run: |
BUILD_DIR=build-x86-64 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 setup $BUILD_DIR $BUILD_OPTIONS
meson compile -C $BUILD_DIR 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 /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 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 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 - id: vars
name: Prepare variables 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 - name: Upload signed+notarized installer image
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4

40
.github/workflows/job_build_tr2.yml vendored Normal file
View file

@ -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

View file

@ -3,6 +3,10 @@ name: Create a new release
on: on:
workflow_call: workflow_call:
inputs: inputs:
changelog_game_version:
type: string
description: "Game version to build the changelog from"
required: true
draft: draft:
type: boolean type: boolean
description: "Draft" description: "Draft"
@ -13,11 +17,6 @@ on:
description: "Prerelease" description: "Prerelease"
required: true required: true
default: false default: false
release_name:
type: string
description: "Release name"
required: true
default: "Release ${{ github.ref_name }}"
tag_name: tag_name:
type: string type: string
description: "Tag name" description: "Tag name"
@ -39,19 +38,21 @@ jobs:
submodules: 'true' submodules: 'true'
fetch-depth: 0 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<<EOF" >> $GITHUB_OUTPUT
just output-current-changelog ${{ inputs.changelog_game_version }} >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: "Download built game assets" - name: "Download built game assets"
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
path: artifacts/ path: artifacts/
merge-multiple: true 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" - name: "Get information on the latest pre-release"
if: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }} if: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }}
id: last_release id: last_release
@ -77,9 +78,9 @@ jobs:
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ inputs.release_name }}
tag_name: ${{ inputs.tag_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' }} draft: ${{ inputs.draft == true || inputs.draft == 'true' }}
prerelease: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }} prerelease: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }}
fail_on_unmatched_files: true fail_on_unmatched_files: true

View file

@ -11,15 +11,20 @@ on:
- '!develop' - '!develop'
jobs: jobs:
package_multiplatform: package_tr1_multiplatform:
name: 'Create a test build' name: Build TR1
uses: ./.github/workflows/job_build_game.yml uses: ./.github/workflows/job_build_tr1.yml
secrets: inherit secrets: inherit
package_mac: package_tr1_mac:
name: 'Create a test build (macOS)' name: Build TR1
if: vars.MACOS_ENABLE == 'true' if: vars.MACOS_ENABLE == 'true'
uses: ./.github/workflows/job_build_game_macos.yml uses: ./.github/workflows/job_build_tr1_macos.yml
with: with:
let_mac_fail: true let_mac_fail: true
secrets: inherit secrets: inherit
package_tr2_multiplatform:
name: Build TR2
uses: ./.github/workflows/job_build_tr2.yml
secrets: inherit

View file

@ -9,31 +9,38 @@ on:
- develop - develop
jobs: jobs:
package_multiplatform: package_tr1_multiplatform:
name: Build prerelease assets name: Build TR1
if: vars.PRERELEASE_ENABLE == 'true' if: vars.PRERELEASE_ENABLE == 'true'
uses: ./.github/workflows/job_build_game.yml uses: ./.github/workflows/job_build_tr1.yml
secrets: inherit secrets: inherit
package_mac: package_tr1_mac:
name: "Build prerelease assets (mac)" name: Build TR1
if: | if: |
vars.PRERELEASE_ENABLE == 'true' && vars.PRERELEASE_ENABLE == 'true' &&
vars.MACOS_ENABLE == 'true' vars.MACOS_ENABLE == 'true'
uses: ./.github/workflows/job_build_game_macos.yml uses: ./.github/workflows/job_build_tr1_macos.yml
with: with:
let_mac_fail: true let_mac_fail: true
secrets: inherit secrets: inherit
package_tr2_multiplatform:
name: Build TR2
if: vars.PRERELEASE_ENABLE == 'true'
uses: ./.github/workflows/job_build_tr2.yml
secrets: inherit
publish_prerelease: publish_prerelease:
if: vars.PRERELEASE_ENABLE == 'true' if: vars.PRERELEASE_ENABLE == 'true'
name: Create a prerelease name: Create a prerelease
needs: needs:
- package_multiplatform - package_tr1_multiplatform
- package_mac - package_tr1_mac
- package_tr2_multiplatform
with: with:
release_name: 'Development snapshot'
draft: false draft: false
prerelease: true prerelease: true
tag_name: 'latest' tag_name: 'latest'
changelog_game_version: 'all'
uses: ./.github/workflows/job_release.yml uses: ./.github/workflows/job_release.yml

View file

@ -1,4 +1,4 @@
name: Publish a new release name: Publish a new TR1X release
permissions: permissions:
contents: write contents: write
@ -7,7 +7,7 @@ on:
push: push:
branch: stable branch: stable
tags: tags:
- "v?[0-9]*" - "tr1-*"
workflow_dispatch: workflow_dispatch:
inputs: inputs:
@ -21,11 +21,6 @@ on:
required: true required: true
type: boolean type: boolean
default: false default: false
release_name:
description: "Release name"
required: true
type: string
default: "Release name"
tag_name: tag_name:
description: "Tag name" description: "Tag name"
required: false required: false
@ -36,7 +31,7 @@ jobs:
package_multiplatform: package_multiplatform:
name: Build release assets name: Build release assets
if: vars.RELEASE_ENABLE == 'true' if: vars.RELEASE_ENABLE == 'true'
uses: ./.github/workflows/job_build_game.yml uses: ./.github/workflows/job_build_tr1.yml
secrets: inherit secrets: inherit
package_mac: package_mac:
@ -44,7 +39,7 @@ jobs:
if: | if: |
vars.RELEASE_ENABLE == 'true' && vars.RELEASE_ENABLE == 'true' &&
vars.MACOS_ENABLE == 'true' vars.MACOS_ENABLE == 'true'
uses: ./.github/workflows/job_build_game_macos.yml uses: ./.github/workflows/job_build_tr1_macos.yml
with: with:
let_mac_fail: ${{ inputs.let_mac_fail == true || inputs.let_mac_fail == 'true' }} let_mac_fail: ${{ inputs.let_mac_fail == true || inputs.let_mac_fail == 'true' }}
secrets: inherit secrets: inherit
@ -56,8 +51,8 @@ jobs:
- package_multiplatform - package_multiplatform
- package_mac - package_mac
with: with:
release_name: ${{ inputs.release_name }}
draft: ${{ inputs.draft || false }} draft: ${{ inputs.draft || false }}
prerelease: ${{ inputs.draft || false }} prerelease: ${{ inputs.draft || false }}
tag_name: ${{ inputs.tag_name || github.ref_name }} tag_name: ${{ inputs.tag_name || github.ref_name }}
changelog_game_version: 1
uses: ./.github/workflows/job_release.yml uses: ./.github/workflows/job_release.yml

47
.github/workflows/release_tr2.yml vendored Normal file
View file

@ -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

12
.gitignore vendored
View file

@ -31,8 +31,10 @@ Release/
.dotnet/ .dotnet/
# libtrx artefacts # libtrx artefacts
subprojects/packagecache/ **/subprojects/packagecache/
subprojects/dwarfstack-*/ **/subprojects/dwarfstack-*/
subprojects/dwarfstack.wrap **/subprojects/uthash-*/
subprojects/uthash-*/ src/tr1/subprojects/dwarfstack.wrap
subprojects/uthash.wrap src/tr1/subprojects/uthash.wrap
src/tr2/subprojects/dwarfstack.wrap
src/tr2/subprojects/uthash.wrap

View file

@ -8,12 +8,6 @@ repos:
language: system language: system
files: \.[ch](pp)?$ files: \.[ch](pp)?$
- id: update-gameflow
name: Update game flow files
entry: tools/update_gameflow
language: python
stages: [commit]
- id: additional-lint - id: additional-lint
name: Run additional linters name: Run additional linters
entry: tools/additional_lint entry: tools/additional_lint
@ -25,3 +19,9 @@ repos:
entry: tools/sort_imports entry: tools/sort_imports
language: system language: system
files: \.[ch](pp)?$ files: \.[ch](pp)?$
- id: update-gameflow
name: Update game flow files
entry: tools/update_gameflow
language: python
stages: [commit]

713
README.md
View file

@ -1,711 +1,128 @@
<p align="center"> <div align="center">
<img alt="TR1X logo" src="data/tr1/logo-light-theme.png#gh-light-mode-only" width="400"/> <h1>TRX Tomb Raider I & II: Community Edition</h1>
<img alt="TR1X logo" src="data/tr1/logo-dark-theme.png#gh-dark-mode-only" width="400"/>
</p>
This is an open source implementation of the classic Tomb Raider I game (1996), <a href="https://github.com/rr-/TRX/releases?q=tr1x+prerelease%3Afalse&expanded=true">
made by reverse engineering the TombATI / GLRage variant of the original game <img src="data/download_tr1x.svg"/>
and replacing proprietary audio/video libraries with open source variants. </a>
<a href="https://github.com/rr-/TRX/releases?q=tr2x+prerelease%3Afalse&expanded=true">
<img src="data/download_tr2x.svg"/>
</a>
</div>
See the [Tomb Raider Forums <hr/>
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
<table> <table>
<tr> <tr>
<th> <th>
Restored braid Restored braid
<img src="docs/showcase/braid.jpg"/> <img src="docs/tr1/showcase/braid.jpg"/>
</th> </th>
<th> <th>
Enemy health bar and UI scaling Enemy health bar and UI scaling
<img src="docs/showcase/enemy_health_bar_and_scaling.jpg"/> <img src="docs/tr1/showcase/enemy_health_bar_and_scaling.jpg"/>
</th> </th>
</tr> </tr>
<tr> <tr>
<th> <th>
3D pickups 3D pickups
<img src="docs/showcase/3d_pickups.jpg"/> <img src="docs/tr1/showcase/3d_pickups.jpg"/>
</th> </th>
<th>
Improved stats
<img src="docs/showcase/compass_stats.jpg"/>
</th>
</tr>
<tr>
<th> <th>
Skybox support Skybox support
<img src="docs/showcase/skybox.jpg"/> <img src="docs/tr1/showcase/skybox.jpg"/>
</th>
<th>
Customizable draw distance
<img src="docs/showcase/draw_distance.webp"/>
</th> </th>
</tr> </tr>
<tr> <tr>
<th> <th>
Fly cheat Customizable draw distance
<img src="docs/showcase/fly_cheat.jpg"/> <img src="docs/tr1/showcase/draw_distance.webp"/>
</th> </th>
<th> <th>
Developer console Developer console
<img src="docs/showcase/console.webp"/> <img src="docs/tr1/showcase/console.webp"/>
</th> </th>
</tr> </tr>
<tr> <tr>
<th> <th>
Free camera Free camera
<img src="docs/showcase/free_camera.jpg"/> <img src="docs/tr1/showcase/free_camera.jpg"/>
</th> </th>
<th> <th>
PS1 UI and new graphics options PS1 UI and new graphics options
<img src="docs/showcase/ps1_ui_and_gfx.jpg"/> <img src="docs/tr1/showcase/ps1_ui_and_gfx.jpg"/>
</th> </th>
</tr> </tr>
</table> </table>
## Windows / Linux ### Download
Download the latest release:
### Installing (simplified) <a href="https://github.com/rr-/TRX/releases?q=tr1x+prerelease%3Afalse&expanded=true">
<img src="data/download_tr1x.svg"/>
</a>
1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases See [the changelog](docs/tr1/CHANGELOG.md).
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.
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 ### Overview
2. Download the zip file. TR2X serves as a sequel to TR1X, currently focusing on the decompilation of Tomb Raider 2.
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.
Optionally you can also install the Unfinished Business expansion pack files. ### Decompilation Progress
- Either one of these these variants: ![Decompilation Progress](docs/tr2/progress.svg)
- 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 ### Download
less like this (click to expand): Download the latest release:
<details> <a href="https://github.com/rr-/TRX/releases?q=tr2x+prerelease%3Afalse&expanded=true">
<p><em>* Will not be present until the game has been launched.</em></p> <img src="data/download_tr2x.svg"/>
<pre> </a>
.
├── 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
</pre>
</details>
### Configuring See [the changelog](docs/tr2/CHANGELOG.md).
To configure TR1X, run the `TR1X_ConfigTool.exe` application. All the ### Install Instructions
configuration is explained in this tool. Alternatively, after running the game Please refer to the [detailed documentation](docs/tr2/).
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):
<details>
<p><em>* Will not be present until the game has been launched.</em></p>
<pre>
.
└── 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
</pre>
</details>
## 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 ## Q&A
1. **Is the game fully playable from beginning to the end?** 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?** 2. **Can we get HD textures? Reflections? Other visual updates?**
Eventually, probably yes, but we'd really appreciate help with these. We hope so! Being able to introduce skyboxes to TR1 showed that quite
literally sky is the limit. But great stuff takes time.
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.
4. **Can I play this on Mac, Linux, Android...?** 4. **Can I play this on Mac, Linux, Android...?**
Currently supported platforms include Windows, Linux and macOS. In the Currently supported platforms include Windows, Linux and macOS for TR1X,
future, it might be possible to run the game on Android as well and Windows for TR2X.
contributions are welcome!
5. **What's the relation to TR2Main?** 5. **What's the relation to TR2Main?**
Initially established as TR1Main in 2021, our project's development paths Originally founded as TR1Main in 2021, our project flourished independently
deviated, leading us to recognize the need for a distinct name. As a without sharing the code, with the shared brand concept existing only as an
result, we rebranded the project as Tomb1Main. However, to further idea. To better represent this, we rebranded to Tomb1Main. In 2023, we
differentiate ourselves, we underwent another rebranding in 2023, further refined our identity by adopting the name TR1X. Meanwhile, TR2Main
ultimately adopting the name TR1X. TR2Main is a separate project with its follows a completely separate and unique path, unconnected to our
own unique trajectory and not directly related to our development efforts. development work.
## 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.

53
data/download_tr1x.svg Normal file
View file

@ -0,0 +1,53 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="80" viewBox="0 0 700 260">
<rect width="700" height="260" fill="dodgerblue" rx="50" ry="50"/>
<g transform="translate(60 32) scale(0.7)" style="fill:none;stroke:white;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1">
<path transform="translate(238.843 238.343)" d="m0 0-15.683-27.165-31.495.074L-62.99.148l15.684 27.164 31.495-.074z"/>
<path transform="translate(0.62 143.116)" d="m0 0 15.811-27.238 31.495-.074L62.99-.148 47.178 27.091l-31.495.074Z"/>
<path transform="translate(227.585 11.457)" d="m0 0 15.683 27.165L-.128 54.403l-31.495.074-15.683-27.165L-31.495.074Z"/>
<path transform="translate(90.487 173.112)" d="M0 0c-18.457-31.969-6.844-73.227 25.938-92.154s74.319-8.354 92.776 23.614c16.17 28.007 9.261 63.144-14.742 84.149a70.5 70.5 0 0 1-11.195 8.005C59.995 42.541 18.457 31.969 0 0Z"/>
<path transform="translate(121.04 155.472)" d="M0 0c-8.956-15.513-3.321-35.534 12.586-44.718s36.064-4.054 45.021 11.459c7.846 13.59 4.493 30.641-7.154 40.833a34 34 0 0 1-5.433 3.885C29.113 20.643 8.956 15.513 0 0Z"/>
<path transform="translate(155.441 148.537)" d="m0 0 10.619 18.393"/>
<path transform="translate(167.18 110.679)" d="m0 0-11.352 18.443"/>
<path transform="translate(138.435 139.17)" d="m0 0-21.649.62"/>
<path transform="translate(16.438 115.878)" d="M0 0c7.407-37.351 30.63-71.733 66.706-92.562 35.871-20.71 76.995-23.723 112.9-11.704"/>
<path transform="translate(189.827 266.3)" d="M0 0c-59.824 19.518-126.482-3.692-158.449-59.062a130.3 130.3 0 0 1-14.4-36.959"/>
<path transform="translate(243.017 39.056)" d="M0 0a130.3 130.3 0 0 1 25.294 31.39C57.566 87.287 43.776 157.415-4.13 199.337"/>
<path transform="translate(226.142 189.922)" d="m0 0 25.677 17.19"/>
<path transform="translate(232.51 178.697)" d="m0 0 27.842 13.447"/>
<path transform="translate(237.254 166.706)" d="m0 0 29.466 9.443"/>
<path transform="translate(240.28 154.18)" d="m0 0 30.516 5.255"/>
<path transform="translate(241.533 141.365)" d="m0 0 30.973.965"/>
<path transform="translate(240.985 128.51)" d="m0 0 30.826-3.344"/>
<path transform="translate(238.648 115.863)" d="m0 0 30.08-7.588"/>
<path transform="translate(234.567 103.672)" d="m0 0 28.748-11.684"/>
<path transform="translate(106.63 118.008)" d="m0 0-14.554-7.029"/>
<path transform="translate(101.914 137.523)" d="m0 0-16.191-.504"/>
<path transform="translate(105.555 157.227)" d="m0 0-15.028 6.108"/>
<path transform="translate(117.474 174.222)" d="m0 0-11.039 11.886"/>
<path transform="translate(103.146 127.855)" d="m0 0-15.731-3.713"/>
<path transform="translate(102.765 147.929)" d="m0 0-15.916 3.012"/>
<path transform="translate(110.584 166.377)" d="m0 0-13.35 9.216"/>
<path transform="translate(125.9 180.39)" d="m0 0-8.204 13.994"/>
<path transform="translate(170.852 95.687)" d="m0 0 7.07-14.523"/>
<path transform="translate(158.037 91.59)" d="m0 0 2.788-15.933"/>
<path transform="translate(144.61 91.181)" d="m0 0-1.71-16.108"/>
<path transform="translate(131.608 94.494)" d="m0 0-6.075-15.036"/>
<path transform="translate(188.497 167.256)" d="m0 0 13.016 9.582"/>
<path transform="translate(194.844 155.404)" d="m0 0 15.177 5.629"/>
<path transform="translate(197.67 142.28)" d="m0 0 16.161 1.24"/>
<path transform="translate(196.755 128.9)" d="m0 0 15.894-3.245"/>
<path transform="translate(168.151 201.816)" d="m0 0-4.128-13.097a52 52 0 0 0 10.851-4.712C32.064-32.44 40.444-65.366 25.441-91.352a56 56 0 0 0-4.414-6.536l11.459-9.126"/>
<path transform="translate(71.284 193.486)" d="m0 0-23.488 16.337"/>
<path transform="translate(164.392 246.075)" d="M0 0c-43.231 6.616-87.586-12.632-110.29-51.956a106 106 0 0 1-5.84-11.746"/>
<path transform="translate(129.087 258.778)" d="m0 0 4.777-27.605"/>
<path transform="translate(83.466 64.807)" d="M0 0a104.7 104.7 0 0 1 16.709-11.992"/>
<path transform="translate(100.175 52.814)" d="M0 0a104.7 104.7 0 0 1 18.74-8.475"/>
<path transform="translate(113.99 29.288)" d="m0 0 4.926 15.051"/>
<path transform="translate(157.752 21.836)" d="m0 0-1.159 17.144a102.7 102.7 0 0 0-37.677 5.359"/>
<path transform="translate(72.894 53.015)" d="m0 0 10.572 11.792"/>
<path transform="translate(90.333 35.767)" d="m0 0 9.842 17.047"/>
<path transform="translate(44.56 87.188)" d="m0 0 15.426 7.568a102.7 102.7 0 0 1 23.481-29.949"/>
</g>
<text fill="white" dominant-baseline="middle" text-anchor="middle" x="475" y="75" style="font-family:sans-serif;font-size:70px">Download</text>
<text fill="white" dominant-baseline="middle" text-anchor="middle" x="475" y="175" style="font-family:sans-serif;font-size:120px">TR1X</text>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

13
data/download_tr2x.svg Normal file
View file

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="80" viewBox="0 0 700 260">
<rect width="700" height="260" fill="dodgerblue" rx="50" ry="50"/>
<g stroke="none" fill="white" transform="translate(40 5) scale(0.25)">
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024">
<path d="M554 242c17.61-19.23 46.09-38.73 58.75-61.25 12.66-22.53 50.09-54.77 26-83.5C614.66 68.51 596.24 132.21 582 144c-9.06-30.38-31.78-15.94-30.01 5.99 1.78 21.94-15.85 33.96-27.74 48.26-11.9 14.3-33.49 18.86-49.25 25.75 8.71-14.41 21.25-25.84 31.25-39.75 10-13.92 21.04-28.04 26.43-44.57S542.83 97.09 519 92s-18.65 33.26-30.08 40.92-26.49 48.13-6.67 34.33c19.81-13.8 14.11-20.07 26.44-43.56 12.34-23.49 14.3.54 5.23 20.23-9.06 19.69-16.9 26.96-28.67 42.33S463.98 209.54 454 225c-13.53 1.59-26.99 3.56-40 7 28.62-18.64 48.75-50.26 55.22-82.78 6.48-32.52-19.36-20.35-17.29-.29 2.07 20.05-12.36 32.1-21.18 46.82-8.81 14.73-24.66 21.61-37.67 31.33-13.01 9.73-26.36 19.83-40.38 28.62-14.03 8.78-30.09 13.94-40.4 28.6s-11.66 37.75-2.3 53.7c-22.49 16.24-48.74 10.38-73 6s-46.84 29.25-56.07.07c-9.24-29.17-8.12-52.66 3.82-76.32 11.93-23.67 28.28-32.44 51.02-41.98s45.31-18.29 72.21-19.79c26.89-1.5 35.22-47.9 10.02-17.98-28.16 7.45-63.81 12.06-89.92 24.08-26.12 12.01-48.1 27.13-59.3 53.7-11.21 26.57-16.6 68.04 1.97 92.47 18.56 24.43 14.19 52.74 40.25 72.75-4.9 6.17-12.91 19.36-3 24-3.09 19.45-4.89 91.92 24 63 11.78 18.02 13.44 30.43 27.69 48.31 14.26 17.87-4.54 44.36-5.35 60.01-.81 15.66 36.01-9.15 37.74-24.24 1.72-15.1 17.53-46.07 5.92-64.08 19.87 8.24 33.83 11.94 56.93 6.93 23.09-5.02-3.16-21.48-12.93-29.93 15.04 3.77 30.08-1.73 43-8 17.02-21.51 31.83 1.73 60.93-8.07 29.1-9.81 11.28-22.85-13.93-9.93-25.2 12.93-41.2-7.78-62-14 18.37-14.52 39.19-19.97 63-25s45.9 22.21 69 15c-14.59-12.72-24.18-25.73-43.78-31.22s-43.73 1.26-63 8.44c-19.27 7.19-29.99 17.37-51.2 23.8-21.21 6.44 14 26.67 28.98 24.98-15.5 18.05-43.77-.99-59.33-2.67C298.12 486.66 326.33 529 335 530c-22.94-4.24-34.84-1.21-52.75-23.25s-23.18 2.87-7.02 21.02S286.37 570.08 281 590c-1.21-23.03-15.39-46.71-27.75-65.25C240.9 506.2 237.32 460.11 226 504c-7.21-16.9-5.09-40.51 2-57 21.07 3.59 33.05 19.6 56 19s34.45-10.07 56.77-18.23 24.17-14.73 52.01-21.99c27.84-7.27 8.83-27.47-14.09-15.09-22.91 12.39-32.03 10.82-50.99 23.01-18.97 12.18-36.04 18.18-56.68 12.28-20.63-5.91-31.79-21.8-52.02-11.98 20.23-5.96 16.02-37.14 34.08-18.08 18.07 19.06 30.16-21.18 8-25-22.17-3.81-35.61-2.05-39.08 22.08-25.39-1.11-26.85-40.82-5.23-49.23 21.63-8.41 45.62.96 60.98 16.48 15.37 15.52 35.89 33.11 58.23 25.73 22.33-7.39 44.71-19.12 67.8-14.76 23.08 4.36 40.33 15.12 57.47 28.53 17.15 13.4 29.12 27.63 46.67 38.33C525.47 468.79 552.35 479.7 572 479s54.84-.69 64.25-21.75c9.4-21.07-33.87-.69-51.25 1.75s-52.6-4.84-68.25-17.75c-15.66-12.9-31.99-22.84-48.5-36.5s-35.48-23.62-57.58-28.42-50.28 2.39-70.99 11.35S295.32 380.22 284 363c39.05-3.15 46.22-17.86 37-48s34.68-38.47 52.31-52.69C390.93 248.08 435.2 245.17 458 242c-4.08 5.07-9.39 9.56-11 16 26.9-1.63 52.88-24.81 78.75-40.25 25.88-15.44 40.88-51.96 47.25-77.75 5.48 5.28-7.5 43.23 10.75 27.75 18.26-15.47 28.3-39.45 37.25-60.75 16.14 30.51-17.76 60.25-42.25 88.75-24.49 28.51-46.02 40.65-71.5 74.5C481.76 304.1 556.98 322.33 585 296c10.58 22.53 45.72 10.82 66.99 9.99 21.28-.83 43.1-7.83 59.01-22.99-12.76 28.4-35.14 36.81-61.93 50.07-26.78 13.27-68.93-3.36-92.07 4.93-3.65-11.82-16.18-27.6-30-21 28.09 30.18 13.13 74.86-17.3 96.7-30.43 21.83 22.03 4.84 31.28 14.32 9.24 9.49 44.2 11.12 60.02 12.98s47.03-.56 63.78-7.22c16.74-6.66 40.58-13.16 59.22-13.78 18.63-.63 42.84-3.91 56.75-16.25 13.92-12.34 28.12-18.67 34.27-38.73 6.16-20.06-21.23 1.11-38 5-16.76 3.9-48.63-1.04-67.02-2.02 24.89-23.32 59.6-39.83 71.08-73.92 11.47-34.1-.46-29-15.33-12.33S735.58 299.88 716 304c14.63-4.33 30.58-69.09 11.92-59.08-18.66 10-28.03 41.29-55.25 39.75S617.05 303.01 588 294c14.12-19.54 35.08-20.2 54.98-25.02 19.89-4.83 26.42-27.38 42.77-39.23C702.11 217.91 722.2 209.57 742 205c19.79-4.58 48.73 1.35 62.25 15.75 13.53 14.39 33.82 33.4 25.75 55.25s-11.05 48.12 8.7 24.7c19.74-23.43 3.15-44.98-2.78-65.62-5.92-20.64-25.58-29.08-42.89-39.1-17.32-10.03-49.17-9.19-68.35-3.3s-38.56 14.76-53.43 28.57C656.37 235.06 645.34 252.33 625 256c12.22-15.94 20.81-38.82 17-59-26.9 18.29-54.35 44.65-88 45m68-11c-13.38 43.35-62.82 68.05-107 59 21.24-37.25 77.33-28.33 107-59m137 74c-17.51 27.89-53.81 35.93-78 50 6.56 1.7 14.27.97 20 2-42.9 27.57-95 30.57-143 19 3.64-11.7 2.13-25.19 0-37 29.03 15.1 77.47 26.29 107.69 4.69C695.92 322.1 726.59 321.7 759 305m21 82c-36.17 23.22-80.28 12.53-118.68 30.32S572.88 421.28 536 409c15.34-34.47 38.29-17.58 68.07-10.07 29.79 7.5 59.64-12.51 86.01-19.85S751.83 400.63 780 387Z"/>
<path d="M392 287c-20.32-10.09-49.2 7.61-52.67 27.33S358.07 368.34 373 346c9.67 28.21 53.03 19.82 58-7 19.26 15.56 52.57-34.42 40.22-39.22-12.34-4.81-33.03 40.53-54.14 17.14-21.12-23.38-45.66-2.3-56.08 14.08-14.33-21.72 13.63-39.32 31-44m2 38c17.55 1.67 14.69 23.14-2 22-16.69-1.15-16.17-23.73 2-22Z"/>
<path d="M490 348c-12.99-1.62-23.01 7.92-28.22 18.78-5.22 10.86 16.25 30.36 16.22 8.22-.03-22.13 19.52-5.95 17.32 3.32s-26.86 27.6-1.55 19.45S517.62 351.45 490 348Z"/>
<path d="M674 441c-40.33 11.24-42.76 69.89-73.25 99.75S540.04 597.72 502 615c27.72-31.09 59.82-60.86 77.23-98.77S558.91 500.55 558 524c-13.08-6.32-21.74-21.77-37-24 3.55-6.21 7.89-11.71 13-17-8.15-3.56-15.71-9.78-25-10 .82 25.91-27.5 45.1-49.77 59.23s-51.06 24.71-74 39-42.75 30.47-60.48 52.52c-17.72 22.06-29.92 48.2-36.77 76.23-6.86 28.02-3.41 71.04 8.25 96.79s24.8 48.46 46.07 66.93c21.28 18.47 48.6 33.02 75.7 37.3 27.1 4.29 72.2 3.76 92.75-16.25 20.55-20 32.25-40.86 45.27-66.73 13.03-25.86 3.18-64.3-19.79-83.25s-58.57-22.78-83.98-8.52-23.27 74.33 13 52c36.27-22.34 41.09 30.39 4.97 35.97s-59.64-1.36-75.9-31.54c-16.26-30.19-1.87-61.91 14.43-85.93 16.3-24.01 38.04-38.35 62.56-50.44 24.51-12.1 52.5-22.87 75.44-37.56C569.7 594.07 588.44 576.36 609 559c-1.39 9.25-12.56 14.2-12 24 33.57-1.74 27.76-36.49 38.7-57.3 10.93-20.82 24.46-44.39 30.3-66.7 1.84 18.26 22.11 35.43 14 53 8.89-14.12-25.65-35.74-21.97-17.02 3.67 18.71 13.1 21.57 14.04 42.95.95 21.37 22.13 10.43 21.6-7.26s4.6-35.06-.89-51.45c-5.49-16.38-14.2-22.27-18.78-38.22m-160 67c24.5 10.25 43.09 26.66 23.25 47.25s-29.62 32.3-50.48 47.52-36.06 27.16-55.47 42.53c-19.4 15.38-30.8 34.17-44.22 53.78s-23.51 55.69-16.3 79.14c7.2 23.45 23.42 40.9 41.47 56.53 18.05 15.64 55.11 22.14 73.5 6 18.39-16.15 38.3-38.33 30.47-63.97-7.82-25.65-45.39-28.55-63.22-11.78-.94-56.2 91.16-27.42 89.99 16.01-1.16 43.43-26.47 87.36-69.67 100.31-43.19 12.95-96.76-7.24-126.32-39.32 10.92-1.33 21.38-4.38 32-7-2.12-26.28-36.97.92-47.92-12.08-10.94-13-16.39-29.78-23.08-44.92 8.41-2.09 51.28 14.81 50.97-1.02-.3-15.84-36.68-6.46-48.19-15.76-11.52-9.3-8.78-35.99-6.78-53.22 21.9-3.24 28.92 20.42 50.22 20.78 21.31.35 3.51-21.47-18.9-25.1-22.41-3.64-26.87-15.53-18.4-36.76 8.48-21.22 26.51-21.3 38.33-6.17 11.81 15.14 39.09 27.45 32.98 10.02s-27.52-20.03-39.48-32.52C336.8 625.75 359.27 611.44 371 602c10.99 12.33 52.93 61.18 49.23 34.77-3.71-26.41-64.67-41.3-14.23-55.77 14.17 11.5 30 42.33 48 40-2.7-21.51-25.55-32.32-39-47 27.66-18.95 39.59-11.81 60.25 13.75C495.92 613.3 485.64 555.21 458 555c5.15-7.51 16.07-10.94 23-17 17-4.6 29.56 34.8 42.08 28.08 12.51-6.73-18.91-36.56-30.08-36.08 5.57-7.94 12.64-16.51 21-22m-46 325c-6.5-.66-14.33 1.32-20-1 6.54-.17 13.93-1.72 20 1Z"/>
</svg>
</g>
<text fill="white" dominant-baseline="middle" text-anchor="middle" x="475" y="75" style="font-family:sans-serif;font-size:70px">Download</text>
<text fill="white" dominant-baseline="middle" text-anchor="middle" x="475" y="175" style="font-family:sans-serif;font-size:120px">TR2X</text>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -1,7 +1,7 @@
{ {
// NOTE: bad changes to this file may result in crashes. // NOTE: bad changes to this file may result in crashes.
// Lines starting with double slashes are comments and are ignored. // 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. // for usage.
"main_menu_picture": "data/images/title.webp", "main_menu_picture": "data/images/title.webp",

View file

@ -1,7 +1,7 @@
{ {
// NOTE: bad changes to this file may result in crashes. // NOTE: bad changes to this file may result in crashes.
// Lines starting with double slashes are comments and are ignored. // 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. // for usage.
"main_menu_picture": "data/images/title_ub.webp", "main_menu_picture": "data/images/title_ub.webp",

View file

@ -2,17 +2,9 @@
## Build workflow ## Build workflow
Initial build:
- Compile the project (described in the next section) - Compile the project (described in the next section)
- Copy all executable files from `build/` to your game directory - Copy all .dll and .exe files from `build/` to your game directory
- Copy the contents of `data/tr1/ship/` to your game directory - Copy the contents of `data/…/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)
@ -22,14 +14,16 @@ Subsequent builds:
- **With Docker**: - **With Docker**:
Make sure to install Docker and [just](https://github.com/casey/just), then Make sure to install Docker and [just](https://github.com/casey/just).
run `just`. The binaries should appear in the `build/` directory. To see the list of all possible build targets, run `just -l`. To build the
To see list of possible build targets, run `just -l`. 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**: - **Without Docker**:
This scenario is not officially supported, but you can see how it's done by 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 dependencies and `meson.build` for the local files, then tailoring your
system to match the process. 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 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 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 even refuse to compile.
will be always considered supplementary.
@ -100,8 +93,9 @@ guidelines:
- Variables are `lower_snake_case` - Variables are `lower_snake_case`
- Global variables are `g_PascalCase` - Global variables are `g_PascalCase`
- Module variables are `m_PascalCase` - Module-scoped global variables are `m_PascalCase` and static
- Function names are `Module_PascalCase` - Function names are `Module_PascalCase`
- Module-scoped function names are `M_PascalCase` and static
- Macros are `UPPER_SNAKE_CASE` - Macros are `UPPER_SNAKE_CASE`
- Struct names are `UPPER_SNAKE_CASE` - Struct names are `UPPER_SNAKE_CASE`
- Struct members are `lower_snake_case` - Struct members are `lower_snake_case`
@ -151,29 +145,27 @@ a review from the interested parties.
### Changelog ### 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 or refactor needs an entry there. Furthermore, new features and OG bugfixes
should be documented in README as well. If your change modifies gameflow should be documented in the `README.md` file as well.
behavior, make sure to update `GAMEFLOW.md` as appropriate.
### Commit scope ### Commit scope
Either you can make a lot of throwaway commits such as 'test' 'continue There are two options for handling commits. One approach involves making
testing' 'fix 1' 'fix 2' 'fix of the fix' and then squash your pull request as temporary commits with messages like 'test,' 'continue testing,' 'fix 1,' 'fix
a whole, or you can craft a nice history with proper commit messages and then 2,' and 'fix of the fix,' followed by squashing all of them when creating a
merge-rebase. The first case is mostly acceptable for isolated features, but in pull request. The other approach is to maintain a clean commit history with
general we prefer the latter approach. As a principle, refactors should made in meaningful messages and use merge-rebase. While the first approach can be
separate commits. Code review changes are best made incrementally and then suitable for isolated features, the latter is generally preferred.
squashed prior to merging, for easing the review process.
### Commit messages ### Commit messages
**The most important thing to remember:** bug fixes and feature implementations **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 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 submitted pull request contains player-facing changes, a ticket needs to be
to be created first no exceptions. created first.
Anything else is just for consistency and general neatness. Our commit messages Anything else is just for consistency and general neatness. Our commit messages
aim to respect the 50/72 rule and have the following form: 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 ### Tooling
We try to code all of our internal tools in a reasonably recent version of 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 ### Releasing a new version
@ -273,13 +265,16 @@ a specific version. See git history for details.
## Glossary ## Glossary
- Tomb1Main: the previous name of this project - Tomb1Main: the previous name of this project
- T1M: short hand of Tomb1Main - T1M: short hand of Tomb1Main
- OG: original game, most often TombATI - OG: original game, most often TombATI
- PS: PlayStation version of the game
- Vole: a rat that swims - Vole: a rat that swims
- Pod: a mutant egg (including the big egg) - Pod: a mutant egg (including the big egg)
- Cabin: the room with the pistols from Natla's Mines - Cabin: the room with the pistols from Natla's Mines
- Statue: centaur statues from the entrance of Tihocan's Tomb - Statue: centaur statues from the entrance of Tihocan's Tomb
- Bacon Lara: the doppelgänger Lara in the Atlantis level - Bacon Lara: the doppelgänger Lara in the Atlantis level
- Torso/Adam: the big boss mutant from The Great Pyramid 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

View file

@ -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 `/sfx` command
- added `/nextlevel` alias to `/endlevel` console command - added `/nextlevel` alias to `/endlevel` console command
- added `/quit` alias to `/exit` console command - added `/quit` alias to `/exit` console command

View file

@ -1412,6 +1412,14 @@ provided with the game achieves.
PS1 version but not the PC. PS1 version but not the PC.
</td> </td>
</tr> </tr>
<tr valign="top">
<td>
<code>explosion.bin</code>
</td>
<td>
Injects explosion sprites for certain console commands.
</td>
</tr>
<tr valign="top"> <tr valign="top">
<td> <td>
<code>lara_animations.bin</code> <code>lara_animations.bin</code>

604
docs/tr1/README.md Normal file
View file

@ -0,0 +1,604 @@
<p align="center">
<img alt="TR1X logo" src="/data/tr1/logo-light-theme.png#gh-light-mode-only" width="400"/>
<img alt="TR1X logo" src="/data/tr1/logo-dark-theme.png#gh-dark-mode-only" width="400"/>
</p>
## 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):
<details>
<p><em>* Will not be present until the game has been launched.</em></p>
<pre>
.
├── 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
</pre>
</details>
### 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):
<details>
<p><em>* Will not be present until the game has been launched.</em></p>
<pre>
.
└── 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
</pre>
</details>
## 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

View file

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 223 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Before After
Before After

View file

@ -1,55 +1,21 @@
<p align="center"> <p align="center">
<img alt="TR2X logo" src="data/logo-light-theme.png#gh-light-mode-only" width="400"/> <img alt="TR2X logo" src="/data/tr2/logo-light-theme.png#gh-light-mode-only" width="400"/>
<img alt="TR2X logo" src="data/logo-dark-theme.png#gh-dark-mode-only" width="400"/> <img alt="TR2X logo" src="/data/tr2/logo-dark-theme.png#gh-dark-mode-only" width="400"/>
</p> </p>
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 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 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 ## 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 - added an option to fix M16 accuracy while running
- fixed killing the T-Rex with a grenade launcher crashing the game - fixed killing the T-Rex with a grenade launcher crashing the game
- fixed secret rewards not displaying shotgun ammo - 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 #### 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 - fixed music not playing with certain game versions
#### Mods #### Mods
- added developer console (accessible with `/`, see [COMMANDS.md] for details) - added developer console (accessible with `/`, see [COMMANDS.md](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.

View file

@ -2,7 +2,7 @@ CWD := `pwd`
HOST_USER_UID := `id -u` HOST_USER_UID := `id -u`
HOST_USER_GID := `id -g` HOST_USER_GID := `id -g`
default: (build-win "debug") default: (tr1-build-win "debug")
_docker_push tag: _docker_push tag:
docker push {{tag}} docker push {{tag}}
@ -10,7 +10,7 @@ _docker_push tag:
_docker_build dockerfile tag force="0": _docker_build dockerfile tag force="0":
#!/usr/bin/env sh #!/usr/bin/env sh
if [ "{{force}}" = "0" ]; then if [ "{{force}}" = "0" ]; then
docker images --format '{''{.Repository}}' | grep '^{{tag}}$' docker images --format '{''{.Repository}}' | grep '^{{tag}}$' >/dev/null
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Docker image {{tag}} found" echo "Docker image {{tag}} found"
exit 0 exit 0
@ -41,34 +41,48 @@ _docker_run *args:
{{args}} {{args}}
image-linux force="1": (_docker_build "tools/tr1/docker/game-linux/Dockerfile" "rrdash/tr1x-linux" force) tr1-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) tr1-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) tr1-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-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") tr1-push-image-linux: (tr1-image-linux "0") (_docker_push "rrdash/tr1x-linux")
push-image-win: (image-win "0") (_docker_push "rrdash/tr1x") 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") tr1-build-linux target='debug': (tr1-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") tr1-build-win target='debug': (tr1-image-win "0") (_docker_run "-e" "TARGET="+target "rrdash/tr1x")
build-win-config: (image-win-config "0") (_docker_run "rrdash/tr1x-config") tr1-build-win-config: (tr1-image-win-config "0") (_docker_run "rrdash/tr1x-config")
build-win-installer: (image-win-installer "0") (_docker_run "rrdash/tr1x-installer") 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") tr1-package-linux: (tr1-build-linux "release") (_docker_run "rrdash/tr1x-linux" "package")
package-win: (build-win "release") (_docker_run "rrdash/tr1x" "package") tr1-package-win: (tr1-build-win "release") (_docker_run "rrdash/tr1x" "package")
package-win-all: (build-win "release") (build-win-config) (_docker_run "rrdash/tr1x" "package") tr1-package-win-all: (tr1-build-win "release") (tr1-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-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 #!/bin/sh
git checkout "tools/tr1/installer/Installer/Resources/release.zip" 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}" cp tools/tr1/installer/out/TR1X_Installer.exe "${exe_name}"
echo "Created ${exe_name}" echo "Created ${exe_name}"
output-current-version: tr2-image-win force="1": (_docker_build "tools/tr2/docker/game-win/Dockerfile" "rrdash/tr2x" force)
tools/get_version tr2-image-win-config force="1": (_docker_build "tools/tr2/docker/config/Dockerfile" "rrdash/tr2x-config" force)
output-current-changelog: tr2-push-image-win: (tr2-image-win "0") (_docker_push "rrdash/tr2x")
tools/output_current_changelog
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: clean:
-find build/ -type f -delete -find build/ -type f -delete

View file

@ -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

166
src/libtrx/meson.build Normal file
View file

@ -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)
]
)

20
src/libtrx/meson.options Normal file
View file

@ -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'
)

320
src/tr1/meson.build Normal file
View file

@ -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

203
src/tr2/meson.build Normal file
View file

@ -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,
)

1
src/tr2/subprojects/libtrx Symbolic link
View file

@ -0,0 +1 @@
../../libtrx

@ -1 +0,0 @@
Subproject commit ee2f6107f96c31607d2347e24f6813792c39707f

View file

@ -1,5 +1,73 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from libtrx.cli.additional_lint import run_script import argparse
from tr1x.paths import TR1X_REPO_DIR 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)

View file

@ -3,22 +3,27 @@ import argparse
from pathlib import Path from pathlib import Path
import pyjson5 import pyjson5
from tr1x.paths import TR1X_TOOLS_DIR from shared.paths import PROJECT_PATHS
CONFIG_TOOL_SPEC_PATH = (
TR1X_TOOLS_DIR / "config/TR1X_ConfigTool/Resources/specification.json"
)
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=( 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" "of the config tool to find changes in the default values"
) )
) )
parser.add_argument( 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() return parser.parse_args()
@ -26,7 +31,11 @@ def parse_args() -> argparse.Namespace:
def main() -> None: def main() -> None:
args = parse_args() args = parse_args()
game_config = pyjson5.loads(args.path.read_text()) 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 = { spec_map = {
option["Field"]: option["DefaultValue"] option["Field"]: option["DefaultValue"]

24
tools/generate_icon Executable file
View file

@ -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()

View file

@ -3,10 +3,11 @@ import argparse
from pathlib import Path from pathlib import Path
from subprocess import check_call from subprocess import check_call
from tr1x.paths import TR1X_DATA_DIR from shared.paths import TR1Paths
SOURCE_DIR = TR1X_DATA_DIR / "images" SOURCE_DIR = TR1Paths.data_dir / "images"
TARGET_DIR = TR1X_DATA_DIR / "ship/data/images" TARGET_DIR = TR1Paths.shipped_data_dir / "data/images"
print(SOURCE_DIR)
def format_size(size: int) -> str: def format_size(size: int) -> str:

View file

@ -1,4 +1,19 @@
#!/usr/bin/env python3 #!/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()

View file

@ -1 +0,0 @@
../subprojects/libtrx/tools/libtrx

View file

@ -1,5 +1,43 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from libtrx.changelog import get_current_version_changelog import argparse
from tr1x.paths import TR1X_REPO_DIR
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()

30
tools/output_release_name Executable file
View file

@ -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()

138
tools/release Executable file
View file

@ -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()

39
tools/shared/changelog.py Normal file
View file

@ -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

View file

@ -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)

53
tools/shared/files.py Normal file
View file

@ -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

77
tools/shared/git.py Normal file
View file

@ -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)

78
tools/shared/icons.py Normal file
View file

@ -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])

View file

@ -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("<libtrx"):
groups["shared"].add(include)
elif include.startswith("<"):
groups["external"].add(include)
elif include.startswith('"'):
groups["local"].add(include)
groups = {key: value for key, value in groups.items() if value}
ret = "\n\n".join(
"\n".join(
f"#include {include}"
for include in custom_sort(group, forced_order)
)
for group in groups.values()
).strip()
return ret
source = re.sub(
"^#include [^\n]+(\n*#include [^\n]+)*",
cb,
source,
flags=re.M,
)
if source != path.read_text():
path.write_text(source)
def sort_imports(
root_dir: Path,
system_include_dirs: list[Path],
paths: list[Path],
own_include_map: dict[str, str],
fix_map: dict[str, str],
forced_order: list[str],
) -> 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,
)

129
tools/shared/linting.py Normal file
View file

@ -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)

17
tools/shared/packaging.py Normal file
View file

@ -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)

34
tools/shared/paths.py Normal file
View file

@ -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}

View file

@ -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 "?"
)

81
tools/sort_imports Executable file
View file

@ -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=["<ddrawi.h>", "<d3dhal.h>"],
)
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=[
"<windows.h>",
"<dbghelp.h>",
"<tlhelp32.h>",
],
)

0
tools/tr1/__init__.py Normal file
View file

View file

@ -67,7 +67,7 @@
<EmbeddedResource Include="Resources\specification.json" /> <EmbeddedResource Include="Resources\specification.json" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<Reference Include="TRX_ConfigToolLib"> <Reference Include="TRX_ConfigToolLib">
<HintPath>..\..\..\..\subprojects\libtrx\tools\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll</HintPath> <HintPath>..\..\..\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -7,7 +7,7 @@ echo $HOME
shopt -s globstar shopt -s globstar
# Build the common lib DLL # Build the common lib DLL
cd /app/subprojects/libtrx/tools/config/ cd /app/tools/config/
rm -rf **/bin **/obj rm -rf **/bin **/obj
dotnet restore -p:EnableWindowsTargeting=true dotnet restore -p:EnableWindowsTargeting=true
dotnet publish -c Release -p:EnableWindowsTargeting=true dotnet publish -c Release -p:EnableWindowsTargeting=true

View file

@ -1,17 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path from pathlib import Path
from libtrx.cli.game_docker_entrypoint import run_script from shared.cli.game_docker_entrypoint import run_script
run_script( run_script(
ship_dir=Path("/app/data/tr1/ship/"), version=1,
build_root=Path("/app/build/linux/"), platform="linux",
compile_args=[], compile_args=[],
release_zip_filename="TR1X-{version}-Linux.zip",
release_zip_files=[ release_zip_files=[
(Path("/app/build/linux/TR1X"), "TR1X"), (Path("/app/build/tr1/linux/TR1X"), "TR1X"),
], ],
compressable_exes=[ compressable_exes=[
Path("/app/build/linux/TR1X"), Path("/app/build/tr1/linux/TR1X"),
], ],
) )

View file

@ -1,24 +1,23 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path from pathlib import Path
from libtrx.cli.game_docker_entrypoint import run_script from shared.cli.game_docker_entrypoint import run_script
run_script( run_script(
ship_dir=Path("/app/data/tr1/ship/"), version=1,
build_root=Path("/app/build/win/"), platform="win",
compile_args=[ compile_args=[
"--cross", "--cross",
"/app/tools/tr1/docker/game-win/meson_linux_mingw32.txt", "/app/tools/tr1/docker/game-win/meson_linux_mingw32.txt",
], ],
release_zip_filename="TR1X-{version}-Windows.zip",
release_zip_files=[ 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"), Path("/app/tools/tr1/config/out/TR1X_ConfigTool.exe"),
"TR1X_ConfigTool.exe", "TR1X_ConfigTool.exe",
), ),
], ],
compressable_exes=[ compressable_exes=[
Path("/app/build/win/TR1X.exe"), Path("/app/build/tr1/win/TR1X.exe"),
], ],
) )

View file

@ -2,7 +2,7 @@
import argparse import argparse
from pathlib import Path from pathlib import Path
from libtrx.versioning import generate_version from shared.versioning import generate_version
TEMPLATE = """ TEMPLATE = """
const char *g_TR1XVersion = "TR1X {version}"; const char *g_TR1XVersion = "TR1X {version}";
@ -16,7 +16,7 @@ def parse_args() -> argparse.Namespace:
def get_init_c() -> str: 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: def update_init_c(output_path: Path) -> None:
@ -30,7 +30,7 @@ def main() -> None:
if args.output: if args.output:
update_init_c(output_path=args.output) update_init_c(output_path=args.output)
else: else:
print(args.outptu) print(get_init_c(), end="")
if __name__ == "__main__": if __name__ == "__main__":

14
tools/generate_rcfile → tools/tr1/generate_rcfile Normal file → Executable file
View file

@ -2,8 +2,8 @@
import argparse import argparse
from pathlib import Path from pathlib import Path
from libtrx.versioning import generate_version from shared.paths import TR1Paths
from tr1x.paths import TR1X_DATA_DIR from shared.versioning import generate_version
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
@ -17,17 +17,19 @@ def write_rc_template(
) -> None: ) -> None:
template = input_path.read_text() template = input_path.read_text()
template = template.replace("{version}", version) 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) output_path.write_text(template)
def main() -> None: def main() -> None:
args = parse_args() 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( write_rc_template(
input_path=TR1X_DATA_DIR / output_path.name, input_path=TR1Paths.data_dir / output_path.name,
output_path=output_path, output_path=output_path,
version=version, version=version,
) )

29
tools/tr1/inspect_save Executable file
View file

@ -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()

View file

@ -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",
)

1
tools/tr1/shared Symbolic link
View file

@ -0,0 +1 @@
../shared

View file

@ -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=[],
)

View file

@ -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()

View file

@ -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"

0
tools/tr2/__init__.py Normal file
View file

View file

@ -59,7 +59,7 @@
<EmbeddedResource Include="Resources\specification.json" /> <EmbeddedResource Include="Resources\specification.json" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<Reference Include="TRX_ConfigToolLib"> <Reference Include="TRX_ConfigToolLib">
<HintPath>..\..\..\subprojects\libtrx\tools\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll</HintPath> <HintPath>..\..\..\config\TRX_ConfigToolLib\bin\Release\publish\TRX_ConfigToolLib.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -2,4 +2,4 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
ENV HOME /app ENV HOME /app
WORKDIR /app WORKDIR /app
ENTRYPOINT ["/app/tools/docker/config/entrypoint.sh"] ENTRYPOINT ["/app/tools/tr2/docker/config/entrypoint.sh"]

View file

@ -7,13 +7,13 @@ echo $HOME
shopt -s globstar shopt -s globstar
# Build the common lib DLL # Build the common lib DLL
cd /app/subprojects/libtrx/tools/config/ cd /app/tools/config/
rm -rf **/bin **/obj rm -rf **/bin **/obj
dotnet restore -p:EnableWindowsTargeting=true dotnet restore -p:EnableWindowsTargeting=true
dotnet publish -c Release -p:EnableWindowsTargeting=true dotnet publish -c Release -p:EnableWindowsTargeting=true
# Build the main executable # Build the main executable
cd /app/tools/config/ cd /app/tools/tr2/config/
rm -rf **/bin **/obj **/out/* rm -rf **/bin **/obj **/out/*
dotnet restore dotnet restore
dotnet publish -c Release -o out dotnet publish -c Release -o out

View file

@ -125,4 +125,4 @@ RUN apt-get install -y \
ninja ninja
ENV PYTHONPATH=/app/tools/ ENV PYTHONPATH=/app/tools/
ENTRYPOINT ["/app/tools/docker/game-win/entrypoint.sh"] ENTRYPOINT ["/app/tools/tr2/docker/game-win/entrypoint.sh"]

View file

@ -1,26 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path from pathlib import Path
from libtrx.cli.game_docker_entrypoint import run_script from shared.cli.game_docker_entrypoint import run_script
run_script( run_script(
ship_dir=Path("/app/data/ship/"), version=2,
build_root=Path("/app/build/win/"), platform="win",
compile_args=[ compile_args=[
"--cross", "--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=[ release_zip_files=[
(Path("/app/build/win/TR2X.exe"), "TR2X.exe"), (Path("/app/build/tr2/win/TR2X.exe"), "TR2X.exe"),
(Path("/app/build/win/TR2X.dll"), "TR2X.dll"), (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", "TR2X_ConfigTool.exe",
), ),
], ],
compressable_exes=[ compressable_exes=[
Path("/app/build/win/TR2X.exe"), Path("/app/build/tr2/win/TR2X.exe"),
Path("/app/build/win/TR2X.dll"), Path("/app/build/tr2/win/TR2X.dll"),
], ],
) )

View file

@ -1 +0,0 @@
../subprojects/libtrx/tools/ffmpeg_flags.txt

View file

@ -1,13 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path
import re import re
from pathlib import Path
from tr2x.ida_progress import FUNC_PTR_RE, VAR_RE, Symbol, parse_progress_file from shared.ida_progress import (
from tr2x.paths import TR2X_PROGRESS_FILE, TR2X_SRC_DIR FUNC_PTR_RE,
VAR_RE,
Symbol,
parse_progress_file,
)
from shared.paths import TR2Paths
FUNCS_H_FILE = TR2X_SRC_DIR / "global/funcs.h" FUNCS_H_FILE = TR2Paths.src_dir / "global/funcs.h"
VARS_H_FILE = TR2X_SRC_DIR / "global/vars_decomp.h" VARS_H_FILE = TR2Paths.src_dir / "global/vars_decomp.h"
TYPES_H_FILE = TR2X_SRC_DIR / "global/types.h" TYPES_H_FILE = TR2Paths.src_dir / "global/types.h"
COMMON_HEADER = [ COMMON_HEADER = [
@ -145,7 +150,13 @@ def make_types_h(types: list[str]) -> None:
"\n".join( "\n".join(
[ [
*header, *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, *footer,
] ]
) )
@ -154,7 +165,7 @@ def make_types_h(types: list[str]) -> None:
def main() -> 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_funcs_h(progress_file.functions)
make_vars_h(progress_file.variables) make_vars_h(progress_file.variables)

View file

@ -10,14 +10,15 @@ import tempfile
from pathlib import Path from pathlib import Path
import regex import regex
from tr2x.ida_progress import (
from shared.ida_progress import (
FUNC_PTR_RE, FUNC_PTR_RE,
FUNC_RE, FUNC_RE,
VAR_RE, VAR_RE,
Symbol, Symbol,
parse_progress_file, parse_progress_file,
) )
from tr2x.paths import TR2X_PROGRESS_FILE from shared.paths import TR2Paths
def generate_types(types: list[str], file) -> None: def generate_types(types: list[str], file) -> None:
@ -109,7 +110,7 @@ def parse_args() -> argparse.Namespace:
def main(): def main():
args = parse_args() args = parse_args()
progress_file = parse_progress_file(TR2X_PROGRESS_FILE) progress_file = parse_progress_file(TR2Paths.progress_file)
output = Path(args.output) output = Path(args.output)
with output.open("w") as file: with output.open("w") as file:

View file

@ -2,7 +2,7 @@
import argparse import argparse
from pathlib import Path from pathlib import Path
from libtrx.versioning import generate_version from shared.versioning import generate_version
TEMPLATE = """ TEMPLATE = """
const char *g_TR2XVersion = "TR2X {version}"; const char *g_TR2XVersion = "TR2X {version}";
@ -16,7 +16,7 @@ def parse_args() -> argparse.Namespace:
def get_init_c() -> str: 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: def update_init_c(output_path: Path) -> None:
@ -30,7 +30,7 @@ def main() -> None:
if args.output: if args.output:
update_init_c(output_path=args.output) update_init_c(output_path=args.output)
else: else:
print(args.output) print(get_init_c(), end="")
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -2,8 +2,8 @@
import argparse import argparse
from pathlib import Path from pathlib import Path
from libtrx.versioning import generate_version from shared.paths import TR2Paths
from tr2x.paths import TR2X_DATA_DIR from shared.versioning import generate_version
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
@ -17,17 +17,19 @@ def write_rc_template(
) -> None: ) -> None:
template = input_path.read_text() template = input_path.read_text()
template = template.replace("{version}", version) 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) output_path.write_text(template)
def main() -> None: def main() -> None:
args = parse_args() 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( write_rc_template(
input_path=TR2X_DATA_DIR / output_path.name, input_path=TR2Paths.data_dir / output_path.name,
output_path=output_path, output_path=output_path,
version=version, version=version,
) )

View file

@ -1 +0,0 @@
../subprojects/libtrx/tools/libtrx

View file

@ -8,8 +8,8 @@ from itertools import groupby
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from tr2x.ida_progress import Symbol, SymbolStatus, parse_progress_file from shared.ida_progress import Symbol, SymbolStatus, parse_progress_file
from tr2x.paths import TR2X_PROGRESS_FILE from shared.paths import TR2Paths
DOCUMENT_MARGIN = Decimal(2) DOCUMENT_MARGIN = Decimal(2)
GRID_MAX_SQUARES = 50 GRID_MAX_SQUARES = 50
@ -23,9 +23,6 @@ LEGEND_MARGIN = Decimal(15)
TEXT_SIZE = Decimal(15) TEXT_SIZE = Decimal(15)
TEXT_MARGIN = Decimal(5) TEXT_MARGIN = Decimal(5)
SECTION_MARGIN = GRID_SQUARE_SIZE + LEGEND_MARGIN 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_WIDTH = (
GRID_MAX_SQUARES * (GRID_SQUARE_SIZE + GRID_SQUARE_MARGIN) GRID_MAX_SQUARES * (GRID_SQUARE_SIZE + GRID_SQUARE_MARGIN)
- GRID_SQUARE_MARGIN - GRID_SQUARE_MARGIN
@ -204,7 +201,7 @@ def squarify(
def collect_functions() -> Iterable[Symbol]: def collect_functions() -> Iterable[Symbol]:
in_functions = False in_functions = False
for line in PROGRESS_TXT_FILE.open(): for line in TR2Paths.progress_file.open():
line = line.strip() line = line.strip()
if line == "# FUNCTIONS": if line == "# FUNCTIONS":
in_functions = True in_functions = True
@ -698,9 +695,9 @@ class ProgressSVG(Container):
def main() -> None: 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) svg = ProgressSVG(progress_file.functions)
print(svg.render(), file=handle) print(svg.render(), file=handle)

1
tools/tr2/shared Symbolic link
View file

@ -0,0 +1 @@
../shared

View file

@ -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"

View file

@ -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()

113
tools/update_gameflow Executable file
View file

@ -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()