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

View file

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

View file

@ -1,4 +1,4 @@
name: Build the game and the installer (macOS)
name: Build TR1X and the installer (macOS)
on:
workflow_call:
@ -225,14 +225,14 @@ jobs:
- name: Build arm64 and create app bundle
run: |
BUILD_DIR=build-arm64
BUILD_OPTIONS="--prefix=/tmp/TR1X.app --bindir=Contents/MacOS"
BUILD_OPTIONS="src/tr1 --prefix=/tmp/TR1X.app --bindir=Contents/MacOS"
meson setup $BUILD_DIR $BUILD_OPTIONS
meson install -C $BUILD_DIR
- name: Build x86-64
run: |
BUILD_DIR=build-x86-64
BUILD_OPTIONS="--prefix=/tmp/TR1X.app --bindir=Contents/MacOS --cross-file tools/tr1/mac/x86-64_cross_file.txt"
BUILD_OPTIONS="src/tr1 --prefix=/tmp/TR1X.app --bindir=Contents/MacOS --cross-file tools/tr1/mac/x86-64_cross_file.txt"
meson setup $BUILD_DIR $BUILD_OPTIONS
meson compile -C $BUILD_DIR
@ -267,11 +267,11 @@ jobs:
/usr/bin/codesign --force --options runtime -s "${IDENTITY}" --keychain $KEYCHAIN_PATH -v TR1X-Installer.dmg
xcrun notarytool submit --wait --apple-id "$MACOS_APPLEID" --password "$MACOS_APP_PWD" --team-id "$MACOS_TEAMID" TR1X-Installer.dmg
xcrun stapler staple -v TR1X-Installer.dmg
mv TR1X-Installer.dmg "TR1X-$(tools/get_version)-Installer.dmg"
mv TR1X-Installer.dmg "TR1X-$(tools/get_version 1)-Installer.dmg"
- id: vars
name: Prepare variables
run: echo "version=$(tools/get_version)" >> $GITHUB_OUTPUT
run: echo "version=$(tools/get_version 1)" >> $GITHUB_OUTPUT
- name: Upload signed+notarized installer image
uses: actions/upload-artifact@v4

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:
workflow_call:
inputs:
changelog_game_version:
type: string
description: "Game version to build the changelog from"
required: true
draft:
type: boolean
description: "Draft"
@ -13,11 +17,6 @@ on:
description: "Prerelease"
required: true
default: false
release_name:
type: string
description: "Release name"
required: true
default: "Release ${{ github.ref_name }}"
tag_name:
type: string
description: "Tag name"
@ -39,19 +38,21 @@ jobs:
submodules: 'true'
fetch-depth: 0
- name: "Prepare release data"
id: prepare_release_data
run: |
echo -n "release_name=" >> $GITHUB_OUTPUT
just output-release-name ${{ inputs.changelog_game_version }} >> $GITHUB_OUTPUT
echo "changelog<<EOF" >> $GITHUB_OUTPUT
just output-current-changelog ${{ inputs.changelog_game_version }} >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: "Download built game assets"
uses: actions/download-artifact@v4
with:
path: artifacts/
merge-multiple: true
- name: "Generate changelog"
run: |
hash=$(git log -1 --pretty=format:%H)
tag=$(just output-current-version)
echo -e "**Commit: $hash** \n**Tag: $tag**\n\n### Changes\n" > _changes.txt
just output-current-changelog >> _changes.txt
- name: "Get information on the latest pre-release"
if: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }}
id: last_release
@ -77,9 +78,9 @@ jobs:
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ inputs.release_name }}
tag_name: ${{ inputs.tag_name }}
body_path: _changes.txt
name: ${{ steps.prepare_release_data.outputs.release_name }}
body: ${{ steps.prepare_release_data.outputs.changelog }}
draft: ${{ inputs.draft == true || inputs.draft == 'true' }}
prerelease: ${{ inputs.prerelease == true || inputs.prerelease == 'true' }}
fail_on_unmatched_files: true

View file

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

View file

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

View file

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

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/
# libtrx artefacts
subprojects/packagecache/
subprojects/dwarfstack-*/
subprojects/dwarfstack.wrap
subprojects/uthash-*/
subprojects/uthash.wrap
**/subprojects/packagecache/
**/subprojects/dwarfstack-*/
**/subprojects/uthash-*/
src/tr1/subprojects/dwarfstack.wrap
src/tr1/subprojects/uthash.wrap
src/tr2/subprojects/dwarfstack.wrap
src/tr2/subprojects/uthash.wrap

View file

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

713
README.md
View file

@ -1,711 +1,128 @@
<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>
<div align="center">
<h1>TRX Tomb Raider I & II: Community Edition</h1>
This is an open source implementation of the classic Tomb Raider I game (1996),
made by reverse engineering the TombATI / GLRage variant of the original game
and replacing proprietary audio/video libraries with open source variants.
<a href="https://github.com/rr-/TRX/releases?q=tr1x+prerelease%3Afalse&expanded=true">
<img src="data/download_tr1x.svg"/>
</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
topic](https://www.tombraiderforums.com/showthread.php?p=8286101).
<hr/>
## 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>
<tr>
<th>
Restored braid
<img src="docs/showcase/braid.jpg"/>
<img src="docs/tr1/showcase/braid.jpg"/>
</th>
<th>
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>
</tr>
<tr>
<th>
3D pickups
<img src="docs/showcase/3d_pickups.jpg"/>
<img src="docs/tr1/showcase/3d_pickups.jpg"/>
</th>
<th>
Improved stats
<img src="docs/showcase/compass_stats.jpg"/>
</th>
</tr>
<tr>
<th>
Skybox support
<img src="docs/showcase/skybox.jpg"/>
</th>
<th>
Customizable draw distance
<img src="docs/showcase/draw_distance.webp"/>
<img src="docs/tr1/showcase/skybox.jpg"/>
</th>
</tr>
<tr>
<th>
Fly cheat
<img src="docs/showcase/fly_cheat.jpg"/>
Customizable draw distance
<img src="docs/tr1/showcase/draw_distance.webp"/>
</th>
<th>
Developer console
<img src="docs/showcase/console.webp"/>
<img src="docs/tr1/showcase/console.webp"/>
</th>
</tr>
<tr>
<th>
Free camera
<img src="docs/showcase/free_camera.jpg"/>
<img src="docs/tr1/showcase/free_camera.jpg"/>
</th>
<th>
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>
</tr>
</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
2. Download the installer. Your browser may complain that the .exe is unsafe, but it's OK to ignore this alert.
3. Mark the installer EXE as safe to run by right-clicking on the .exe, going to properties and clicking "Unblock".
4. Run the installer and proceed with the steps.
See [the changelog](docs/tr1/CHANGELOG.md).
We hope that eventually these alerts will go away as the popularity of the project rises.
### Install Instructions
Please refer to the [detailed documentation](docs/tr1/).
### Installing (advanced / manual)
## TR2X - Tomb Raider 2
1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases
2. Download the zip file.
3. Extract the zip file into a directory of your choice.
Make sure you choose to overwrite existing directories and files
(`cfg/TR1X_config.json5` can remain, but new features will not be configurable).
4. (First time installation) Put your original game files into the target directory.
1. For Steam and GOG users, extract the original `GAME.BIN` file using a tool such as UltraISO to your target directory.
Note that neither the GOG nor the Steam releases ship the music files. You have a few options here:
- You can download the music files from the link below.
https://lostartefacts.dev/aux/tr1x/music.zip
The legality of this approach is disputable.
- Rip the assets yourself from a physical PlayStation/SegaSaturn disk.
### Overview
TR2X serves as a sequel to TR1X, currently focusing on the decompilation of Tomb Raider 2.
Optionally you can also install the Unfinished Business expansion pack files.
- Either one of these these variants:
- https://lostartefacts.dev/aux/tr1x/trub-music.zip (fan-made patch to include music triggers)
- https://lostartefacts.dev/aux/tr1x/trub-vanilla.zip (original level files, which do not include music triggers)
- Or the more manual link: https://archive.org/details/tomb-raider-i-unfinished-business-pc-eng-full-version_20201225
2. For TombATI users this means copying the `data`, `fmv` and `music` directories.
5. To play the game, run `TR1X.exe`.
6. To play the Unfinished Expansion pack, run `TR1X.exe -gold`.
### Decompilation Progress
![Decompilation Progress](docs/tr2/progress.svg)
If you install everything correctly, your game directory should look more or
less like this (click to expand):
### Download
Download the latest release:
<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>
<a href="https://github.com/rr-/TRX/releases?q=tr2x+prerelease%3Afalse&expanded=true">
<img src="data/download_tr2x.svg"/>
</a>
### Configuring
See [the changelog](docs/tr2/CHANGELOG.md).
To configure TR1X, run the `TR1X_ConfigTool.exe` application. All the
configuration is explained in this tool. Alternatively, after running the game
at least once, you can edit `TR1X.json5` manually in a text editor such
as Notepad.
### Install Instructions
Please refer to the [detailed documentation](docs/tr2/).
## macOS
### Installing
1. Head over to GitHub releases: https://github.com/LostArtefacts/TR1X/releases
2. Download the `TR1X-Installer.dmg` installer image. Mount the image and drag TR1X to the Applications folder.
3. Run TR1X from the Applications folder. This will show you an error dialog about missing game data files. This is expected at this point, as you have not copied them in yet. However, it's important to run the app first to allow macOS to verify the app bundle's signature.
4. Find TR1X in your Applications folder. Right-click it and click "Show Package Contents".
5. Copy your Tomb Raider 1 game data files into `Contents/Resources`. (See the Windows / Linux instructions for retrieving game data from e.g. GOG.)
If you install everything correctly, your game directory should look more or
less like this (click to expand):
<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
1. **Is the game fully playable from beginning to the end?**
Yes. If you encounter a bug, please file a ticket.
By all means! If you encounter a bug, please file a ticket.
2. **Can we get HD textures? Reflections? Other visual updates?**
Eventually, probably yes, but we'd really appreciate help with these.
3. **Can we get braid in every level? Skyboxes? Flyby cameras? New animations? etc.**
The difficulty here is that these features often require inserting a
completely new animation, a textured mesh or a sound file and pretend
they're always been a part of the original game. Work is underway on an
injection framework, and the braid is now supported in each level.
We hope so! Being able to introduce skyboxes to TR1 showed that quite
literally sky is the limit. But great stuff takes time.
4. **Can I play this on Mac, Linux, Android...?**
Currently supported platforms include Windows, Linux and macOS. In the
future, it might be possible to run the game on Android as well
contributions are welcome!
Currently supported platforms include Windows, Linux and macOS for TR1X,
and Windows for TR2X.
5. **What's the relation to TR2Main?**
Initially established as TR1Main in 2021, our project's development paths
deviated, leading us to recognize the need for a distinct name. As a
result, we rebranded the project as Tomb1Main. However, to further
differentiate ourselves, we underwent another rebranding in 2023,
ultimately adopting the name TR1X. TR2Main is a separate project with its
own unique trajectory and not directly related to our development efforts.
## License
This project is licensed under the GNU General Public License - see the
[COPYING.md](COPYING.md) file for details.
## Copyright
(c) 2021 Marcin Kurczewski. All rights reserved. Original game is created by
Core Design Ltd. in 1996. Lara Croft and Tomb Raider are trademarks of Square
Enix Ltd. Title image by Kidd Bowyer. Loading screens and high quality images
by goblan_oldnewpixel and Posix.
Originally founded as TR1Main in 2021, our project flourished independently
without sharing the code, with the shared brand concept existing only as an
idea. To better represent this, we rebranded to Tomb1Main. In 2023, we
further refined our identity by adopting the name TR1X. Meanwhile, TR2Main
follows a completely separate and unique path, unconnected to our
development work.

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.
// Lines starting with double slashes are comments and are ignored.
// Refer to https://github.com/LostArtefacts/TR1X/blob/stable/GAMEFLOW.md
// Refer to https://github.com/LostArtefacts/TRX/blob/stable/docs/tr1/GAMEFLOW.md
// for usage.
"main_menu_picture": "data/images/title.webp",

View file

@ -1,7 +1,7 @@
{
// NOTE: bad changes to this file may result in crashes.
// Lines starting with double slashes are comments and are ignored.
// Refer to https://github.com/LostArtefacts/TR1X/blob/stable/GAMEFLOW.md
// Refer to https://github.com/LostArtefacts/TRX/blob/stable/docs/tr1/GAMEFLOW.md
// for usage.
"main_menu_picture": "data/images/title_ub.webp",

View file

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

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 `/nextlevel` alias to `/endlevel` 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.
</td>
</tr>
<tr valign="top">
<td>
<code>explosion.bin</code>
</td>
<td>
Injects explosion sprites for certain console commands.
</td>
</tr>
<tr valign="top">
<td>
<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">
<img alt="TR2X logo" src="data/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-light-theme.png#gh-light-mode-only" width="400"/>
<img alt="TR2X logo" src="/data/tr2/logo-dark-theme.png#gh-dark-mode-only" width="400"/>
</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
decompilation process. We recognize that there is much work to be done.
## Decompilation progress
## Windows
![](docs/progress.svg)
### Installing (manual)
1. Head over to GitHub releases: https://github.com/LostArtefacts/TRX/releases
2. Download the TR2X zip file.
3. Extract the TR2X zip file into a directory of your choice.
Make sure you choose to overwrite existing directories and files.
4. (First time installation) Put your original game files into the target directory.
5. To play the game, run `TR2X.exe`.
## Improvements over original game
@ -58,6 +24,10 @@ decompilation process. We recognize that there is much work to be done.
- added an option to fix M16 accuracy while running
- fixed killing the T-Rex with a grenade launcher crashing the game
- fixed secret rewards not displaying shotgun ammo
- fixed numeric keys interfering with the demos
- fixed explosions sometimes being drawn too dark
- fixed controls dialog remapping being too sensitive
- fixed the distorted skybox in room 5 of Barkhang Monastery
#### Visuals
@ -68,14 +38,4 @@ decompilation process. We recognize that there is much work to be done.
- fixed music not playing with certain game versions
#### Mods
- added developer console (accessible with `/`, see [COMMANDS.md] for details)
## Contributions
Please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) file for more
information on how to contribute to the project.
## License
TR2X follows the GPL license. Please refer to the [COPYING.md](COPYING.md) file
for further details.
- added developer console (accessible with `/`, see [COMMANDS.md](COMMANDS.md) for details)

View file

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

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
from libtrx.cli.additional_lint import run_script
from tr1x.paths import TR1X_REPO_DIR
import argparse
import sys
from collections.abc import Iterable
from fnmatch import fnmatch
from pathlib import Path
run_script(root_dir=TR1X_REPO_DIR, ignored_patterns=["*.patch", "*.bin"])
from shared.files import find_versioned_files, is_binary_file
from shared.linting import LintContext, lint_bulk_files, lint_file
from shared.paths import SRC_DIR
IGNORED_PATTERNS = ["*.patch", "*.bin", "gl_core_3_3.h"]
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("path", type=Path, nargs="*")
parser.add_argument("-D", "--debug", action="store_true")
return parser.parse_args()
def filter_files(
files: Iterable[Path], ignored_patterns: list[str] | None, debug: bool
) -> Iterable[Path]:
for path in files:
if is_binary_file(path):
if debug:
print(f"{path} is a binary file, ignoring", file=sys.stderr)
continue
if ignored_patterns and any(
fnmatch(path.name, pattern) for pattern in ignored_patterns
):
if debug:
print(
f"{path} has a prohibited extension, ignoring",
file=sys.stderr,
)
continue
yield path
def main(root_dir: Path, ignored_patterns: list[str] | None = None) -> None:
args = parse_args()
context = LintContext(
root_dir=SRC_DIR,
versioned_files=find_versioned_files(root_dir=SRC_DIR),
)
if args.path:
files = args.path
else:
files = context.versioned_files
files = list(
filter_files(
files, ignored_patterns=IGNORED_PATTERNS, debug=args.debug
)
)
exit_code = 0
for file in files:
if args.debug:
print(f"Checking {file}...", file=sys.stderr)
for lint_warning in lint_file(context, file):
print(str(lint_warning), file=sys.stderr)
exit_code = 1
if args.debug:
print(f"Checking files in bulk {file}...", file=sys.stderr)
for lint_warning in lint_bulk_files(context, files):
print(str(lint_warning), file=sys.stderr)
exit_code = 1
exit(exit_code)

View file

@ -3,22 +3,27 @@ import argparse
from pathlib import Path
import pyjson5
from tr1x.paths import TR1X_TOOLS_DIR
CONFIG_TOOL_SPEC_PATH = (
TR1X_TOOLS_DIR / "config/TR1X_ConfigTool/Resources/specification.json"
)
from shared.paths import PROJECT_PATHS
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Compares a given TR1X.json5 file with the default specifications "
"Compares a given configuration file with the default specifications "
"of the config tool to find changes in the default values"
)
)
parser.add_argument(
"path", type=Path, help="path to the freshly written TR1X.json5"
"-v",
"--version",
type=int,
help="game version to check the configuration file for",
required=True,
)
parser.add_argument(
"path",
type=Path,
help="path to the freshly written configuration file",
)
return parser.parse_args()
@ -26,7 +31,11 @@ def parse_args() -> argparse.Namespace:
def main() -> None:
args = parse_args()
game_config = pyjson5.loads(args.path.read_text())
tool_spec = pyjson5.loads(CONFIG_TOOL_SPEC_PATH.read_text())
tool_spec_path = (
PROJECT_PATHS[args.version].tools_dir
/ "config/TR1X_ConfigTool/Resources/specification.json"
)
tool_spec = pyjson5.loads(tool_spec_path.read_text())
spec_map = {
option["Field"]: option["DefaultValue"]

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 subprocess import check_call
from tr1x.paths import TR1X_DATA_DIR
from shared.paths import TR1Paths
SOURCE_DIR = TR1X_DATA_DIR / "images"
TARGET_DIR = TR1X_DATA_DIR / "ship/data/images"
SOURCE_DIR = TR1Paths.data_dir / "images"
TARGET_DIR = TR1Paths.shipped_data_dir / "data/images"
print(SOURCE_DIR)
def format_size(size: int) -> str:

View file

@ -1,4 +1,19 @@
#!/usr/bin/env python3
from libtrx.versioning import generate_version
import argparse
print(generate_version(), end="")
from shared.versioning import generate_version
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("version", choices=[1, 2], type=int)
return parser.parse_args()
def main() -> None:
args = parse_args()
print(generate_version(args.version), end="")
if __name__ == "__main__":
main()

View file

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

View file

@ -1,5 +1,43 @@
#!/usr/bin/env python3
from libtrx.changelog import get_current_version_changelog
from tr1x.paths import TR1X_REPO_DIR
import argparse
print(get_current_version_changelog(TR1X_REPO_DIR / "CHANGELOG.md"))
from shared.git import Git
from shared.versioning import generate_version
from shared.changelog import get_current_version_changelog
from shared.paths import PROJECT_PATHS
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("version", choices=['1', '2', 'all'])
return parser.parse_args()
def main() -> None:
args = parse_args()
commit_hash = Git().get_current_commit_hash()
if args.version == 'all':
print(f"**Commit: {commit_hash}** ")
print()
for version, project_paths in PROJECT_PATHS.items():
commit_tag = generate_version(version)
print(f'### TR{version}X changes')
print()
print(f'**Tag: {commit_tag}**')
print()
print(get_current_version_changelog(project_paths.changelog_path))
print()
else:
project_paths = PROJECT_PATHS.get(int(args.version))
commit_tag = generate_version(int(args.version))
print(f"**Commit: {commit_hash}** ")
print(f"**Tag: {commit_tag}**")
print()
print("### Changes")
print(get_current_version_changelog(project_paths.changelog_path))
if __name__ == "__main__":
main()

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" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<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>
</ItemGroup>
</Project>

View file

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

View file

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

View file

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

View file

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

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

@ -2,8 +2,8 @@
import argparse
from pathlib import Path
from libtrx.versioning import generate_version
from tr1x.paths import TR1X_DATA_DIR
from shared.paths import TR1Paths
from shared.versioning import generate_version
def parse_args() -> argparse.Namespace:
@ -17,17 +17,19 @@ def write_rc_template(
) -> None:
template = input_path.read_text()
template = template.replace("{version}", version)
template = template.replace("{icon_path}", str(TR1X_DATA_DIR / "icon.ico"))
template = template.replace(
"{icon_path}", str(TR1Paths.data_dir / "icon.ico")
)
output_path.write_text(template)
def main() -> None:
args = parse_args()
version = generate_version()
version = generate_version(1)
for output_path in args.output:
for output_path in args.output or []:
write_rc_template(
input_path=TR1X_DATA_DIR / output_path.name,
input_path=TR1Paths.data_dir / output_path.name,
output_path=output_path,
version=version,
)

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" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<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>
</ItemGroup>
</Project>

View file

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

View file

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

View file

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

View file

@ -1,26 +1,25 @@
#!/usr/bin/env python3
from pathlib import Path
from libtrx.cli.game_docker_entrypoint import run_script
from shared.cli.game_docker_entrypoint import run_script
run_script(
ship_dir=Path("/app/data/ship/"),
build_root=Path("/app/build/win/"),
version=2,
platform="win",
compile_args=[
"--cross",
"/app/tools/docker/game-win/meson_linux_mingw32.txt",
"/app/tools/tr2/docker/game-win/meson_linux_mingw32.txt",
],
release_zip_filename="TR2X-{version}-Windows.zip",
release_zip_files=[
(Path("/app/build/win/TR2X.exe"), "TR2X.exe"),
(Path("/app/build/win/TR2X.dll"), "TR2X.dll"),
(Path("/app/build/tr2/win/TR2X.exe"), "TR2X.exe"),
(Path("/app/build/tr2/win/TR2X.dll"), "TR2X.dll"),
(
Path("/app/tools/config/out/TR2X_ConfigTool.exe"),
Path("/app/tools/tr2/config/out/TR2X_ConfigTool.exe"),
"TR2X_ConfigTool.exe",
),
],
compressable_exes=[
Path("/app/build/win/TR2X.exe"),
Path("/app/build/win/TR2X.dll"),
Path("/app/build/tr2/win/TR2X.exe"),
Path("/app/build/tr2/win/TR2X.dll"),
],
)

View file

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

View file

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

View file

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

View file

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

View file

@ -2,8 +2,8 @@
import argparse
from pathlib import Path
from libtrx.versioning import generate_version
from tr2x.paths import TR2X_DATA_DIR
from shared.paths import TR2Paths
from shared.versioning import generate_version
def parse_args() -> argparse.Namespace:
@ -17,17 +17,19 @@ def write_rc_template(
) -> None:
template = input_path.read_text()
template = template.replace("{version}", version)
template = template.replace("{icon_path}", str(TR2X_DATA_DIR / "icon.ico"))
template = template.replace(
"{icon_path}", str(TR2Paths.data_dir / "icon.ico")
)
output_path.write_text(template)
def main() -> None:
args = parse_args()
version = generate_version()
version = generate_version(2)
for output_path in args.output:
for output_path in args.output or []:
write_rc_template(
input_path=TR2X_DATA_DIR / output_path.name,
input_path=TR2Paths.data_dir / output_path.name,
output_path=output_path,
version=version,
)

View file

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

View file

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

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