Compare commits

..
Author SHA1 Message Date
water 2e6bbcbce8 splitscreen hack 2023-11-28 20:19:08 -05:00
10047 changed files with 1135714 additions and 4557304 deletions

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ IncludeCategories:
Priority: 2
- Regex: '^(<|")common.*'
Priority: 3
- Regex: '^"(third-party|fmt|curl).*'
- Regex: '^"third-party.*'
Priority: 4
SortPriority: 5
- Regex: '.*'
-8
View File
@@ -1,8 +0,0 @@
.cache/
.git/
build/
decompiler_out/
failures/
iso_data/
out/
log/
+1 -2
View File
@@ -1,7 +1,6 @@
name: "\U0001F41B Jak 1 Bug Report"
description: Create a bug report for Jak 1.
labels: ["jak1"]
type: 'bug'
labels: ["bug", "jak1"]
body:
- type: checkboxes
attributes:
+1 -2
View File
@@ -1,7 +1,6 @@
name: "\U0001F41B Jak 2 Bug Report"
description: Create a bug report for Jak 2.
labels: ["jak2"]
type: 'bug'
labels: ["bug", "jak2"]
body:
- type: checkboxes
attributes:
@@ -1,75 +0,0 @@
name: "\U0001F41B Jak 3 Bug Report"
description: Create a bug report for Jak 3.
labels: ["jak3"]
type: 'bug'
body:
- type: checkboxes
attributes:
label: Acknowledgements
description: Take a couple minutes to help our maintainers work faster.
options:
- label: I have [searched](https://github.com/open-goal/jak-project/issues?q=is%3Aissue+is%3Aopen+label%3Abug+label%3Ajak3+sort%3Aupdated-desc+) for duplicate or closed bug reports
required: true
- label: I understand that I am supposed to provide my own legitimately obtained copy of the game
required: true
- type: textarea
attributes:
label: Describe the Bug
description: A clear and concise description of what the bug is. You may post screenshots or videos of the bug here.
validations:
required: true
- type: textarea
attributes:
label: How To Reproduce
description: Steps to reproduce the behavior. You can also post a video of it here.
validations:
required: true
- type: dropdown
attributes:
label: Does this problem occur on original hardware or PCSX2?
description: Some things that may seem like bugs are actually exactly how the original game behaved.
options:
- Yes, it's unique to OpenGOAL
- Didn't check
- Not needed, bug is obvious
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A clear and concise description of the expected behavior.
placeholder: When I do X, Y should happen.
validations:
required: true
- type: textarea
attributes:
label: Environment Information
description: "You can upload the [Support Package](https://github.com/open-goal/launcher#asking-for-help) provided by the Launcher here, or you can provide the following information: CPU, GPU, OS Version, OpenGOAL Version (found in the window's title bar)"
validations:
required: true
- type: dropdown
attributes:
label: Game Version
options:
- NTSC 1.0 (black label)
- NTSC Greatest Hits version (red label)
- PAL
- KO
validations:
required: true
- type: dropdown
attributes:
label: Have you set the game to something other than `60fps`?
options:
- "No"
- "Yes"
validations:
required: true
@@ -1,10 +1,4 @@
{
"3990": {
"msg": "Provided invalid or missing arguments"
},
"3991": {
"msg": "Input path(s) do not exist"
},
"4000": {
"msg": "Validation Failed: Cannot locate ELF"
},
@@ -32,4 +32,4 @@ cp -r $SOURCE/decompiler/config $DEST/data/decompiler/
cp -r $SOURCE/goal_src $DEST/data
cp -r $SOURCE/game/assets $DEST/data/game/
cp -r $SOURCE/game/graphics/opengl_renderer/shaders $DEST/data/game/graphics/opengl_renderer
cp -r $SOURCE/custom_assets $DEST/data
cp -r $SOURCE/custom_levels $DEST/data
@@ -24,4 +24,4 @@ cp -r $SOURCE/decompiler/config $DEST/data/decompiler/
cp -r $SOURCE/goal_src $DEST/data
cp -r $SOURCE/game/assets $DEST/data/game/
cp -r $SOURCE/game/graphics/opengl_renderer/shaders $DEST/data/game/graphics/opengl_renderer
cp -r $SOURCE/custom_assets $DEST/data
cp -r $SOURCE/custom_levels $DEST/data
+9 -9
View File
@@ -49,14 +49,14 @@ jobs:
name: "🍎 MacOS"
uses: ./.github/workflows/macos-build.yaml
with:
cmakePreset: "Release-macos-x86_64-clang"
cmakePreset: "Release-macos-clang"
cachePrefix: ""
secrets: inherit
build_macos_arm:
name: "🍎 MacOS"
uses: ./.github/workflows/macos-build-arm.yaml
with:
cmakePreset: "Release-macos-arm64-clang"
cachePrefix: ""
secrets: inherit
# Q4 2023 there will hopefully be native arm64 runners
# https://github.com/github/roadmap/issues/528
# build_macos_arm:
# name: "🍎 MacOS"
# uses: ./.github/workflows/macos-build-arm.yaml
# with:
# cmakePreset: "Release-macos-clang"
# cachePrefix: ""
@@ -1,96 +0,0 @@
name: Compilation Check
on:
pull_request:
branches:
- master
jobs:
build:
name: Compare
runs-on: ubuntu-22.04
timeout-minutes: 60
steps:
- name: Checkout Repository
uses: actions/checkout@v6
with:
ref: master
- name: Install Package Dependencies
run: |
sudo apt update
sudo apt install build-essential cmake \
clang gcc g++ lcov make nasm libxrandr-dev \
libxinerama-dev libxcursor-dev libpulse-dev \
libxi-dev zip ninja-build libgl1-mesa-dev libssl-dev
- name: Setup sccache
uses: hendrikmuhs/[email protected]
with:
variant: sccache
key: linux-ubuntu-22.04--Release-linux-clang-static-${{ github.sha }}
restore-keys: linux-ubuntu-22.04--Release-linux-clang-static
max-size: 1000M
- name: CMake Generation (master)
env:
CC: clang
CXX: clang++
run: |
cmake -B build --preset=Release-linux-clang-static \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build goalc (master)
run: |
cmake --build build --parallel $((`nproc`)) --target goalc
mv ./build ./build.master
- name: Checkout PR
uses: actions/checkout@v6
with:
clean: "false"
- name: CMake Generation (PR)
env:
CC: clang
CXX: clang++
run: |
cmake -B build --preset=Release-linux-clang-static \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build goalc (PR)
run: cmake --build build --parallel $((`nproc`)) --target goalc
- name: Compile and preserve (master)
run: |
./build.master/goalc/goalc --game jak1 --cmd "(make-group \"all-code\")"
./build.master/goalc/goalc --game jak2 --cmd "(make-group \"all-code\")"
./build.master/goalc/goalc --game jak3 --cmd "(make-group \"all-code\")"
mv ./out/jak1/obj ./out/jak1/obj.master
mv ./out/jak2/obj ./out/jak2/obj.master
mv ./out/jak3/obj ./out/jak3/obj.master
- name: Compile and preserve (PR)
run: |
./build/goalc/goalc --game jak1 --cmd "(make-group \"all-code\")"
./build/goalc/goalc --game jak2 --cmd "(make-group \"all-code\")"
./build/goalc/goalc --game jak3 --cmd "(make-group \"all-code\")"
mv ./out/jak1/obj ./out/jak1/obj.pr
mv ./out/jak2/obj ./out/jak2/obj.pr
mv ./out/jak3/obj ./out/jak3/obj.pr
- name: Compare Results and Produce Report
run: |
ls -l ./out/jak1
ls -l ./out/jak2
ls -l ./out/jak3
set +e
python ./scripts/gsrc/compare-compilation-outputs.py --base "./out/jak1/obj.master,./out/jak2/obj.master,./out/jak3/obj.master" --compare "./out/jak1/obj.pr,./out/jak2/obj.pr,./out/jak3/obj.pr" --markdown
SCRIPT_EXIT_CODE=$?
cat ./comp-diff-report.md >> $GITHUB_STEP_SUMMARY
if [ "$SCRIPT_EXIT_CODE" -ne 0 ]; then
exit 1
fi
+35
View File
@@ -0,0 +1,35 @@
name: 🏭 Draft Release
on:
workflow_dispatch:
inputs:
bump:
description: 'Semver Bump Type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
jobs:
cut-release:
runs-on: ubuntu-latest
steps:
# Docs - https://github.com/mathieudutour/github-tag-action
# Workflows cannot trigger other workflows implicitly
# - https://github.community/t/github-actions-workflow-not-triggering-with-tag-push/17053/7
- name: Bump Version and Push Tag
if: github.repository == 'open-goal/jak-project'
id: tag_version
uses: mathieudutour/[email protected]
with:
github_token: ${{ secrets.BOT_PAT }}
tag_prefix: v
default_bump: ${{ github.event.inputs.bump }}
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.BOT_PAT }}
run: gh release create ${{ steps.tag_version.outputs.new_tag }} --generate-notes --draft --repo open-goal/jak-project
+6 -10
View File
@@ -12,7 +12,7 @@ jobs:
gen-docs:
name: Generate Documentation
if: github.repository == 'open-goal/jak-project'
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
timeout-minutes: 45
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
@@ -22,7 +22,7 @@ jobs:
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install Package Dependencies
run: |
@@ -35,7 +35,7 @@ jobs:
- name: Setup Buildcache
uses: mikehardy/[email protected]
with:
cache_key: linux-ubuntu-22.04--Release-linux-clang
cache_key: linux-ubuntu-20.04--Release-linux-clang
buildcache_tag: v0.28.1
- name: CMake Generation
@@ -58,16 +58,12 @@ jobs:
run: |
./build/goalc/goalc --cmd "(begin (make-group \"all-code\") (gen-docs \"${PWD}\"))" --game jak2
- name: Generate Docs For Jak 3
run: |
./build/goalc/goalc --cmd "(begin (make-group \"all-code\") (gen-docs \"${PWD}\"))" --game jak3
- name: Append File Docstrings
run: |
python ./scripts/ci/docs/append-file-docs.py
python ./scripts/docs/append-file-docs.py
- name: Upload Docs Artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v3
with:
name: opengoal-docs
if-no-files-found: error
@@ -79,7 +75,7 @@ jobs:
run: echo ${{ github.run_id }}
- name: Send Dispatch
uses: peter-evans/repository-dispatch@v4
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.BOT_PAT }}
repository: 'open-goal/open-goal.github.io'
+13 -16
View File
@@ -12,24 +12,26 @@ on:
jobs:
formatting:
name: 📝 Formatting
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
timeout-minutes: 10
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v4
# wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
# sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main'
# sudo apt install clang-format-15
# --clang-format-executable $(which clang-format-15)
- name: Get Package Dependencies
run: |
sudo apt update
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main'
sudo apt install clang-format-18
clang-format-18 -version
sudo apt install clang-format
clang-format -version
- name: Check Clang-Formatting
run: |
chmod +x ./third-party/run-clang-format/run-clang-format.py
./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test tools lsp --color always --clang-format-executable $(which clang-format-18)
./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test tools lsp --color always
required-checks:
name: 📝 Required Checks
@@ -37,10 +39,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Check for Trailing Whitespace in goal_src
run: python ./scripts/ci/lint-trailing-whitespace.py
uses: actions/checkout@v4
- name: Check for Unresolved Conflicts
run: python ./scripts/gsrc/check-for-conflicts.py
@@ -49,9 +48,7 @@ jobs:
run: python ./scripts/ci/check-for-asserts.py
- name: Check for Invalid Translation Characters
run: |
python ./scripts/ci/lint-characters.py
python ./scripts/ci/lint-autoglottonyms.py
run: python ./scripts/ci/lint-characters.py
optional-checks:
name: 📝 Optional Checks
@@ -59,7 +56,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install Python Dependencies
run: pip install unidiff colorama
@@ -67,6 +64,6 @@ jobs:
- name: Check for Removed goal_src/ Code
# FYI - if you run this `git diff` on windows, it creates a utf-16 LE file
run: |
git fetch origin master
git fetch origin
git diff origin/master > lint-changes.diff
python ./scripts/ci/lint-gsrc-removals.py
+17 -22
View File
@@ -17,22 +17,19 @@ on:
jobs:
build:
name: Clang
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
timeout-minutes: 60
steps:
# minimal checkout if we're NOT uploading artifacts
- name: Checkout Repository
if: ${{ ! inputs.uploadArtifacts }}
uses: actions/checkout@v6
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
BUILDCACHE_COMPRESS_FORMAT: ZSTD
BUILDCACHE_COMPRESS_LEVEL: 19
BUILDCACHE_DIRECT_MODE: true
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
# full checkout with tags if we ARE uploading artifacts
- name: Checkout Repository with Tags
if: ${{ inputs.uploadArtifacts }}
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Package Dependencies
run: |
@@ -42,13 +39,11 @@ jobs:
libxinerama-dev libxcursor-dev libpulse-dev \
libxi-dev zip ninja-build libgl1-mesa-dev libssl-dev
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.22
- name: Setup Buildcache
uses: mikehardy/buildcache-action@v2.1.0
with:
variant: sccache
key: linux-ubuntu-22.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}-${{ github.sha }}
restore-keys: linux-ubuntu-22.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
max-size: 1000M
cache_key: linux-ubuntu-20.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
buildcache_tag: v0.28.1
- name: CMake Generation
env:
@@ -56,8 +51,8 @@ jobs:
CXX: clang++
run: |
cmake -B build --preset=${{ inputs.cmakePreset }} \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
-DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
- name: Build Project
run: cmake --build build --parallel $((`nproc`))
@@ -76,7 +71,7 @@ jobs:
strip ./build/lsp/lsp
- name: Upload artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v3
if: ${{ inputs.uploadArtifacts }}
with:
name: opengoal-linux-${{ inputs.cachePrefix }}
+24 -17
View File
@@ -13,33 +13,33 @@ on:
jobs:
build:
name: GCC
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
timeout-minutes: 60
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
BUILDCACHE_COMPRESS_FORMAT: ZSTD
BUILDCACHE_COMPRESS_LEVEL: 19
BUILDCACHE_DIRECT_MODE: true
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install Package Dependencies
run: |
sudo apt update
sudo apt install build-essential cmake \
clang gcc-10 g++-10 lcov make nasm libxrandr-dev \
clang gcc g++ lcov make nasm libxrandr-dev \
libxinerama-dev libxcursor-dev libpulse-dev \
libxi-dev zip ninja-build libgl1-mesa-dev libssl-dev
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100
sudo update-alternatives --set gcc /usr/bin/gcc-10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100
sudo update-alternatives --set g++ /usr/bin/g++-10
- name: Setup sccache
uses: hendrikmuhs/[email protected]
- name: Setup Buildcache
uses: mikehardy/[email protected]
with:
variant: sccache
key: linux-ubuntu-22.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}-${{ github.sha }}
restore-keys: linux-ubuntu-22.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
max-size: 1000M
cache_key: linux-ubuntu-20.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
buildcache_tag: v0.28.1
- name: CMake Generation
env:
@@ -48,11 +48,18 @@ jobs:
run: |
cmake -B build --preset=${{ inputs.cmakePreset }} \
-DCODE_COVERAGE=OFF \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
-DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
- name: Build Project
run: cmake --build build --parallel $((`nproc`))
run: cmake --build build --parallel $((`nproc`)) -- -w dupbuild=warn
- name: Run Tests
run: ./test.sh
# - name: Submit Coverage Report to Codacy
# uses: codacy/codacy-coverage-reporter-action@v1
# continue-on-error: true
# with:
# project-token: ${{ secrets.CODACY_PROJECT_KEY }}
# coverage-reports: ./build/goalc-test_coverage.info
+21 -36
View File
@@ -9,38 +9,35 @@ on:
cachePrefix:
required: true
type: string
uploadArtifacts:
required: false
type: boolean
default: false
jobs:
build:
name: ARM
runs-on: macos-15
runs-on: macos-latest
timeout-minutes: 120
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
BUILDCACHE_COMPRESS_FORMAT: ZSTD
BUILDCACHE_COMPRESS_LEVEL: 19
BUILDCACHE_DIRECT_MODE: true
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Set up ARM64 environment
run: sudo softwareupdate --install-rosetta --agree-to-license
- name: Install Package Dependencies
env:
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_ANALYTICS: 1
run: |
if ! brew install ninja nasm; then
brew update
brew install ninja nasm
fi
run: arch -arm64 brew install cmake ninja
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.22
- name: Setup Buildcache
uses: mikehardy/buildcache-action@v2.1.0
with:
variant: sccache
key: macos-15-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}-${{ github.sha }}
restore-keys: macos-15-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
max-size: 1000M
cache_key: macos-12-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
buildcache_tag: v0.28.1
- name: CMake Generation
env:
@@ -48,31 +45,19 @@ jobs:
CXX: clang++
run: |
cmake -B build --preset=${{ inputs.cmakePreset }} \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
-DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
- name: Build Project
run: cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
- name: List libraries of gk
run: otool -L ./build/game/gk
- name: Run Tests
run: ./test.sh
- name: Prepare artifacts
if: ${{ inputs.uploadArtifacts }}
run: |
strip ./build/goalc/goalc
strip ./build/decompiler/extractor
strip ./build/game/gk
strip ./build/lsp/lsp
- name: Upload artifact
uses: actions/upload-artifact@v7
if: ${{ inputs.uploadArtifacts }}
uses: actions/upload-artifact@v3
with:
name: opengoal-macos-arm-${{ inputs.cachePrefix }}
name: opengoal-macos-${{ inputs.cachePrefix }}
if-no-files-found: error
path: |
./build/goalc/goalc
+18 -33
View File
@@ -17,40 +17,28 @@ on:
jobs:
build:
name: Intel
runs-on: macos-15-intel
runs-on: macos-11
timeout-minutes: 120
steps:
# minimal checkout if we're NOT uploading artifacts
- name: Checkout Repository
if: ${{ ! inputs.uploadArtifacts }}
uses: actions/checkout@v6
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
BUILDCACHE_COMPRESS_FORMAT: ZSTD
BUILDCACHE_COMPRESS_LEVEL: 19
BUILDCACHE_DIRECT_MODE: true
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
# full checkout with tags if we ARE uploading artifacts
- name: Checkout Repository with Tags
if: ${{ inputs.uploadArtifacts }}
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Package Dependencies
env:
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_ANALYTICS: 1
run: |
if ! brew install ninja nasm; then
brew update
brew install ninja nasm
fi
run: brew install cmake nasm ninja
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.22
- name: Setup Buildcache
uses: mikehardy/buildcache-action@v2.1.0
with:
variant: sccache
key: macos-15-intel-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}-${{ github.sha }}
restore-keys: macos-15-intel-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
max-size: 1000M
cache_key: macos-11-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
buildcache_tag: v0.28.1
- name: CMake Generation
env:
@@ -58,15 +46,12 @@ jobs:
CXX: clang++
run: |
cmake -B build --preset=${{ inputs.cmakePreset }} \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache
-DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
- name: Build Project
run: cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
- name: List libraries of gk
run: otool -L ./build/game/gk
- name: Run Tests
run: ./test.sh
@@ -79,7 +64,7 @@ jobs:
strip ./build/lsp/lsp
- name: Upload artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v3
if: ${{ inputs.uploadArtifacts }}
with:
name: opengoal-macos-${{ inputs.cachePrefix }}
+14 -83
View File
@@ -1,52 +1,13 @@
name: 🏭 Draft Release
name: 🏭 Release Pipeline
on:
workflow_dispatch:
inputs:
bump:
description: 'Semver Bump Type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
permissions:
contents: write
push:
tags:
- v*
jobs:
cut_release:
name: Cut Release
runs-on: ubuntu-latest
outputs:
new_tag: ${{ steps.set_tag.outputs.new_tag }}
steps:
# Docs - https://github.com/mathieudutour/github-tag-action
- name: Bump Version and Push Tag
if: github.repository == 'open-goal/jak-project'
id: tag_version
uses: mathieudutour/[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag_prefix: v
default_bump: ${{ github.event.inputs.bump }}
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release create ${{ steps.tag_version.outputs.new_tag }} --generate-notes --draft --repo ${{ github.repository }}
- name: Output new tag
id: set_tag
run: |
echo "new_tag=${{ steps.tag_version.outputs.new_tag }}" >> $GITHUB_OUTPUT
# Windows
build_windows_clang:
needs:
- cut_release
name: "🖥️ Windows"
uses: ./.github/workflows/windows-build-clang.yaml
with:
@@ -57,8 +18,6 @@ jobs:
# Linux
build_linux_clang:
needs:
- cut_release
name: "🐧 Linux"
uses: ./.github/workflows/linux-build-clang.yaml
with:
@@ -69,47 +28,30 @@ jobs:
# macOS
build_macos_intel:
needs:
- cut_release
name: "🍎 MacOS"
uses: ./.github/workflows/macos-build.yaml
with:
cmakePreset: "Release-macos-x86_64-clang-static"
cmakePreset: "Release-macos-clang-static"
cachePrefix: "static"
uploadArtifacts: true
secrets: inherit
build_macos_arm_rosetta:
needs:
- cut_release
name: "🍎 MacOS"
uses: ./.github/workflows/macos-build.yaml
with:
cmakePreset: "Release-macos-x86_64-clang-static"
cachePrefix: "static-arm"
uploadArtifacts: true
secrets: inherit
# Upload the Artifacts
upload_artifacts:
if: github.repository == 'open-goal/jak-project'
needs:
- cut_release
- build_windows_clang
- build_linux_clang
- build_macos_intel
- build_macos_arm_rosetta
name: "Upload Artifacts"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.cut_release.outputs.new_tag }}
- uses: actions/checkout@v4
- name: Prepare Artifact Folder
run: mkdir -p ./ci-artifacts/final
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v3
name: Download all Artifacts
with:
path: ./ci-artifacts/
@@ -122,7 +64,7 @@ jobs:
mkdir -p ./ci-artifacts/linux
./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/linux ./ci-artifacts/opengoal-linux-static ./
pushd ci-artifacts/linux
TAG_VAL=${{ needs.cut_release.outputs.new_tag }}
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
tar czf ../final/opengoal-linux-${TAG_VAL}.tar.gz .
popd
chmod +x ./ci-artifacts/opengoal-linux-static/lsp/lsp
@@ -132,42 +74,31 @@ jobs:
run: |
mkdir -p ./ci-artifacts/windows
./.github/scripts/releases/extract_build_windows.sh ./ci-artifacts/windows ./ci-artifacts/opengoal-windows-static ./
TAG_VAL=${{ needs.cut_release.outputs.new_tag }}
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
7z a -tzip ./ci-artifacts/final/opengoal-windows-${TAG_VAL}.zip ./ci-artifacts/windows/*
cp ./ci-artifacts/opengoal-windows-static/lsp.exe ./ci-artifacts/final/opengoal-lsp-windows-${TAG_VAL}.exe
- name: Prepare Intel macOS Build Assets
- name: Prepare macOS Build Assets
run: |
mkdir -p ./ci-artifacts/macos
./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/macos ./ci-artifacts/opengoal-macos-static ./
pushd ci-artifacts/macos
TAG_VAL=${{ needs.cut_release.outputs.new_tag }}
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
tar czf ../final/opengoal-macos-intel-${TAG_VAL}.tar.gz .
popd
chmod +x ./ci-artifacts/opengoal-macos-static/lsp/lsp
cp ./ci-artifacts/opengoal-macos-static/lsp/lsp ./ci-artifacts/final/opengoal-lsp-macos-intel-${TAG_VAL}.bin
- name: Prepare ARM macOS Build Assets
run: |
mkdir -p ./ci-artifacts/macos-arm
./.github/scripts/releases/extract_build_unix.sh ./ci-artifacts/macos-arm ./ci-artifacts/opengoal-macos-static-arm ./
pushd ci-artifacts/macos-arm
TAG_VAL=${{ needs.cut_release.outputs.new_tag }}
tar czf ../final/opengoal-macos-arm-${TAG_VAL}.tar.gz .
popd
chmod +x ./ci-artifacts/opengoal-macos-static-arm/lsp/lsp
cp ./ci-artifacts/opengoal-macos-static-arm/lsp/lsp ./ci-artifacts/final/opengoal-lsp-macos-arm-${TAG_VAL}.bin
- name: Upload Assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_VAL=${{ needs.cut_release.outputs.new_tag }}
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
gh release upload "${TAG_VAL}" ${{ github.WORKSPACE }}/ci-artifacts/final/* --repo open-goal/jak-project --clobber
- name: Publish Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.BOT_PAT }}
run: |
TAG_VAL=${{ needs.cut_release.outputs.new_tag }}
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
gh release edit ${TAG_VAL} --draft=false --repo open-goal/jak-project
+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
if: github.repository == 'open-goal/jak-project'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
token: ${{ secrets.BOT_PAT }}
@@ -19,7 +19,7 @@ jobs:
wget -O ./game/assets/sdl_controller_db.txt https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.BOT_PAT }}
author: 'OpenGOALBot <[email protected]>'
+16 -20
View File
@@ -21,19 +21,16 @@ jobs:
# Set some sort of timeout in the event of run-away builds. We are limited on concurrent jobs so, get rid of them.
timeout-minutes: 60
steps:
# minimal checkout if we're NOT uploading artifacts
- name: Checkout Repository
if: ${{ ! inputs.uploadArtifacts }}
uses: actions/checkout@v6
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
BUILDCACHE_COMPRESS_FORMAT: ZSTD
BUILDCACHE_COMPRESS_LEVEL: 19
BUILDCACHE_DIRECT_MODE: true
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
# full checkout with tags if we ARE uploading artifacts
- name: Checkout Repository with Tags
if: ${{ inputs.uploadArtifacts }}
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install NASM
# TODO - Simplify this with just the first command once choco 2.0 rolls out everywhere
@@ -45,13 +42,10 @@ jobs:
choco install ${{ github.workspace }}/third-party/nasm/old/nasm.2.15.05.nupkg
}
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.22
- name: Setup Buildcache
uses: mikehardy/buildcache-action@v2.1.0
with:
variant: sccache
key: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}-${{ github.sha }}
restore-keys: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
max-size: 1000M
cache_key: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
- uses: ilammy/msvc-dev-cmd@v1
@@ -64,21 +58,23 @@ jobs:
- name: CMake Generation
shell: cmd
run: cmake -B build --preset=${{ inputs.cmakePreset }} -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache .
run: cmake -B build --preset=${{ inputs.cmakePreset }} -DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache.exe -DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache.exe .
- name: Build Project
shell: cmd
run: cmake --build build --parallel %NUMBER_OF_PROCESSORS%
- name: Run Tests
timeout-minutes: 10
env:
GTEST_OUTPUT: "xml:opengoal-test-report.xml"
run: ./build/bin/goalc-test.exe --gtest_color=yes --gtest_brief=0 --gtest_filter="-*MANUAL_TEST*"
- name: Upload artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v3
if: ${{ inputs.uploadArtifacts }}
with:
name: opengoal-windows-${{ inputs.cachePrefix }}
if-no-files-found: error
path: ./build/bin
+13 -8
View File
@@ -16,9 +16,16 @@ jobs:
runs-on: windows-2022
timeout-minutes: 60
env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md
BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb
BUILDCACHE_COMPRESS_FORMAT: ZSTD
BUILDCACHE_COMPRESS_LEVEL: 19
BUILDCACHE_DIRECT_MODE: true
BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install NASM
# TODO - Simplify this with just the first command once choco 2.0 rolls out everywhere
@@ -30,13 +37,10 @@ jobs:
choco install ${{ github.workspace }}/third-party/nasm/old/nasm.2.15.05.nupkg
}
- name: Setup sccache
uses: hendrikmuhs/ccache-action@v1.2.22
- name: Setup Buildcache
uses: mikehardy/buildcache-action@v2.1.0
with:
variant: sccache
key: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}-${{ github.sha }}
restore-keys: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
max-size: 1000M
cache_key: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
- uses: ilammy/msvc-dev-cmd@v1
@@ -48,7 +52,7 @@ jobs:
- name: CMake Generation - MSVC
shell: cmd
run: cmake -B build --preset=${{ inputs.cmakePreset }} -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache .
run: cmake -B build --preset=${{ inputs.cmakePreset }} -DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache.exe -DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache.exe .
- name: Build Project
shell: cmd
@@ -62,3 +66,4 @@ jobs:
GTEST_OUTPUT: "xml:opengoal-test-report.xml"
run: |
./build/bin/goalc-test.exe --gtest_color=yes --gtest_brief=0 --gtest_filter="-*MANUAL_TEST*"
+3 -14
View File
@@ -1,7 +1,6 @@
# logs
/log
prof.json
/gpu-test.json
# for CMake
/Testing
@@ -10,8 +9,7 @@ prof.json
cmake-build-debug/*
cmake-build-debug--o0/*
.idea/*
build/
build.master/
build/*
/decompiler_out*
logs/*
profile_data/*
@@ -34,7 +32,6 @@ linux-default/
*.bin
*.log
*.p2s
out/
savestate-out/
savestate_out/
failures/
@@ -42,7 +39,6 @@ ee-results*.json
search-results.json
.env
/search-results.json
comp-diff-report.md
# graphics debug
debug_out/
@@ -58,19 +54,13 @@ imgui.ini
node_modules/
# texture replacements
custom_assets/jak1/texture_replacements/*
custom_assets/jak2/texture_replacements/*
custom_assets/jak3/texture_replacements/*
# merc replacements
custom_assets/jak1/merc_replacements/*
custom_assets/jak2/merc_replacements/*
custom_assets/jak3/merc_replacements/*
texture_replacements/*
# generated cmake files
svnrev.h
common/versions/revision.h
ci-artifacts/
out/build/
__pycache__/
# sqlite stuff
@@ -86,4 +76,3 @@ __pycache__/
/TODO.md
unifont-15.0.03.ttf
*.diff
goalc-report.html
+1
View File
@@ -0,0 +1 @@
endOfLine: "auto"
-11
View File
@@ -1,11 +0,0 @@
{
"endOfLine": "auto",
"overrides": [
{
"files": ["**/*.jsonc"],
"options": {
"parser": "json"
}
}
]
}
+36 -238
View File
@@ -7,58 +7,42 @@
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - Unit-Tests - Summary",
"args": [
"--gtest_brief=1"
]
"args": ["--gtest_brief=1"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - Unit-Tests - Verbose",
"args": [
"--gtest_brief=0"
]
"args": ["--gtest_brief=0"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - Draft Tests - Verbose",
"args": [
"--gtest_brief=0",
"--gtest_filter=\"*Draft*\""
]
"args": ["--gtest_brief=0", "--gtest_filter=\"*Draft*\""]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - TypeConsistency - Verbose",
"args": [
"--gtest_brief=0",
"--gtest_filter=\"*TypeConsistency*\""
]
"args": ["--gtest_brief=0", "--gtest_filter=\"*TypeConsistency*\""]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - TypeConsistency - Jak 2 - Verbose",
"args": [
"--gtest_brief=0",
"--gtest_filter=\"*Jak2TypeConsistency*\""
]
"args": ["--gtest_brief=0", "--gtest_filter=\"*Jak2TypeConsistency*\""]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - WithGameTests - Verbose",
"args": [
"--gtest_brief=0",
"--gtest_filter=\"*WithGameTests*\""
]
"args": ["--gtest_brief=0", "--gtest_filter=\"*WithGameTests*\""]
},
{
"type": "default",
@@ -84,18 +68,6 @@
"jak2"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "offline-test.exe (bin\\offline-test.exe)",
"name": "Tests - Offline Tests - Jak 3",
"args": [
"--iso_data_path",
"${workspaceRoot}/iso_data/jak3",
"--game",
"jak3"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
@@ -129,147 +101,56 @@
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 1 - Runtime",
"args": [
"-v",
"--game",
"jak1",
"--",
"-fakeiso",
"-debug"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - GPU Test",
"args": [
"-v",
"--gpu-test",
"opengl",
"--gpu-test-out-path",
"./gpu-test.json"
]
"args": ["-v", "--game", "jak1", "--", "-fakeiso", "-debug"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 1 - Runtime (boot)",
"args": [
"-v",
"--game",
"jak1",
"--",
"-boot",
"-fakeiso",
"-debug"
]
"args": ["-v", "--game", "jak1", "--", "-boot", "-fakeiso", "-debug"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 2 - Runtime (no boot)",
"args": [
"-v",
"--game",
"jak2",
"--",
"-fakeiso",
"-debug"
]
"args": ["-v", "--game", "jak2", "--", "-fakeiso", "-debug"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 2 - Runtime (boot)",
"args": [
"-v",
"--game",
"jak2",
"--",
"-boot",
"-fakeiso",
"-debug"
]
"args": ["-v", "--game", "jak2", "--", "-boot", "-fakeiso", "-debug"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 2 - Runtime (release)",
"args": [
"-v",
"--game",
"jak2",
"--",
"-boot",
"-fakeiso"
]
"args": ["-v", "--game", "jak2", "--", "-boot", "-fakeiso"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 3 - Runtime (boot)",
"args": [
"-v",
"--game",
"jak3",
"--",
"-boot",
"-fakeiso",
"-debug"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 3 - Runtime (release)",
"args": [
"-v",
"--game",
"jak3",
"--",
"-boot",
"-fakeiso"
]
"projectTarget": "goalc.exe (bin\\goalc.exe)",
"name": "REPL",
"args": ["--user-auto"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc.exe (bin\\goalc.exe)",
"name": "REPL - Jak 1",
"args": [
"--user-auto",
"--game",
"jak1"
]
"args": ["--user-auto", "--game", "jak1"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc.exe (bin\\goalc.exe)",
"name": "REPL - Jak 2",
"args": [
"--user-auto",
"--game",
"jak2"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc.exe (bin\\goalc.exe)",
"name": "REPL - Jak 3",
"args": [
"--user-auto",
"--game",
"jak3"
]
"args": ["--user-auto", "--game", "jak2"]
},
{
"type": "default",
@@ -337,84 +218,19 @@
"--config-override \"{\\\"decompile_code\\\": false, \\\"levels_extract\\\": true, \\\"allowed_objects\\\": []}\""
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
"name": "Decompiler - Jak 3",
"args": [
"${workspaceRoot}/decompiler/config/jak3/jak3_config.jsonc",
"${workspaceRoot}/iso_data",
"${workspaceRoot}/decompiler_out",
"--version",
"ntsc_v1"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
"name": "Decompiler - Jak 3 - Extract",
"args": [
"${workspaceRoot}/decompiler/config/jak3/jak3_config.jsonc",
"${workspaceRoot}/iso_data",
"${workspaceRoot}/decompiler_out",
"--version",
"ntsc_v1",
"--config-override \"{\\\"decompile_code\\\": false, \\\"levels_extract\\\": true, \\\"allowed_objects\\\": []}\""
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
"name": "Decompiler - Jak X",
"args": [
"${workspaceRoot}/decompiler/config/jakx/jakx_config.jsonc",
"${workspaceRoot}/iso_data",
"${workspaceRoot}/decompiler_out",
"--version",
"ntsc_v1"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
"name": "Decompiler - Jak X - PAL",
"args": [
"${workspaceRoot}/decompiler/config/jakx/jakx_config.jsonc",
"${workspaceRoot}/iso_data",
"${workspaceRoot}/decompiler_out",
"--version",
"pal"
]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "memory_dump_tool.exe (bin\\memory_dump_tool.exe)",
"name": "Tools - EE Memory Analyze - Jak 1",
"args": [
"\"${workspaceRoot}/eeMemory.bin\"",
"--output-path",
"\"${workspaceRoot}\"",
"--game",
"jak1"
]
"args": ["\"${workspaceRoot}/eeMemory.bin\"", "--output-path", "\"${workspaceRoot}\"", "--game", "jak1"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "memory_dump_tool.exe (bin\\memory_dump_tool.exe)",
"name": "Tools - EE Memory Analyze - Jak 2",
"args": [
"\"${workspaceRoot}/eeMemory.bin\"",
"--output-path",
"\"${workspaceRoot}\"",
"--game",
"jak2"
]
"args": ["\"${workspaceRoot}/eeMemory.bin\"", "--output-path", "\"${workspaceRoot}\"", "--game", "jak2"]
},
{
"type": "default",
@@ -431,53 +247,35 @@
"project": "CMakeLists.txt",
"projectTarget": "extractor.exe (bin\\extractor.exe)",
"name": "Tools - Extractor - Full",
"args": [
"D:\\ISOs\\Jak\\Temp\\bad.iso"
]
"args": ["\"E:\\ISOs\\Jak\\Jak 1.iso\""]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "lsp.exe (bin\\lsp.exe)",
"name": "Tools - LSP",
"args": []
"type" : "default",
"project" : "CMakeLists.txt",
"projectTarget" : "lsp.exe (bin\\lsp.exe)",
"name" : "Tools - LSP",
"args" : []
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "type_searcher.exe (bin\\type_searcher.exe)",
"name": "Tools - Type Searcher",
"args": [
"--game",
"jak2",
"--output-path",
"./search-results.json",
"--size",
255,
"--fields",
"[{\\\"type\\\":\\\"quaternion\\\",\\\"offset\\\":48}]"
]
"type" : "default",
"project" : "CMakeLists.txt",
"projectTarget" : "type_searcher.exe (bin\\type_searcher.exe)",
"name" : "Tools - Type Searcher",
"args" : ["--game", "jak2", "--output-path", "./search-results.json", "--size", 255, "--fields", "[{\\\"type\\\":\\\"quaternion\\\",\\\"offset\\\":48}]"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "formatter.exe (bin\\formatter.exe)",
"name": "Tools - Formatter - Inplace",
"args": [
"--write",
"--file",
"C:\\Users\\xtvas\\Repositories\\opengoal\\jak-project\\goal_src\\jak1\\engine\\camera\\cam-states.gc"
]
"type" : "default",
"project" : "CMakeLists.txt",
"projectTarget" : "formatter.exe (bin\\formatter.exe)",
"name" : "Tools - Formatter - Inplace",
"args" : ["--write", "--file", "C:\\Users\\xtvas\\Repos\\opengoal\\jak-project\\goal_src\\jak2\\pc\\util\\popup-menu-h.gc"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Tests - Formatter",
"args": [
"--gtest_brief=0",
"--gtest_filter=\"*FormatterTests*\""
]
"args": ["--gtest_brief=0", "--gtest_filter=\"*FormatterTests*\""]
}
]
}
}
+2 -20
View File
@@ -4,22 +4,6 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run C++ Tests LLDB",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/goalc-test",
"args": [
"--gtest_brief=0",
"--gtest_filter=*CodeTester*",
"--gtest_break_on_failure"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
},
{
"name": "Append File Docs",
"type": "python",
@@ -27,9 +11,7 @@
"program": "${workspaceFolder}/scripts/ci/lint-characters.py",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}",
"args": [
"--fix"
]
"args": ["--fix"]
},
]
}
}
+92 -3
View File
@@ -5,12 +5,101 @@
"comments": true,
"strings": true
},
"editor.wordBasedSuggestions": "matchingDocuments",
"editor.wordBasedSuggestions": true,
"editor.snippetSuggestions": "top"
},
"cmake.configureOnOpen": false,
"python.formatting.provider": "black",
"files.associations": {
"optional": "cpp",
"algorithm": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"filesystem": "cpp",
"format": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"ostream": "cpp",
"queue": "cpp",
"random": "cpp",
"ranges": "cpp",
"ratio": "cpp",
"regex": "cpp",
"set": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"utility": "cpp",
"valarray": "cpp",
"variant": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"vector": "cpp"
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
}
}
-62
View File
@@ -1,62 +0,0 @@
// Project-local debug tasks
//
// For more documentation on how to configure debug tasks,
// see: https://zed.dev/docs/debugger
[
{
"label": "Game - Jak 1 (Windows)",
"build": "Build - gk",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/gk.exe",
"args": ["-v", "--game", "jak1", "--", "-fakeiso", "-debug"],
"request": "launch",
"adapter": "CodeLLDB",
},
{
"label": "Game - Jak 2 (Windows)",
"build": "Build - gk",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/gk.exe",
"args": ["-v", "--game", "jak2", "--", "-fakeiso", "-debug"],
"request": "launch",
"adapter": "CodeLLDB",
},
{
"label": "Game - Jak 3 (Windows)",
"build": "Build - gk",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/gk.exe",
"args": ["-v", "--game", "jak3", "--", "-fakeiso", "-debug"],
"request": "launch",
"adapter": "CodeLLDB",
},
{
"label": "REPL - Jak 1 (Windows)",
"build": "Build - goalc",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/goalc.exe",
"args": ["--user-auto", "--game", "jak1"],
"request": "launch",
"adapter": "CodeLLDB",
},
{
"label": "REPL - Jak 2 (Windows)",
"build": "Build - goalc",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/goalc.exe",
"args": ["--user-auto", "--game", "jak2"],
"request": "launch",
"adapter": "CodeLLDB",
},
{
"label": "REPL - Jak 3 (Windows)",
"build": "Build - goalc",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/goalc.exe",
"args": ["--user-auto", "--game", "jak3"],
"request": "launch",
"adapter": "CodeLLDB",
},
{
"label": "Tests - Unit - Draft Only (Windows)",
"build": "Build - goalc-test",
"program": "$ZED_WORKTREE_ROOT/out/build/Debug/bin/goalc-test.exe",
"args": ["--gtest_brief=0", "--gtest_filter=\"*Draft*\""],
"request": "launch",
"adapter": "CodeLLDB",
},
]
-111
View File
@@ -1,111 +0,0 @@
// Project tasks configuration. See https://zed.dev/docs/tasks for documentation.
[
{
"label": "Generate CMake - Release",
"command": "task gen-cmake-release",
},
{
"label": "Generate CMake - Debug",
"command": "task gen-cmake-debug",
},
{
"label": "Build Project - Release",
"command": "task build-release",
},
{
"label": "Build Project - Debug",
"command": "task build-debug",
},
{
"label": "Build - goalc",
"command": "task build-debug",
"env": {
"CMAKE_BUILD_ADDITIONAL_ARGS": "--target goalc",
},
"cwd": "$ZED_WORKTREE_ROOT",
},
{
"label": "Build - goalc-test",
"command": "task build-debug",
"env": {
"CMAKE_BUILD_ADDITIONAL_ARGS": "--target goalc-test",
},
"cwd": "$ZED_WORKTREE_ROOT",
},
{
"label": "Build - gk",
"command": "task build-debug",
"env": {
"CMAKE_BUILD_ADDITIONAL_ARGS": "--target gk",
},
"cwd": "$ZED_WORKTREE_ROOT",
},
{
"label": "Tests - Unit - Brief",
"command": "task unit-tests",
"env": {
"TEST_FILTER": "--gtest_brief=0",
},
},
{
"label": "Tests - Unit - Verbose",
"command": "task unit-tests",
"env": {
"TEST_FILTER": "--gtest_brief=1",
},
},
{
"label": "Tests - Unit - Type Consistency",
"command": "task unit-tests",
"env": {
"TEST_FILTER": "--gtest_brief=0 --gtest_filter=\"*TypeConsistency*\"",
},
},
{
"label": "Tests - Unit - WithGameTests",
"command": "task unit-tests",
"env": {
"TEST_FILTER": "--gtest_brief=0 --gtest_filter=\"*WithGameTests*\"",
},
},
{
"label": "Tests - Offline Tests",
"command": "task offline-tests-fast",
},
{
"label": "Tests - Formatter",
"command": "task unit-tests",
"env": {
"TEST_FILTER": "--gtest_brief=0 --gtest_filter=\"*FormatterTests*\"",
},
},
{
"label": "Run - Game (Jak 1)",
"command": "task run-game",
"env": {
"GAME": "jak1",
},
},
{
"label": "Run - Game (Jak 2)",
"command": "task run-game",
"env": {
"GAME": "jak2",
},
},
{
"label": "Run - Game (Jak 3)",
"command": "task run-game",
"env": {
"GAME": "jak3",
},
},
{
"label": "Run - Decompiler",
"command": "task decomp",
},
{
"label": "Format",
"command": "task format",
},
]
+19 -46
View File
@@ -1,8 +1,7 @@
# Top Level CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
project(jak)
include(CTest)
@@ -32,19 +31,6 @@ if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" )
"${PROJECT_SOURCE_DIR}/build/compile_commands.json")
endif()
# NOTE - for SDL, i think SDL3 regressed this - https://github.com/libsdl-org/SDL/pull/6455
# a more recent issue - https://github.com/libsdl-org/SDL/issues/12078
if (APPLE)
enable_language(OBJC)
execute_process(
COMMAND xcrun --show-sdk-path
OUTPUT_VARIABLE MACOSX_SYSROOT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Tell CMake to use it
set(CMAKE_OSX_SYSROOT "${MACOSX_SYSROOT}" CACHE PATH "macOS SDK path" FORCE)
endif()
# Setup compiler flags
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "Clang Detected - Setting Defaults")
@@ -53,14 +39,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-fcxx-exceptions \
-fexceptions \
-fdiagnostics-color=always \
-std=c++17 \
-mavx \
-Wall \
-Wno-c++11-narrowing \
-Wno-c++98-compat \
-Wno-deprecated-this-capture \
-Wno-missing-field-initializers \
-Wno-sign-compare \
-Wno-ignored-qualifiers \
-O3 \
-D_CRT_SECURE_NO_WARNINGS")
@@ -70,7 +53,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Note: this is only _reserved_ memory, not necessarily _committed_ memory
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LDFLAGS} -Xlinker /STACK:16000000")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -g -Wextra")
endif()
# additional c++ and linker flags for release mode for our projects
@@ -103,6 +86,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-Wdisabled-optimization \
-Wformat \
-Wextra \
-Wmissing-include-dirs \
-Woverloaded-virtual \
-Wredundant-decls \
-Wshadow \
@@ -119,6 +103,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
message(STATUS "AppleClang detected - Setting Defaults")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} \
-march=native \
-Wall \
-Winit-self \
-ggdb \
@@ -128,6 +113,7 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
-Wdisabled-optimization \
-Wformat \
-Wextra \
-Wmissing-include-dirs \
-Woverloaded-virtual \
-Wredundant-decls \
-Wshadow \
@@ -135,13 +121,6 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
-fdiagnostics-color=always"
)
# TODO - make a proper flag for arm compiling
if (CMAKE_APPLE_SILICON_PROCESSOR STREQUAL "x86_64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mcrc")
endif()
# additional c++ flags for release mode for our projects
if(CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
@@ -189,15 +168,16 @@ endif()
function(build_third_party_lib dir_name target_name)
add_subdirectory(third-party/${dir_name} EXCLUDE_FROM_ALL)
# Convert all public include dirs to SYSTEM
get_target_property(INCLUDES ${target_name} INTERFACE_INCLUDE_DIRECTORIES)
if(INCLUDES)
set_target_properties(${target_name} PROPERTIES
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${INCLUDES}"
)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# For Microsoft Visual C++ Compiler (MSVC)
target_compile_options(${target_name} PRIVATE /w)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
# For GNU Compiler Collection (GCC) or Clang
target_compile_options(${target_name} PRIVATE -w)
endif()
endfunction()
# Dependencies and Libraries
# includes relative to top level jak-project folder
include_directories(./)
@@ -210,9 +190,6 @@ include_directories(SYSTEM third-party/inja)
# TODO - probably integrate with ZSTD since we have it already
set(CURL_USE_LIBSSH2 OFF)
set(CURL_ZLIB OFF)
# suddenly became enabled on macOS atleast, only needed for unicode URLs
# wasn't being properly statically linked
set(USE_LIBIDN2 OFF)
if(WIN32)
set(CURL_USE_SCHANNEL ON) # native Windows SSL support
elseif(APPLE)
@@ -242,15 +219,12 @@ build_third_party_lib(sqlite3 sqlite3)
# build tree-sitter parser
include_directories(third-party/tree-sitter/tree-sitter/lib/include)
include_directories(third-party/tree-sitter/tree-sitter-opengoal/include)
build_third_party_lib(tree-sitter tree-sitter)
# native OS dialogs for error messages
build_third_party_lib(libtinyfiledialogs libtinyfiledialogs)
# build format library
include_directories(third-party/fmt/include)
build_third_party_lib(fmt fmt)
# build common library
add_subdirectory(common)
@@ -270,9 +244,9 @@ build_third_party_lib(zstd libzstd_static)
# build SDL
include(SDLOptions)
if(STATICALLY_LINK)
build_third_party_lib(SDL SDL3-static)
build_third_party_lib(SDL SDL2-static)
else()
build_third_party_lib(SDL SDL3-shared)
build_third_party_lib(SDL SDL2)
endif()
# build imgui
@@ -303,11 +277,10 @@ include(test/CMakeLists.txt)
# build lzokay library
build_third_party_lib(lzokay lzokay)
# build format library
build_third_party_lib(fmt fmt)
build_third_party_lib(stb_image stb_image)
# build draco library for tinygltf compression support
include_directories(third-party/draco/src)
add_subdirectory(third-party/draco)
add_compile_definitions(TINYGLTF_ENABLE_DRACO)
build_third_party_lib(tiny_gltf tiny_gltf)
build_third_party_lib(xdelta3 xdelta3)
+9 -73
View File
@@ -62,22 +62,12 @@
"name": "base-linux-debug",
"hidden": true,
"inherits": "base",
"binaryDir": "${sourceDir}/build/Debug/bin",
"binaryDir": "${sourceDir}/build/Release/bin",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/${presetName}"
}
},
{
"name": "base-macos-debug",
"hidden": true,
"inherits": "base",
"binaryDir": "${sourceDir}/build/Debug/bin",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
}
},
{
"name": "base-macos-release",
"hidden": true,
@@ -88,22 +78,6 @@
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/${presetName}"
}
},
{
"name": "base-macos-arm64",
"hidden": true,
"cacheVariables": {
"CMAKE_APPLE_SILICON_PROCESSOR": "arm64",
"CMAKE_OSX_ARCHITECTURES": "arm64"
}
},
{
"name": "base-macos-x86_64",
"hidden": true,
"cacheVariables": {
"CMAKE_APPLE_SILICON_PROCESSOR": "x86_64",
"CMAKE_OSX_ARCHITECTURES": "x86_64"
}
},
{
"name": "base-clang",
"hidden": true,
@@ -134,16 +108,6 @@
"description": "Build with Clang with Debug Symbols",
"inherits": ["base-windows-debug", "base-clang"]
},
{
"name": "Debug-windows-clang-static",
"displayName": "Windows Static Debug (clang)",
"description": "Build with Clang as Debug but statically linked",
"inherits": ["base-windows-debug", "base-clang"],
"cacheVariables": {
"STATICALLY_LINK": "true",
"ZYDIS_BUILD_SHARED_LIB": "OFF"
}
},
{
"name": "RelWithDebInfo-windows-clang",
"displayName": "Windows RelWithDebInfo (clang)",
@@ -185,38 +149,16 @@
"inherits": ["base-linux-release", "base-clang"]
},
{
"name": "Debug-macos-arm64-clang",
"displayName": "MacOS ARM64 Debug (clang)",
"description": "Build for ARM64 with Clang as Debug",
"inherits": ["base-macos-arm64", "base-macos-debug", "base-clang"]
"name": "Release-macos-clang",
"displayName": "MacOS Release (clang)",
"description": "Build with Clang as Release without Debug Symbols",
"inherits": ["base-macos-release", "base-clang"]
},
{
"name": "Release-macos-arm64-clang",
"displayName": "MacOS ARM64 Release (clang)",
"description": "Build for ARM64 with Clang as Release without Debug Symbols",
"inherits": ["base-macos-release", "base-macos-arm64", "base-clang"]
},
{
"name": "Release-macos-x86_64-clang",
"displayName": "MacOS x86_64 Release (clang)",
"description": "Build for x86_64 with Clang as Release without Debug Symbols",
"inherits": ["base-macos-release", "base-macos-x86_64", "base-clang"]
},
{
"name": "Release-macos-arm64-clang-static",
"displayName": "MacOS ARM64 Static Release (clang)",
"description": "Build for ARM64 with Clang as Release without Debug Symbols but statically linked",
"inherits": ["base-macos-release", "base-macos-arm64", "base-clang"],
"cacheVariables": {
"STATICALLY_LINK": "true",
"ZYDIS_BUILD_SHARED_LIB": "OFF"
}
},
{
"name": "Release-macos-x86_64-clang-static",
"displayName": "MacOS x86_64 Static Release (clang)",
"description": "Build for x86_64 with Clang as Release without Debug Symbols but statically linked, requires MacOS Sequoia",
"inherits": ["base-macos-release", "base-macos-x86_64", "base-clang"],
"name": "Release-macos-clang-static",
"displayName": "MacOS Static Release (clang)",
"description": "Build with Clang as Release without Debug Symbols but statically linked",
"inherits": ["base-macos-release", "base-clang"],
"cacheVariables": {
"STATICALLY_LINK": "true",
"ZYDIS_BUILD_SHARED_LIB": "OFF"
@@ -241,12 +183,6 @@
"ZYDIS_BUILD_SHARED_LIB": "OFF"
}
},
{
"name": "Debug-linux-clang",
"displayName": "Linux Release (clang)",
"description": "Build with Clang as Release with Debug Symbols",
"inherits": ["base-linux-debug", "base-clang"]
},
{
"name": "Debug-linux-clang-static",
"displayName": "Linux Static Release (clang)",
+1 -1
View File
@@ -1,6 +1,6 @@
ISC License
Copyright (c) 2020-2026 OpenGOAL Team
Copyright (c) 2020-2022 OpenGOAL Team
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
+300 -31
View File
@@ -13,7 +13,7 @@
## Please read first <!-- omit from toc -->
> [!IMPORTANT]
> Our repositories on GitHub are for development of the project and tracking active issues. Most of the information you will find here pertains to setting up the project for development purposes and is not relevant to the end-user.
> Our repositories on GitHub are primarily for development of the project and tracking active issues. Most of the information you will find here pertains to setting up the project for development purposes and is not relevant to the end-user.
For a setup guide on how to install and play the game there is the following video that you can check out: https://youtu.be/K84UUMnkJc4
@@ -22,27 +22,45 @@ For questions or additional information pertaining to the project, we have a Dis
Additionally, you can find further documentation and answers to **frequently asked questions** on the project's main website: https://opengoal.dev
> [!WARNING]
> **Do not use this decompilation project without the use of your own legally purchased copy of the game.** OpenGOAL does not include any assets from the original games, so you must provide your own legitimately obtained PS2 copy of the game. OpenGOAL supports every retail PAL, NTSC, and NTSC-J build, including Greatest Hits copies. Please note that does NOT include any of the later releases (PS3/PS4/PS5).
> **Do not use this decompilation project without providing your own legally purchased copy of the game.** We do not distribute any assets from the game - you must use your own legitimately obtained PS2 copy of the game. We support every retail PAL, NTSC, and NTSC-J build, including Greatest Hits copies.
- [Project Description](#project-description)
- [Current Status](#current-status)
- [Methodology](#methodology)
- [Setting up a Development Environment](#setting-up-a-development-environment)
- [OS Setup](#os-setup)
- [Editor Setup](#editor-setup)
- [Docker](#docker)
- [Linux](#linux)
- [Ubuntu (20.04)](#ubuntu-2004)
- [Arch](#arch)
- [Fedora](#fedora)
- [Windows](#windows)
- [Required Software](#required-software)
- [Using Visual Studio](#using-visual-studio)
- [MacOS](#macos)
- [Intel Based](#intel-based)
- [Apple Silicon](#apple-silicon)
- [VSCode](#vscode)
- [Building and Debugging](#building-and-debugging)
- [Building and Running the Game](#building-and-running-the-game)
- [Extract Assets](#extract-assets)
- [Build the Game (Running the Compiler)](#build-the-game-running-the-compiler)
- [Build the Game](#build-the-game)
- [Run the Game](#run-the-game)
- [Connecting the REPL to the Game](#connecting-the-repl-to-the-game)
- [Running the Game Without Auto-Booting](#running-the-game-without-auto-booting)
- [Interacting with the Game](#interacting-with-the-game)
- [Technical Project Overview](#technical-project-overview)
- [`goalc`](#goalc)
- [Running the compiler](#running-the-compiler)
- [`decompiler`](#decompiler)
- [Running the decompiler](#running-the-decompiler)
- [`goal_src/`](#goal_src)
- [`game` runtime](#game-runtime)
## Project Description
The project's goal is to port the original trilogy (Jak 1 -> Jak 3) to PC. Over 98% of the games were written in GOAL, a custom LISP language developed by Naughty Dog. Our strategy is:
This project is to port the original Jak and Daxter and Jak II to PC. Over 98% of the games are written in GOAL, a custom Lisp language developed by Naughty Dog. Our strategy is:
- decompile the original game code into human-readable GOAL code
- develop our own compiler for GOAL and recompile the game code for x86-64
- develop our own compiler for GOAL and recompile game code for x86-64
- create a tool to extract game assets into formats that can be easily viewed or modified
- create tools to repack game assets into a format that our port uses.
@@ -52,50 +70,217 @@ Our objectives are:
- try to match things from the original game and development as possible. For example, the original GOAL compiler supported live modification of code while the game is running, so we do the same, even though it's not required for just porting the game.
- support modifications. It should be possible to make edits to the code without everything else breaking.
At the moment we support **x86_64** on Windows, Linux and macOS (via Rosetta translation). There are no plans to ever make a mobile release.
We support both Linux and Windows on x86-64.
> We do not support, or plan to support the ARM architecture. This means that this will not run on devices such as an M1 Mac or a mobile device.
### Current Status
- Jak 1 has been considered in a polished, complete state for years at this point.
- Jak 2 is considered in beta due to a few issues we are aware of that need fixing, however to the casual user, the game is essentially complete.
- Jak 3 has a good amount of work left to do.
Jak 1 is largely playable from start to finish with a handful of bugs that are continually being ironed out. Jak 2 is in development.
![](./docs/img/promosmall1.png)
![](./docs/img/promosmall2.png)
YouTube playlist showcasing some of the early progress for Jak 1:
YouTube playlist:
https://www.youtube.com/playlist?list=PLWx9T30aAT50cLnCTY1SAbt2TtWQzKfXX
### Methodology
To assist with decompiling, we've built a decompiler that can process GOAL code and unpack game assets. We manually specify function types and locations where we believe the original code had type casts (or where they feel appropriate) until the decompilation succeeds, then we clean up the output of the decompiled code by adding comments and adjusting formatting, then save it in `goal_src/`.
To help with decompiling, we've built a decompiler that can process GOAL code and unpack game assets. We manually specify function types and locations where we believe the original code had type casts (or where they feel appropriate) until the decompiler succeeds, then we clean up the output of the decompiled code by adding comments and adjusting formatting, then save it in `goal_src`.
Our decompiler is designed specifically for processing the output of the original GOAL compiler. As a result, when given correct casts, it often produces code that can be directly fed into a compiler and works perfectly. This is continually tested as part of our unit tests.
## Setting up a Development Environment
The remainder of this README is aimed at people interested in building the project from source, typically with the intention of contributing as a developer.
The remainder of this README is catered towards people interested in building the project from source, typically with the intention on contributing as a developer.
If this does not sound like you and you just want to play the game, refer to the above section [Quick Start](#quick-start)
### OS Setup
### Docker
- [Windows](/docs/setup/system/windows.md)
- [Linux](/docs/setup/system/linux.md)
- [MacOS](/docs/setup/system/macos.md)
- [Docker](/docs/setup/system/docker.md)
All three Linux systems are supported using Docker.
### Editor Setup
Pick your supported prefered flavour of linux and build your chosen image
You can of course use whatever editor you want, but here is some documentation that should help you get started on some of the editor's we have used and have written about:
```
docker build -f docker/(Arch|Fedora|Ubuntu)/Dockerfile -t jak .
```
- [Visual Studio (Windows)](/docs/setup/dev/vs.md)
- [Visual Studio Code](/docs/setup/dev/vscode.md)
- [Zed](/docs/setup/dev/zed.md)
This will create an image with all required dependencies and already built.
```
docker run -v "$(pwd)"/build:/home/jak/jak-project/build -it jak bash
```
Note: If you change the content of the `build/` directory you'll need to rerun the `build` command. Alternatively you can get the build via `docker cp`.
This will link your `build/` folder to the images so can validate your build or test it on an external device.
Docker images can be linked into your IDE (e.g. CLion) to help with codesniffing, static analysis, run tests and continuous build.
Unfortunately you'll still need task runner on your local machine to run the game or instead, manually run the game via the commands found in `Taskfile.yml`.
### Linux
#### Ubuntu (20.04)
Install packages and init repository:
```sh
sudo apt install gcc make cmake build-essential g++ nasm clang-format libxrandr-dev libxinerama-dev libxcursor-dev libpulse-dev libxi-dev python libgl1-mesa-dev libssl-dev
sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
```
Compile:
```sh
cmake -B build && cmake --build build -j 8
```
Run tests:
```sh
./test.sh
```
Note: we have found that `clang` and `lld` are significantly faster to compile and link than `gcc`, generate faster code, and have better warning messages. To install these:
```sh
sudo apt install lld clang
```
and run `cmake` (in a fresh build directory) with:
```sh
cmake -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ..
```
#### Arch
Install packages and init repository:
```sh
sudo pacman -S cmake libpulse base-devel nasm python libx11 libxrandr libxinerama libxcursor libxi
yay -S go-task
```
For Arch only, replace `task` with `go-task` in the rest of the instructions.
Compile:
```sh
cmake -B build && cmake --build build -j 8
```
Run tests:
```sh
./test.sh
```
#### Fedora
Install packages and init repository:
```sh
sudo dnf install cmake python lld clang nasm libX11-devel libXrandr-devel libXinerama-devel libXcursor-devel libXi-devel pulseaudio-libs-devel mesa-libGL-devel
sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
```
Compile with `clang`:
```sh
cmake -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -B build
cmake --build build -j$(nproc)
```
Run tests:
```sh
./test.sh
```
### Windows
#### Required Software
We primarily use Visual Studio on Windows for C++ development. Download the latest community edition from [here](https://visualstudio.microsoft.com/vs/). At the time of writing this is Visual Studio 2022.
You will require the `Desktop development with C++` workload. This can be selected during the installation, or after via the `Visual Studio Installer`, modifying the Visual Studio Installation.
On Windows, it's recommended to use a package manager, we use Scoop. Follow the steps on the bottom of the homepage [here](https://scoop.sh/) to get it.
Once Scoop is installed, run the following commands:
```sh
scoop install git llvm nasm python task
```
#### Using Visual Studio
Clone the repository by running the following command in your folder of choice.
```sh
git clone https://github.com/open-goal/jak-project.git
```
This will create a `jak-project` folder, open the project as a CMake project via Visual Studio.
![](./docs/img/windows/open-project.png)
Then build the entire project as `Windows Release (clang)`. You can also press Ctrl+Shift+B as a hotkey for Build All. We currently prefer `clang` on Windows as opposed to `msvc`, though it should work as well!
![](./docs/img/windows/release-build.png)
![](./docs/img/windows/build-all.png)
### MacOS
> NOTE: At this time you can only run the game on macOS if you have an Intel processor.
Ensure that you have Xcode command line tools installed (this installs things like Apple Clang). If you don't, you can run the following command:
```bash
xcode-select --install
```
#### Intel Based
```bash
brew install go-task/tap/go-task
brew install cmake nasm ninja go-task clang-format
cmake -B build --preset=Release-macos-clang
cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
```
#### Apple Silicon
**Not Supported at This Time**
```bash
brew install go-task/tap/go-task
brew install cmake ninja go-task clang-format
cmake -B build --preset=Release-macos-clang
cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
```
You may have to add the MacOS SDK to your `LIBRARY_PATH`:
- `export LIBRARY_PATH="$LIBRARY_PATH:/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"`
### VSCode
If you either don't want to or cannot use Visual Studio for working with the C++ project, VSCode is a good alternatively.
The `clangd` extension is [recommended](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd) and requires `clangd` to be on your `$PATH`. If you can run `clangd` in a terminal successfully then you should be good to go.
Once you generate your CMake for the first time the clangd LSP should be able to index the project and give you intellisense.
#### Building and Debugging
TODO - Consider Contributing Documentation :)
### Building and Running the Game
Getting a running game involves 4 main steps:
Getting a running game involves 4 steps:
1. Build C++ tools (follow Getting Started steps above for your platform)
2. Extract assets from the game
@@ -104,16 +289,25 @@ Getting a running game involves 4 main steps:
#### Extract Assets
First, we have to setup our environment so we know which game and version we are operating with. For the black label version of Jak 1 we would run the following:
First, setup your settings so the following scripts know which game you are using, and which version. For the black label version of the game, run the following in a terminal:
```sh
task set-game-jak1
task set-decomp-ntscv1 # or for example for PAL, `task set-decomp-pal`
task set-decomp-ntscv1
```
For other versions of the game, you will need to use a different `-set-decomp-<VERSION>` command. An example for the PAL version:
```sh
task set-game-jak1
task set-decomp-pal
```
> Run `task --list` to see the other available options
Next, ensure you extract your ISO file contents into the relevant `iso_data/<game-name>` folder. In the case of Jak 1 this is `iso_data/jak1`.
> At the time of writing, only Jak 1 is expected to work end-to-end!
The first step is to extract your ISO file contents into the `iso_data/<game-name>` folder. In the case of Jak 1 this is `iso_data/jak1`.
Once this is done, open a terminal in the `jak-project` folder and run the following:
@@ -121,7 +315,7 @@ Once this is done, open a terminal in the `jak-project` folder and run the follo
task extract
```
#### Build the Game (Running the Compiler)
#### Build the Game
The next step is to build the game itself. To do so, in the same terminal run the following:
@@ -158,7 +352,7 @@ g > (mi)
#### Run the Game
Finally the game can be launched. Open a second terminal from the `jak-project` directory and run the following:
Finally the game can be ran. Open a second terminal from the `jak-project` directory and run the following:
```sh
task boot-game
@@ -214,6 +408,81 @@ gc> (test-play)
gc>
```
#### Interacting with the Game
In the graphics window, you can use the period key to bring up the debug menu. Controllers also work, using the same mapping as the original game.
Check out the `pc_debug`, `examples` and `pc` folders under `goal_src` for some examples of GOAL code we wrote. The debug files that are not loaded automatically by the engine have instructions for how to run them.
## Technical Project Overview
Some more detail about the various components of the project can be found [here](/docs/project-overview.md)
There are four main components to the project.
1. `goalc` - the GOAL compiler for x86-64
2. `decompiler` - our decompiler
3. `goal_src/` - the folder containing all OpenGOAL / GOOS code
4. `game` - aka the runtime written in C++
Let's break down each component.
### `goalc`
Our implementation of GOAL is called OpenGOAL.
All of the compiler source code is in `goalc/`. The compiler is controlled through a prompt which can be used to enter commands to compile, connect to a running GOAL program for interaction, run the OpenGOAL debugger, or, if you are connected to a running GOAL program, can be used as a REPL to run code interactively. In addition to compiling code files, the compiler has features to pack and build data files.
#### Running the compiler
**Environment Agnostic**
If you have installed `task` as recommended above, you can run the compiler with `task repl`
**Linux**
To run the compiler on Linux, there is a script `scripts/shell/gc.sh`.
**Windows**
On Windows, there is a `scripts/batch/gc.bat` scripts and a `scripts/batch/gc-no-lt.bat` script, the latter of which will not attempt to automatically attach to a running target.
### `decompiler`
The second component to the project is the decompiler.
The decompiler will output code and other data intended to be inspected by humans in the `decompiler_out` folder. Files in this folder will not be used by the compiler.
#### Running the decompiler
You must have a copy of the PS2 game and place all files from the DVD inside a folder corresponding to the game within `iso_data` folder (`jak1` for Jak 1 Black Label, etc.), as seen in this picture:
![](./docs/img/iso_data-help.png)
The decompiler will extract assets to the `assets` folder. These assets will be used by the compiler when building the port, and you may want to turn asset extraction off after running it once.
**Environment Agnostic**
If you have installed `task` as recommended above, you can run the compiler with `task decomp`
**Linux**
To run, you can use `scripts/shell/decomp.sh` to run the decompiler
**Windows**
To run, you can use `scripts/shell/decomp-jak1.bat` to run the decompiler
### `goal_src/`
The game source code, written in OpenGOAL, is located in `goal_src`. All GOAL and GOOS code should be in this folder.
### `game` runtime
The final component is the "runtime", located in `game`. This is the part of the game that's written in C++.
In the port, that includes:
- The "C Kernel", which contains the GOAL linker and some low-level GOAL language features. GOAL has a completely custom dynamically linked object file format so in order to load the first GOAL code, you need a linker written in C++. Some low-level functions for memory allocation, communicating with the I/O Processor, symbol table, strings, and the type system are also implemented in C, as these are required for the linker. It also listens for incoming messages from the compiler and passes them to the running game. This also initializes the game, by initializing the PS2 hardware, allocating the GOAL heaps, loading the GOAL kernel off of the DVD, and executing the kernel dispatcher function. This is in the `game/kernel` folder. This should be as close as possible to the game, and all differences should be noted with a comment.
- Implementation of Sony's standard library. GOAL code can call C library functions, and Naughty Dog used some Sony library functions to access files, memory cards, controllers, and communicate with the separate I/O Processor. The library functions are in `game/sce`. Implementations of library features specific to the PC port are located in `game/system`.
- The I/O Processor driver, OVERLORD. The PS2 had a separate CPU called the I/O Processor (IOP) that was directly connected to the DVD drive hardware and the sound hardware. Naughty Dog created a custom driver for the IOP that handled streaming data off of the DVD. It is much more complicated than I first expected. It's located in `game/overlord`. Like the C kernel, we try to keep this as close as possible to the actual game.
- Sound code. Naughty Dog used a third party library for sound called `989SND`. Code for the library and an interface for it is located in `game/sound`.
- PC specific graphics code. We have a functional OpenGL renderer and context that can create a game window and display graphics on it. The specific renderers used by the game however are mostly implemented. Aside from post-processing effects, everything in the game is rendered. This is located in `game/graphics`. While many liberties will be taken to make this work, the end result should very closely match the actual game.
- Extra assets used by the port in some fashion, located in `game/assets`. These include extra text files, icons, etc.
+49 -117
View File
@@ -10,35 +10,33 @@ dotenv:
tasks:
# SETTINGS / CONFIGURATION
settings:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --info"
- 'python ./scripts/tasks/update-env.py --info'
set-game-jak1:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --game jak1"
- 'python ./scripts/tasks/update-env.py --game jak1'
set-game-jak2:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --game jak2"
- 'python ./scripts/tasks/update-env.py --game jak2'
set-game-jak3:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --game jak3"
set-game-jakx:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --game jakx"
- 'python ./scripts/tasks/update-env.py --game jak3'
set-decomp-ntscv1:
desc: "aka black label"
cmds:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --decomp_config ntscv1"
- 'python ./scripts/tasks/update-env.py --decomp_config ntscv1'
set-decomp-ntscv2:
desc: "aka red label"
cmds:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --decomp_config ntscv2"
- 'python ./scripts/tasks/update-env.py --decomp_config ntscv2'
set-decomp-pal:
desc: "PAL region version"
cmds:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --decomp_config pal"
- 'python ./scripts/tasks/update-env.py --decomp_config pal'
set-decomp-ntscjp:
desc: "NTSC-J region version"
cmds:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --decomp_config ntscjp"
- 'python ./scripts/tasks/update-env.py --decomp_config ntscjp'
set-decomp-ntscko:
desc: "NTSC-K region version"
cmds:
- "{{.PYTHON}} ./scripts/tasks/update-env.py --decomp_config ntscko"
- 'python ./scripts/tasks/update-env.py --decomp_config ntscko'
# GENERAL
extract:
desc: "Extracts the game's assets from './iso_data' with the set decompiler config"
@@ -46,7 +44,7 @@ tasks:
- sh: test -f {{.DECOMP_BIN_RELEASE_DIR}}/decompiler{{.EXE_FILE_EXTENSION}}
msg: "Couldn't locate decompiler executable in '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler'"
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": false, "levels_extract": true, "allowed_objects": []}'''
- "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler \"./decompiler/config/{{.DECOMP_CONFIG}}\" \"./iso_data\" \"./decompiler_out\" --version \"{{.DECOMP_CONFIG_VERSION}}\" --config-override '{\"decompile_code\": false, \"levels_extract\": true, \"allowed_objects\": []}'"
boot-game:
desc: "Boots the game, it will fail if it's not already compiled!"
preconditions:
@@ -69,35 +67,6 @@ tasks:
cmds:
- "{{.GK_BIN_RELEASE_DIR}}/gk -v --game {{.GAME}} -- -fakeiso -debug"
# DEVELOPMENT
clean-cmake:
desc: "Cleans up any existing cmake artifacts"
cmds:
- cmd: "{{.PYTHON}} ./scripts/tasks/delete-file-or-folder.py --path ./build"
- cmd: "{{.PYTHON}} ./scripts/tasks/delete-file-or-folder.py --path ./out/build"
gen-cmake:
desc: "Generate the CMake"
cmds:
- "cmake {{.CMAKE_GEN_BUILD_DIR_DEBUG}} --preset={{.CMAKE_PRESET}}"
gen-cmake-release:
desc: "Generate the CMake"
cmds:
- "cmake {{.CMAKE_GEN_BUILD_DIR_RELEASE}} --preset={{.DEFAULT_CMAKE_RELEASE_PRESET}}"
gen-cmake-debug:
desc: "Generate the CMake"
cmds:
- "cmake {{.CMAKE_GEN_BUILD_DIR_DEBUG}} --preset={{.DEFAULT_CMAKE_DEBUG_PRESET}}"
build:
desc: "Build the project using the generated CMake"
cmds:
- "cmake {{.CMAKE_BUILD_DIR_DEBUG}} --parallel 8"
build-release:
desc: "Build the project using the generated CMake"
cmds:
- "cmake {{.CMAKE_BUILD_DIR_RELEASE}} --parallel 8"
build-debug:
desc: "Build the project using the generated CMake"
cmds:
- "cmake {{.CMAKE_BUILD_DIR_DEBUG}} --parallel 8 --target goalc"
repl:
desc: "Start the REPL"
preconditions:
@@ -105,78 +74,55 @@ tasks:
msg: "Couldn't locate compiler executable in '{{.GOALC_BIN_RELEASE_DIR}}/goalc'"
cmds:
- "{{.GOALC_BIN_RELEASE_DIR}}/goalc --user-auto --game {{.GAME}}"
format:
desc: "Format code"
cmds:
- cmd: python ./scripts/cpp/format-includes.py
- cmd: python ./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test tools lsp -i
# npm install -g prettier
- cmd: npx prettier --write ./decompiler/config/jak1/**/*.jsonc
ignore_error: true
- cmd: npx prettier --write ./decompiler/config/jak2/**/*.jsonc
ignore_error: true
format-json:
desc: "Format code"
cmds:
# npm install -g prettier
- cmd: npx prettier --write ./decompiler/config/jak*/**/*.jsonc
- cmd: npx prettier --write ./decompiler/config/jak1/**/*.jsonc
ignore_error: true
- cmd: npx prettier --write ./decompiler/config/jak2/**/*.jsonc
ignore_error: true
format:
desc: "Format code"
cmds:
- cmd: "{{.PYTHON}} ./scripts/cpp/format-includes.py"
- cmd: "{{.PYTHON}} ./scripts/ci/lint-trailing-whitespace.py --fix"
- cmd: "{{.PYTHON}} ./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test tools lsp -i"
- task: format-json
format-gsrc:
desc: "Run formatter on gsrc file"
preconditions:
- sh: test -f {{.FORMATTER_BIN_RELEASE_DIR}}/formatter{{.EXE_FILE_EXTENSION}}
msg: "Couldn't locate formatter executable in '{{.FORMATTER_BIN_RELEASE_DIR}}/formatter'"
cmds:
- "{{.FORMATTER_BIN_RELEASE_DIR}}/formatter --write --file '{{.FILE}}'"
# DECOMPILING
decomp:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": true, "levels_extract": false}'''
- "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler \"./decompiler/config/{{.DECOMP_CONFIG}}\" \"./iso_data\" \"./decompiler_out\" --version \"{{.DECOMP_CONFIG_VERSION}}\" --config-override '{\"decompile_code\": true, \"levels_extract\": false}'"
decomp-file:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": true, "levels_extract": false, "allowed_objects": ["{{.FILE}}"]}'''
- "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler \"./decompiler/config/{{.DECOMP_CONFIG}}\" \"./iso_data\" \"./decompiler_out\" --version \"{{.DECOMP_CONFIG_VERSION}}\" --config-override '{\"decompile_code\": true, \"levels_extract\": false, \"allowed_objects\": [\"{{.FILE}}\"]}'"
decomp-clean:
cmds:
- '{{.PYTHON}} ./scripts/tasks/clean-decomp.py --game "{{.GAME}}"'
- python ./scripts/tasks/clean-decomp.py --game "{{.GAME}}"
lint-gsrc-file:
cmds:
- '{{.PYTHON}} ./scripts/gsrc/lint-gsrc-file.py --game {{.GAME}} --file "{{.FILE}}"'
- python ./scripts/gsrc/lint-gsrc-file.py --game {{.GAME}} --file {{.FILE}}
update-gsrc:
cmds:
- '{{.PYTHON}} ./scripts/gsrc/update-gsrc-via-refs.py --game "{{.GAME}}" --decompiler "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler" --decompiler_config {{.DECOMP_CONFIG}} --version {{.DECOMP_CONFIG_VERSION}}'
- python ./scripts/gsrc/update-gsrc-via-refs.py --game "{{.GAME}}" --decompiler "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler" --decompiler_config {{.DECOMP_CONFIG}} --version {{.DECOMP_CONFIG_VERSION}}
update-gsrc-glob:
cmds:
- '{{.PYTHON}} ./scripts/gsrc/update-gsrc-via-refs.py --game "{{.GAME}}" --file_pattern "{{.GLOB}}" --decompiler "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler" --decompiler_config {{.DECOMP_CONFIG}} --version {{.DECOMP_CONFIG_VERSION}}'
- python ./scripts/gsrc/update-gsrc-via-refs.py --game "{{.GAME}}" --file_pattern "{{.GLOB}}" --decompiler "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler" --decompiler_config {{.DECOMP_CONFIG}} --version {{.DECOMP_CONFIG_VERSION}}
update-gsrc-file:
cmds:
- task: decomp-file
- '{{.PYTHON}} ./scripts/gsrc/update-from-decomp.py --game "{{.GAME}}" --file "{{.FILE}}"'
- python ./scripts/gsrc/update-from-decomp.py --game "{{.GAME}}" --file {{.FILE}}
- task: lint-gsrc-file
copy-common-naming:
cmds:
- '{{.PYTHON}} ./scripts/gsrc/copy-common-naming.py --file "{{.FILE}}" --decompiler "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler"'
- task: format-json
copy-common-naming-from-refs:
cmds:
- '{{.PYTHON}} ./scripts/gsrc/copy-common-naming.py --update-names-from-refs --decompiler "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler"'
- task: format-json
# ASSET RIPPING
rip-textures:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": false, "levels_extract": true, "save_texture_pngs": true}'''
rip-levels:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": false, "levels_extract": true, "rip_levels": true}'''
rip-collision:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": false, "levels_extract": true, "extract_collision": true, "rip_collision": true}'''
rip-audio:
cmds:
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out" --version "{{.DECOMP_CONFIG_VERSION}}" --config-override ''{"decompile_code": false, "levels_extract": true, "rip_streamed_audio": true}'''
# TOOLS
analyze-ee-memory:
cmds:
- '{{.PYTHON}} ./scripts/tasks/extract-zip.py --file "{{.FILE}}" --out ./savestate_out/'
- "{{.MEMDUMP_BIN_RELEASE_DIR}}/memory_dump_tool ./savestate_out/eeMemory.bin --output-path ./ --game {{.GAME}} > ee-analysis.log"
- python ./scripts/tasks/extract-zip.py --file "{{.FILE}}" --out ./savestate_out/
- '{{.MEMDUMP_BIN_RELEASE_DIR}}/memory_dump_tool ./savestate_out/eeMemory.bin --output-path ./ --game {{.GAME}} > ee-analysis.log'
watch-pcsx2:
# {{.PYTHON}} -m pip install -U "watchdog[watchmedo]"
# python -m pip install -U "watchdog[watchmedo]"
cmds:
- watchmedo shell-command --drop --patterns="*.p2s" --recursive --command='task analyze-ee-memory FILE="${watch_src_path}"' "{{.SAVESTATE_DIR}}"
vars:
@@ -184,59 +130,45 @@ tasks:
type-search:
desc: Just an example to show it running
cmds:
- '{{.TYPESEARCH_BIN_RELEASE_DIR}}/type_searcher --output-path ./search-results.json --game {{.GAME}} --fields ''[{"type":"int16","offset":2},{"type":"int16","offset":4}]'''
- "{{.TYPESEARCH_BIN_RELEASE_DIR}}/type_searcher --output-path ./search-results.json --game {{.GAME}} --fields '[{\"type\":\"int16\",\"offset\":2},{\"type\":\"int16\",\"offset\":4}]'"
update-treesitter:
desc: Updates locally built tree-sitter rules
cmds:
- cd ../tree-sitter-opengoal && yarn gen
- '{{.PYTHON}} ./scripts/tasks/cp.py --src "../tree-sitter-opengoal/src/*" --dest "./third-party/tree-sitter/tree-sitter-opengoal"'
- '{{.PYTHON}} ./scripts/tasks/cp.py --src "../tree-sitter-opengoal/grammar.js" --dest "./third-party/tree-sitter/tree-sitter-opengoal"'
- python ./scripts/tasks/cp.py --src "../tree-sitter-opengoal/src/*" --dest "./third-party/tree-sitter/tree-sitter-opengoal"
- python ./scripts/tasks/cp.py --src "../tree-sitter-opengoal/grammar.js" --dest "./third-party/tree-sitter/tree-sitter-opengoal"
fix-translations:
desc: Finds and tries to fix invalid translation Characters
cmds:
- "{{.PYTHON}} ./scripts/ci/lint-autoglottonyms.py --fix"
- "{{.PYTHON}} ./scripts/ci/lint-characters.py --fix"
lint:
cmds:
- "{{.PYTHON}} ./scripts/ci/lint-trailing-whitespace.py"
run-gpu-test:
desc: "Runs the game's built in GPU test"
preconditions:
- sh: test -f {{.GK_BIN_RELEASE_DIR}}/gk{{.EXE_FILE_EXTENSION}}
msg: "Couldn't locate runtime executable in '{{.GK_BIN_RELEASE_DIR}}/gk'"
cmds:
- "{{.GK_BIN_RELEASE_DIR}}/gk -v --gpu-test opengl --gpu-test-out-path ./gpu-test.json"
- python ./scripts/ci/lint-characters.py --fix
# TESTS
unit-tests:
cmds:
- "{{.GOALCTEST_BIN_DEBUG_DIR}}/goalc-test{{.EXE_FILE_EXTENSION}} {{.TEST_FILTER}}"
offline-tests: # ran by jenkins
cmds:
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test{{.EXE_FILE_EXTENSION}} --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --fail-on-cmp'
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --fail-on-cmp'
offline-test-file:
cmds:
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test{{.EXE_FILE_EXTENSION}} --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --file {{.FILE}}'
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --file {{.FILE}}'
offline-tests-fast:
cmds:
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test{{.EXE_FILE_EXTENSION}} --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --pretty-print --num_threads 8 --dump_current_output --fail-on-cmp'
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --pretty-print --num_threads 32 --dump_current_output --fail-on-cmp'
# TODO - amalgamate offline-tests and this task, run twice if the previous step fails
update-ref-tests:
cmds:
- cmd: "{{.PYTHON}} ./scripts/tasks/delete-file-or-folder.py --path failures"
- cmd: '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --pretty-print --num_threads 8 --dump_current_output --fail-on-cmp'
- cmd: python ./scripts/tasks/delete-file-or-folder.py --path failures
- cmd: '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --pretty-print --num_threads 32 --dump_current_output --fail-on-cmp'
ignore_error: true
- "{{.PYTHON}} ./scripts/update_decomp_reference.py ./failures ./test/decompiler/reference/ --game {{.GAME}}"
- python ./scripts/update_decomp_reference.py ./failures ./test/decompiler/reference/ --game {{.GAME}}
- task: offline-tests-fast
update-ref-file:
cmds:
- cmd: "{{.PYTHON}} ./scripts/tasks/delete-file-or-folder.py --path failures"
- cmd: python ./scripts/tasks/delete-file-or-folder.py --path failures
- cmd: '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --file {{.FILE}} --game {{.GAME}} --dump_current_output --fail-on-cmp'
ignore_error: true
- "{{.PYTHON}} ./scripts/update_decomp_reference.py ./failures ./test/decompiler/reference/ --game {{.GAME}}"
- python ./scripts/update_decomp_reference.py ./failures ./test/decompiler/reference/ --game {{.GAME}}
- task: offline-test-file
type-test-jak1:
cmds:
- cmd: '{{.GOALCTEST_BIN_RELEASE_DIR}}/goalc-test --gtest_brief=0 --gtest_filter="*Jak1TypeConsistency*" --gtest_break_on_failure'
type-test:
cmds:
- cmd: '{{.GOALCTEST_BIN_RELEASE_DIR}}/goalc-test --gtest_brief=0 --gtest_filter="*{{.TYPE_CONSISTENCY_TEST_FILTER}}*" --gtest_break_on_failure'
tests-filtered:
cmds:
- cmd: '{{.GOALCTEST_BIN_RELEASE_DIR}}/goalc-test --gtest_brief=0 --gtest_filter="*{{.FILTER}}*" --gtest_break_on_failure'
- cmd: '{{.GOALCTEST_BIN_RELEASE_DIR}}/goalc-test --gtest_brief=0 --gtest_filter="*Jak2TypeConsistency*" --gtest_break_on_failure'
+3 -17
View File
@@ -15,13 +15,6 @@ function(write_revision_h)
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
# To support being on a commit with mutiple tags
if(${GIT_TAG} MATCHES "\n")
string(REPLACE "\n" ";" GIT_TAG_LIST "${GIT_TAG}")
list(LENGTH GIT_TAG_LIST GIT_TAG_LIST_LENGTH)
math(EXPR GIT_TAG_LAST_INDEX "${GIT_TAG_LIST_LENGTH} - 1")
list(GET GIT_TAG_LIST ${GIT_TAG_LAST_INDEX} GIT_TAG)
endif()
endif()
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/versions/revision.h "#define BUILT_TAG \"${GIT_TAG}\"\n#define BUILT_SHA \"${GIT_SHORT_SHA}\"\n")
endfunction()
@@ -58,7 +51,7 @@ add_library(common
repl/config.cpp
repl/nrepl/ReplClient.cpp
repl/nrepl/ReplServer.cpp
repl/repl_wrapper.cpp
repl/util.cpp
serialization/subtitles/subtitles_v1.cpp
serialization/subtitles/subtitles_v2.cpp
serialization/subtitles/subtitles.cpp
@@ -73,7 +66,6 @@ add_library(common
type_system/TypeSpec.cpp
type_system/TypeSystem.cpp
util/Assert.cpp
util/ast_util.cpp
util/BitUtils.cpp
util/compress.cpp
util/crc32.cpp
@@ -83,11 +75,7 @@ add_library(common
util/dialogs.cpp
util/diff.cpp
util/FileUtil.cpp
util/font/dbs/font_db_jak1.cpp
util/font/dbs/font_db_jak2.cpp
util/font/dbs/font_db_jak3.cpp
util/font/font_utils_korean.cpp
util/font/font_utils.cpp
util/FontUtils.cpp
util/FrameLimiter.cpp
util/json_util.cpp
util/os.cpp
@@ -98,12 +86,10 @@ add_library(common
util/term_util.cpp
util/Timer.cpp
util/unicode_util.cpp
util/gltf_util.cpp
util/image_resize.cpp
versions/versions.cpp
)
target_link_libraries(common fmt lzokay replxx libzstd_static tree-sitter sqlite3 libtinyfiledialogs tiny_gltf)
target_link_libraries(common fmt lzokay replxx libzstd_static tree-sitter sqlite3 libtinyfiledialogs)
if(WIN32)
target_link_libraries(common wsock32 ws2_32 windowsapp)
+4 -22
View File
@@ -3,7 +3,7 @@
#include "common/log/log.h"
#include "common/util/BinaryWriter.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
/*!
* Write a wave file from a vector of samples.
@@ -56,15 +56,11 @@ void write_wave_file(const std::vector<s16>& left_samples,
writer.write_to_file(name);
}
std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
const bool stereo,
const u32 version) {
std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader, const bool mono) {
std::vector<s16> left_samples;
std::vector<s16> right_samples;
s32 left_sample_prev[2] = {0, 0};
s32 right_sample_prev[2] = {0, 0};
bool first_left = true;
bool first_right = true;
constexpr s32 f1[5] = {0, 60, 115, 98, 122};
constexpr s32 f2[5] = {0, 0, -52, -55, -60};
@@ -75,29 +71,15 @@ std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
// instead they are partitioned into contiguous 8kb (thats 8192 bytes) chunks
// alternating left/right
bool processing_left_chunk = true;
// We need to skip the vag header for each channel
if (first_left) {
reader.ffwd(48);
bytes_read += 48;
first_left = false;
}
while (true) {
if (!reader.bytes_left()) {
break;
}
if (stereo && bytes_read == (version == 3 ? 0x1000 : 0x2000)) {
if (bytes_read == 0x2000) {
// switch streams
processing_left_chunk = !processing_left_chunk;
bytes_read = 0;
// We need to skip the vag header for each channel
if (first_right) {
// skip the header
reader.ffwd(48);
bytes_read += 48;
first_right = false;
}
}
u8 shift_filter = reader.read<u8>();
@@ -125,7 +107,7 @@ std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
s32 sample = (s32)(s16)(nibble << 12);
sample >>= shift;
if (!stereo || processing_left_chunk) {
if (mono || processing_left_chunk) {
sample += (left_sample_prev[0] * f1[filter] + left_sample_prev[1] * f2[filter] + 32) / 64;
if (sample > 0x7fff) {
+1 -3
View File
@@ -34,8 +34,6 @@ void write_wave_file(const std::vector<s16>& left_samples,
s32 sample_rate,
const fs::path& name);
std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader,
const bool mono,
const u32 version);
std::pair<std::vector<s16>, std::vector<s16>> decode_adpcm(BinaryReader& reader, const bool mono);
std::vector<u8> encode_adpcm(const std::vector<s16>& samples);
+1 -1
View File
@@ -11,7 +11,7 @@
#include "common/goal_constants.h"
#include "common/util/Timer.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#ifdef __linux
#include <fcntl.h>
+1 -1
View File
@@ -19,7 +19,7 @@
#include <stdio.h>
#include <string.h>
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#include "common/log/log.h"
// clang-format on
+2 -2
View File
@@ -5,8 +5,6 @@
#include "common/cross_sockets/XSocket.h"
#include "common/log/log.h"
#include "fmt/format.h"
// clang-format off
#ifdef _WIN32
#define NOMINMAX
@@ -15,7 +13,9 @@
#include <WinSock2.h>
#include <WS2tcpip.h>
#endif
#include "common/repl/nrepl/ReplServer.h"
#include "third-party/fmt/core.h"
// clang-format on
XSocketClient::XSocketClient(int _tcp_port) {
+5 -13
View File
@@ -4,7 +4,7 @@
#include "common/cross_sockets/XSocket.h"
#include "common/common_types.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#ifdef _WIN32
#define NOMINMAX
@@ -35,7 +35,7 @@ void XSocketServer::shutdown_server() {
close_server_socket();
}
bool XSocketServer::init_server(bool failure_may_occur) {
bool XSocketServer::init_server() {
listening_socket = open_socket(AF_INET, SOCK_STREAM, 0);
if (listening_socket < 0) {
listening_socket = -1;
@@ -76,27 +76,19 @@ bool XSocketServer::init_server(bool failure_may_occur) {
addr.sin_port = htons(tcp_port);
if (bind(listening_socket, (sockaddr*)&addr, sizeof(addr)) < 0) {
if (failure_may_occur) {
lg::debug("[XSocketServer:{}] failed to bind", tcp_port);
} else {
lg::error("[XSocketServer:{}] failed to bind", tcp_port);
}
lg::error("[XSocketServer:{}] failed to bind", tcp_port);
close_server_socket();
return false;
}
if (listen(listening_socket, 0) < 0) {
if (failure_may_occur) {
lg::debug("[XSocketServer:{}] failed to listen", tcp_port);
} else {
lg::error("[XSocketServer:{}] failed to listen", tcp_port);
}
lg::error("[XSocketServer:{}] failed to listen", tcp_port);
close_server_socket();
return false;
}
server_initialized = true;
lg::debug("[XSocketServer:{}] initialized", tcp_port);
lg::info("[XSocketServer:{}] initialized", tcp_port);
post_init();
return true;
}
+1 -1
View File
@@ -20,7 +20,7 @@ class XSocketServer {
XSocketServer(const XSocketServer&) = delete;
XSocketServer& operator=(const XSocketServer&) = delete;
bool init_server(bool failure_may_occur = false);
bool init_server();
void shutdown_server();
void close_server_socket();
+29 -91
View File
@@ -3,17 +3,16 @@
#include <algorithm>
#include <functional>
#include "common/log/log.h"
#ifndef __aarch64__
#include "xmmintrin.h"
#else
#include "third-party/sse2neon/sse2neon.h"
#endif
#include "common/util/Assert.h"
#include "common/util/simd_util.h"
namespace tfrag3 {
void PackedTimeOfDay::serialize(Serializer& ser) {
ser.from_pod_vector(&data);
ser.from_ptr(&color_count);
}
void PackedTieVertices::serialize(Serializer& ser) {
ser.from_pod_vector(&color_indices);
ser.from_pod_vector(&matrices);
@@ -43,7 +42,6 @@ void ShrubDraw::serialize(Serializer& ser) {
ser.from_ptr(&num_triangles);
ser.from_ptr(&first_index_index);
ser.from_ptr(&num_indices);
ser.from_ptr(&proto_idx);
}
void InstancedStripDraw::serialize(Serializer& ser) {
@@ -72,9 +70,10 @@ void TfragTree::serialize(Serializer& ser) {
draw.serialize(ser);
}
// ser.from_pod_vector(&vertices);
ser.from_pod_vector(&packed_vertices.vertices);
ser.from_pod_vector(&packed_vertices.cluster_origins);
colors.serialize(ser);
ser.from_pod_vector(&colors);
bvh.serialize(ser);
ser.from_ptr(&use_strips);
}
@@ -88,8 +87,10 @@ math::Vector3f vopmsub(math::Vector3f acc, math::Vector3f a, math::Vector3f b) {
}
/*!
* Compute the normal transformation for a TIE from the TIE matrix. This will return properly scaled
* normals.
* Compute the normal transformation for a TIE from the TIE matrix.
* Note that this isn't identical to the original game - we're missing the vf14 scaling factor
* For now, I just set this to 1, then normalize in the shader. Though I think we could avoid
* this by figuring out the value of vf14 here (I am just too lazy right now).
*/
std::array<math::Vector3f, 3> tie_normal_transform_v2(const std::array<math::Vector4f, 4>& m) {
// let:
@@ -179,21 +180,11 @@ std::array<math::Vector3f, 3> tie_normal_transform_v2(const std::array<math::Vec
// sqc2 vf12, -80(t8)
}
s16 saturate_for_s10(s16 s10) {
// our error should be 1 or less as an s8, or 4 as a s10.
ASSERT(s10 >= -520 && s10 < 520);
if (s10 < -512) {
return -512;
}
if (s10 > 511) {
return 511;
}
return s10;
}
u32 pack_to_gl_normal(s16 nx, s16 ny, s16 nz) {
return (saturate_for_s10(nx) & 0x3ff) | ((saturate_for_s10(ny) & 0x3ff) << 10) |
((saturate_for_s10(nz) & 0x3ff) << 20);
ASSERT(nx >= -512 && nx <= 511);
ASSERT(ny >= -512 && ny <= 511);
ASSERT(nz >= -512 && nz <= 511);
return (nx & 0x3ff) | ((ny & 0x3ff) << 10) | ((nz & 0x3ff) << 20);
}
/*!
@@ -205,10 +196,10 @@ u32 unpack_tie_normal(const std::array<math::Vector3f, 3>& mat, s8 nx, s8 ny, s8
nrm += mat[0] * nx;
nrm += mat[1] * ny;
nrm += mat[2] * nz;
// game used signed 8-bit normals, but OpenGL uses signed 10-bit
// multiply by 2^2 = 4
nrm *= 4;
// convert to s16 for OpenGL renderer
// nrm /= 0x100; // number from EE asm
// nrm *= 0x200; // for normalized s10 -> float conversion by OpenGL.
nrm *= 2; // for normalized s10 -> float conversion by OpenGL.
auto as_int = nrm.cast<s16>();
@@ -307,15 +298,13 @@ void TieTree::unpack() {
for (auto& draw : static_draws) {
draw.unpacked.idx_of_first_idx_in_full_buffer = unpacked.indices.size();
// indices can come from either runs or already in plain indices.
ASSERT(draw.plain_indices.empty());
for (auto& run : draw.runs) {
for (u32 ri = 0; ri < run.length; ri++) {
unpacked.indices.push_back(run.vertex0 + ri);
}
unpacked.indices.push_back(UINT32_MAX);
}
unpacked.indices.insert(unpacked.indices.end(), draw.plain_indices.begin(),
draw.plain_indices.end());
}
}
@@ -408,17 +397,15 @@ void TieTree::serialize(Serializer& ser) {
}
packed_vertices.serialize(ser);
colors.serialize(ser);
ser.from_pod_vector(&colors);
bvh.serialize(ser);
ser.from_ptr(&use_strips);
ser.from_ptr(&has_per_proto_visibility_toggle);
ser.from_string_vector(&proto_names);
}
void ShrubTree::serialize(Serializer& ser) {
time_of_day_colors.serialize(ser);
ser.from_pod_vector(&time_of_day_colors);
ser.from_pod_vector(&indices);
packed_vertices.serialize(ser);
if (ser.is_saving()) {
@@ -429,33 +416,6 @@ void ShrubTree::serialize(Serializer& ser) {
for (auto& draw : static_draws) {
draw.serialize(ser);
}
ser.from_ptr(&has_per_proto_visibility_toggle);
ser.from_string_vector(&proto_names);
}
void HfragmentBucket::serialize(Serializer& ser) {
ser.from_pod_vector(&corners);
ser.from_ptr(&montage_table);
}
void Hfragment::serialize(Serializer& ser) {
ser.from_pod_vector(&vertices);
ser.from_pod_vector(&indices);
ser.from_pod_vector(&corners);
if (ser.is_saving()) {
ser.save<size_t>(buckets.size());
} else {
buckets.resize(ser.load<size_t>());
}
for (auto& x : buckets) {
x.serialize(ser);
}
time_of_day_colors.serialize(ser);
ser.from_ptr(&wang_tree_tex_id);
ser.from_ptr(&draw_mode);
ser.from_ptr(&occlusion_offset);
}
void BVH::serialize(Serializer& ser) {
@@ -499,7 +459,6 @@ void MercDraw::serialize(Serializer& ser) {
ser.from_ptr(&first_index);
ser.from_ptr(&index_count);
ser.from_ptr(&num_triangles);
ser.from_ptr(&no_strip);
}
void Blerc::serialize(Serializer& ser) {
@@ -641,8 +600,6 @@ void Level::serialize(Serializer& ser) {
tree.serialize(ser);
}
hfrag.serialize(ser);
collision.serialize(ser);
merc_data.serialize(ser);
@@ -693,7 +650,8 @@ void PackedShrubVertices::memory_usage(MemoryUsageTracker* tracker) const {
}
void ShrubTree::memory_usage(MemoryUsageTracker* tracker) const {
tracker->add(MemoryUsageCategory::SHRUB_TIME_OF_DAY, sizeof(u8) * time_of_day_colors.data.size());
tracker->add(MemoryUsageCategory::SHRUB_TIME_OF_DAY,
sizeof(TimeOfDayColor) * time_of_day_colors.size());
packed_vertices.memory_usage(tracker);
tracker->add(MemoryUsageCategory::SHRUB_DRAW, sizeof(ShrubDraw) * static_draws.size());
tracker->add(MemoryUsageCategory::SHRUB_IND, sizeof(u32) * indices.size());
@@ -721,7 +679,7 @@ void TieTree::memory_usage(MemoryUsageTracker* tracker) const {
draw.vis_groups.size() * sizeof(StripDraw::VisGroup));
}
packed_vertices.memory_usage(tracker);
tracker->add(MemoryUsageCategory::TIE_TIME_OF_DAY, sizeof(u8) * colors.data.size());
tracker->add(MemoryUsageCategory::TIE_TIME_OF_DAY, sizeof(TimeOfDayColor) * colors.size());
for (auto& draw : instanced_wind_draws) {
draw.memory_usage(tracker);
@@ -745,7 +703,7 @@ void TfragTree::memory_usage(MemoryUsageTracker* tracker) const {
draw.vis_groups.size() * sizeof(StripDraw::VisGroup));
}
packed_vertices.memory_usage(tracker);
tracker->add(MemoryUsageCategory::TFRAG_TIME_OF_DAY, sizeof(u8) * colors.data.size());
tracker->add(MemoryUsageCategory::TFRAG_TIME_OF_DAY, sizeof(TimeOfDayColor) * colors.size());
tracker->add(MemoryUsageCategory::TFRAG_BVH, sizeof(VisNode) * bvh.vis_nodes.size());
}
@@ -758,13 +716,6 @@ void IndexTexture::memory_usage(MemoryUsageTracker* tracker) const {
tracker->add(MemoryUsageCategory::SPECIAL_TEXTURE, 256 * 4); // clut
}
void Hfragment::memory_usage(tfrag3::MemoryUsageTracker* tracker) const {
tracker->add(MemoryUsageCategory::HFRAG_VERTS, vertices.size() * sizeof(HfragmentVertex));
tracker->add(MemoryUsageCategory::HFRAG_INDEX, indices.size() * sizeof(u32));
tracker->add(MemoryUsageCategory::HFRAG_TIME_OF_DAY, time_of_day_colors.data.size() * sizeof(u8));
tracker->add(MemoryUsageCategory::HFRAG_CORNERS, corners.size() * sizeof(HfragmentCorner));
}
void Level::memory_usage(MemoryUsageTracker* tracker) const {
for (const auto& texture : textures) {
texture.memory_usage(tracker);
@@ -785,7 +736,6 @@ void Level::memory_usage(MemoryUsageTracker* tracker) const {
for (const auto& tree : shrub_trees) {
tree.memory_usage(tracker);
}
hfrag.memory_usage(tracker);
collision.memory_usage(tracker);
merc_data.memory_usage(tracker);
}
@@ -829,11 +779,6 @@ void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size) {
{"merc-mod-draw-1", mem_use.data[tfrag3::MemoryUsageCategory::MERC_MOD_DRAW_1]},
{"merc-mod-draw-2", mem_use.data[tfrag3::MemoryUsageCategory::MERC_MOD_DRAW_2]},
{"blerc", mem_use.data[tfrag3::MemoryUsageCategory::BLERC]},
{"hfrag-verts", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_VERTS]},
{"hfrag-index", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_INDEX]},
{"hfrag-time-of-day", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_TIME_OF_DAY]},
{"hfrag-corners", mem_use.data[tfrag3::MemoryUsageCategory::HFRAG_CORNERS]}
};
for (auto& known : known_categories) {
total_accounted += known.second;
@@ -846,8 +791,8 @@ void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size) {
for (const auto& x : known_categories) {
if (x.second) {
lg::print("{:30s} : {:6d} kB {:3.1f}%\n", x.first, x.second / 1024,
100.f * (float)x.second / uncompressed_data_size);
fmt::print("{:30s} : {:6d} kB {:3.1f}%\n", x.first, x.second / 1024,
100.f * (float)x.second / uncompressed_data_size);
}
}
}
@@ -857,11 +802,4 @@ std::size_t PreloadedVertex::hash::operator()(const PreloadedVertex& v) const {
std::hash<float>()(v.s) ^ std::hash<float>()(v.t) ^ std::hash<u16>()(v.color_index);
}
std::size_t PackedTieVertices::Vertex::hash::operator()(const Vertex& v) const {
return std::hash<float>()(v.x) ^ std::hash<float>()(v.y) ^ std::hash<float>()(v.z) ^
std::hash<float>()(v.r) ^ std::hash<float>()(v.g) ^ std::hash<float>()(v.b) ^
std::hash<float>()(v.a) ^ std::hash<float>()(v.s) ^ std::hash<float>()(v.t) ^
std::hash<float>()(v.nx) ^ std::hash<float>()(v.ny) ^ std::hash<float>()(v.nz);
}
} // namespace tfrag3
+17 -85
View File
@@ -18,7 +18,7 @@ namespace tfrag3 {
// - if changing any large things (vertices, vis, bvh, colors, textures) update get_memory_usage
// - if adding a new category to the memory usage, update extract_level to print it.
constexpr int TFRAG3_VERSION = 43;
constexpr int TFRAG3_VERSION = 38;
enum MemoryUsageCategory {
TEXTURE,
@@ -61,11 +61,6 @@ enum MemoryUsageCategory {
MERC_MOD_TABLE,
BLERC,
HFRAG_VERTS,
HFRAG_INDEX,
HFRAG_TIME_OF_DAY,
HFRAG_CORNERS,
COLLISION,
NUM_CATEGORIES
@@ -116,16 +111,6 @@ struct PackedTieVertices {
float s, t;
s8 nx, ny, nz;
u8 r, g, b, a;
struct hash {
std::size_t operator()(const Vertex& x) const;
};
bool operator==(const Vertex& other) const {
return x == other.x && y == other.y && z == other.z && s == other.s && t == other.t &&
nx == other.nx && ny == other.ny && nz == other.nz && r == other.r && g == other.g &&
b == other.b && a == other.a;
}
};
struct MatrixGroup {
@@ -234,14 +219,12 @@ struct ShrubDraw {
// for debug counting.
u32 num_triangles = 0;
u16 proto_idx = 0;
void serialize(Serializer& ser);
};
struct InstancedStripDraw {
DrawMode mode; // the OpenGL draw settings.
s32 tree_tex_id = 0; // the texture that should be bound for the draw
u32 tree_tex_id = 0; // the texture that should be bound for the draw
// the list of vertices in the draw. This includes the restart code of UINT32_MAX that OpenGL
// will use to start a new strip.
@@ -284,23 +267,18 @@ struct BVH {
void serialize(Serializer& ser);
};
// This is split into groups of 4 colors.
// The data in these groups is stored first by palette, then color, then channel.
struct PackedTimeOfDay {
std::vector<u8> data;
u32 color_count = 0;
void serialize(Serializer& ser);
// A time-of-day color. Each stores 8 colors. At a given "time of day", they are interpolated
// to find a single color which goes into a color palette.
struct TimeOfDayColor {
math::Vector<u8, 4> rgba[8];
u8 read(int color, int palette, int channel) const {
const int color_quad = color / 4;
const int color_in_quad = color % 4;
return data[color_quad * 4 * 4 * 8 + palette * 4 * 4 + color_in_quad * 4 + channel];
}
u8& read(int color, int palette, int channel) {
const int color_quad = color / 4;
const int color_in_quad = color % 4;
return data[color_quad * 4 * 4 * 8 + palette * 4 * 4 + color_in_quad * 4 + channel];
bool operator==(const TimeOfDayColor& other) const {
for (size_t i = 0; i < 8; i++) {
if (rgba[i] != other.rgba[i]) {
return false;
}
}
return true;
}
};
@@ -339,8 +317,8 @@ struct TfragTree {
TFragmentTreeKind kind; // our tfrag kind
std::vector<StripDraw> draws; // the actual topology and settings
PackedTfragVertices packed_vertices;
PackedTimeOfDay colors; // vertex colors (pre-interpolation)
BVH bvh; // the bvh for frustum culling
std::vector<TimeOfDayColor> colors; // vertex colors (pre-interpolation)
BVH bvh; // the bvh for frustum culling
bool use_strips = true;
struct {
@@ -421,7 +399,7 @@ struct TieTree {
std::array<u32, kNumTieCategories + 1> category_draw_indices;
PackedTieVertices packed_vertices;
PackedTimeOfDay colors; // vertex colors (pre-interpolation)
std::vector<TimeOfDayColor> colors; // vertex colors (pre-interpolation)
std::vector<InstancedStripDraw> instanced_wind_draws;
std::vector<TieWindInstance> wind_instance_info;
@@ -430,8 +408,6 @@ struct TieTree {
bool has_per_proto_visibility_toggle = false;
std::vector<std::string> proto_names;
bool use_strips = true;
struct {
std::vector<PreloadedVertex> vertices; // mesh vertices
std::vector<u32> indices;
@@ -444,7 +420,7 @@ struct TieTree {
struct ShrubTree {
// todo some visibility structure
PackedTimeOfDay time_of_day_colors; // multiplier colors
std::vector<TimeOfDayColor> time_of_day_colors; // multiplier colors
PackedShrubVertices packed_vertices;
std::vector<ShrubDraw> static_draws; // the actual topology and settings
@@ -454,52 +430,11 @@ struct ShrubTree {
std::vector<ShrubGpuVertex> vertices; // mesh vertices
} unpacked;
// jak 2 and later can toggle on and off visibility per proto by name
bool has_per_proto_visibility_toggle = false;
std::vector<std::string> proto_names;
void serialize(Serializer& ser);
void memory_usage(MemoryUsageTracker* tracker) const;
void unpack();
};
struct HfragmentVertex {
float height = 0;
u32 vi = 0;
u16 color_index = 0;
u8 u = 0, v = 0;
u32 pad = 0;
};
struct HfragmentCorner {
math::Vector<float, 4> bsphere;
u32 vis_id = 0;
u32 index_start = 0;
u32 index_length = 0;
u32 num_tris = 0;
};
struct HfragmentBucket {
std::vector<u32> corners;
std::array<u16, 16> montage_table;
void serialize(Serializer& ser);
};
struct Hfragment {
std::vector<HfragmentVertex> vertices;
std::vector<u32> indices;
std::vector<HfragmentCorner> corners;
std::vector<HfragmentBucket> buckets;
PackedTimeOfDay time_of_day_colors;
std::array<s32, 4> wang_tree_tex_id;
DrawMode draw_mode;
u32 occlusion_offset;
void serialize(Serializer& ser);
void memory_usage(MemoryUsageTracker* tracker) const;
};
struct CollisionMesh {
struct Vertex {
float x, y, z;
@@ -542,8 +477,6 @@ struct MercDraw {
u32 first_index;
u32 index_count;
u32 num_triangles;
// no strip hack for custom models
bool no_strip = false;
void serialize(Serializer& ser);
};
@@ -627,7 +560,6 @@ struct Level {
std::array<std::vector<TfragTree>, TFRAG_GEOS> tfrag_trees;
std::array<std::vector<TieTree>, TIE_GEOS> tie_trees;
std::vector<ShrubTree> shrub_trees;
Hfragment hfrag;
CollisionMesh collision;
MercModelGroup merc_data;
u16 version2 = TFRAG3_VERSION;
+3 -3
View File
@@ -2,9 +2,9 @@
#include "common/util/Assert.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
std::string DmaTag::print() const {
std::string DmaTag::print() {
std::string result;
const char* mode_names[8] = {"refe", "cnt", "next", "ref", "refs", "call", "ret", "end"};
result += fmt::format("TAG: 0x{:08x} {:4s} qwc 0x{:04x}", addr, mode_names[(int)kind], qwc);
@@ -15,7 +15,7 @@ std::string DmaTag::print() const {
return result;
}
std::string VifCode::print() const {
std::string VifCode::print() {
std::string result;
switch (kind) {
+4 -4
View File
@@ -11,7 +11,7 @@
#include "common/common_types.h"
#include "common/util/Assert.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
struct DmaStats {
double sync_time_ms = 0;
@@ -37,7 +37,7 @@ struct DmaTag {
DmaTag(u64 value) {
spr = (value >> 63);
addr = (value >> 32) & 0x7fffffff;
qwc = value & 0xffff;
qwc = value & 0xfff;
kind = Kind((value >> 28) & 0b111);
}
@@ -52,7 +52,7 @@ struct DmaTag {
bool operator!=(const DmaTag& other) const { return !((*this) == other); }
std::string print() const;
std::string print();
};
inline void emulate_dma(const void* source_base, void* dest_base, u32 tadr, u32 dadr) {
@@ -148,7 +148,7 @@ struct VifCode {
u16 num;
u16 immediate;
std::string print() const;
std::string print();
};
struct VifCodeStcycl {
+1 -1
View File
@@ -6,7 +6,7 @@
#include "common/log/log.h"
#include "common/util/Timer.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
/*!
* Convert a DMA chain to an array of bytes that can be directly fed to VIF.
+2 -1
View File
@@ -2,7 +2,8 @@
#include "common/util/Assert.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#include "third-party/fmt/format.h"
std::string reg_descriptor_name(GifTag::RegisterDescriptor reg) {
switch (reg) {
+1
View File
@@ -12,6 +12,7 @@ Also in general I found it _really_ hard to find modern and easy to understand a
Atleast for me, it helps to understand the path code takes as it flows through the formatting process. Originally I tried to do as much at once for sake of efficiency but this just makes things incredibly hard to reason about and fix bugs. So like any problem that is complex, break it down, the formatter goes through many distinct phases chipping away at the problem:
```mermaid
%%{init: {'theme': 'dark', "flowchart" : { "curve" : "basis" } } }%%
flowchart TB
subgraph top1 [Build a Formatting Tree]
direction TB
+48 -196
View File
@@ -2,16 +2,13 @@
#include "formatter_tree.h"
#include "common/formatter/rules/formatting_rules.h"
#include "common/formatter/rules/rule_config.h"
#include "common/log/log.h"
#include "common/util/FileUtil.h"
#include "common/util/string_util.h"
#include "tree_sitter/api.h"
#include "fmt/format.h"
#include "fmt/ranges.h"
#include "third-party/fmt/core.h"
// Declare the `tree_sitter_opengoal` function, which is
// implemented by the `tree-sitter-opengoal` library.
@@ -33,27 +30,11 @@ int hang_indentation_width(const FormatterTreeNode& curr_node) {
return 1 + hang_indentation_width(first_elt);
}
int get_total_form_inlined_width(const FormatterTreeNode& curr_node) {
if (curr_node.token) {
return curr_node.token->length();
}
int width = 1;
for (int i = 0; i < curr_node.refs.size(); i++) {
width += get_total_form_inlined_width(curr_node.refs.at(i));
if (i != curr_node.refs.size() - 1) {
width += 1; // add the space between elements
}
}
return width + 1;
}
// TODO - compute length of each node and store it AOT
// TODO - compute length of each node and store it
void apply_formatting_config(
FormatterTreeNode& curr_node,
std::optional<std::shared_ptr<formatter_rules::config::FormFormattingConfig>>
config_from_parent = {},
std::optional<std::shared_ptr<formatter_rules::config::FormFormattingConfig>>
config_merge_from_parent = {}) {
config_from_parent = {}) {
using namespace formatter_rules;
// node is empty, base-case
if (curr_node.token || curr_node.refs.empty()) {
@@ -69,15 +50,7 @@ void apply_formatting_config(
predefined_config = config::opengoal_form_config.at(form_head.value());
curr_node.formatting_config = predefined_config.value();
}
if (config_merge_from_parent) {
const auto& merge_config = *config_merge_from_parent.value();
curr_node.formatting_config.parent_mutable_extra_indent +=
merge_config.parent_mutable_extra_indent;
curr_node.formatting_config.prevent_inlining = merge_config.prevent_inlining;
}
} else if (config_from_parent) {
// TODO - doesn't merge just replaces, a bit inflexible
predefined_config = *config_from_parent.value();
curr_node.formatting_config = predefined_config.value();
}
@@ -94,10 +67,10 @@ void apply_formatting_config(
//
// Otherwise, we always default to a hang.
//
// NOTE - any modifications here to child elements could be superseeded later in the recursion!
// In order to maintain your sanity, only modify things here that _arent_ touched by default
// NOTE - any modifications here to child elements could be superseeded later in the recursion
// in order to maintain your sanity, only modify things here that _arent_ touched by default
// configurations. These are explicitly prepended with `parent_mutable_`
if (!predefined_config && !curr_node.formatting_config.config_set) {
if (!predefined_config) {
if (curr_node.metadata.is_top_level) {
curr_node.formatting_config.indentation_width = 0;
curr_node.formatting_config.hang_forms = false;
@@ -115,7 +88,7 @@ void apply_formatting_config(
if (curr_node.formatting_config.has_constant_pairs) {
for (int i = 0; i < (int)curr_node.refs.size(); i++) {
auto& child_ref = curr_node.refs.at(i);
const auto& type = child_ref.metadata.node_type;
const auto type = child_ref.metadata.node_type;
if (constant_types.find(type) == constant_types.end() &&
constant_pairs::is_element_second_in_constant_pair(curr_node, child_ref, i)) {
child_ref.formatting_config.parent_mutable_extra_indent = 2;
@@ -131,8 +104,6 @@ void apply_formatting_config(
}
// If we are hanging, lets determine the indentation width since it is based on the form itself
if (curr_node.formatting_config.hang_forms) {
// TODO - this isn't being calculated for a pre-defined config
// TODO - another idea is to do this during consolidation
curr_node.formatting_config.indentation_width = hang_indentation_width(curr_node);
}
// iterate through the refs
@@ -140,67 +111,27 @@ void apply_formatting_config(
auto& ref = curr_node.refs.at(i);
if (!ref.token) {
// If the child has a pre-defined configuration at that index, we pass it along
if (predefined_config && predefined_config->index_config_override.find(i) !=
predefined_config->index_config_override.end()) {
apply_formatting_config(ref, {}, predefined_config->index_config_override.at(i));
} else if (predefined_config && predefined_config->index_configs.find(i) !=
predefined_config->index_configs.end()) {
apply_formatting_config(ref, predefined_config->index_configs.at(i), {});
if (predefined_config &&
predefined_config->index_configs.find(i) != predefined_config->index_configs.end()) {
apply_formatting_config(ref, predefined_config->index_configs.at(i));
} else if (predefined_config && predefined_config->default_index_config) {
apply_formatting_config(ref, predefined_config->default_index_config, {});
apply_formatting_config(ref, predefined_config->default_index_config);
} else {
apply_formatting_config(ref, {}, {});
apply_formatting_config(ref);
}
}
}
// Precompute the column widths for things like deftype fields
if (curr_node.formatting_config.determine_column_widths_for_list_elements) {
// iterate through each ref and find the max length of each index (may be a token, may not be!)
// then store that info in each list element's `list_element_column_widths` to be used when
// printing out the tokens (pad width)
// Find the maximum number of columns
int max_columns = 0;
for (const auto& field : curr_node.refs) {
if ((int)field.refs.size() > max_columns) {
max_columns = field.refs.size();
}
}
// if only one field has a value in the max col position, it looks weird for it to be indented
bool ignore_final_column_width = true;
int fields_with_atleast_max_col = 0;
for (const auto& field : curr_node.refs) {
if ((int)field.refs.size() == max_columns) {
fields_with_atleast_max_col++;
if (fields_with_atleast_max_col > 1) {
ignore_final_column_width = false;
break;
}
}
}
// Now find the column max widths
std::vector<int> column_max_widths = {};
for (int col = 0; col < max_columns; col++) {
column_max_widths.push_back(0);
// -2 because its the indentation before the final column that we want to skip
if (col >= curr_node.formatting_config.num_columns_to_compute_widths ||
(ignore_final_column_width && col == max_columns - 2)) {
continue;
}
for (const auto& field : curr_node.refs) {
if ((int)field.refs.size() > col) {
const auto width = get_total_form_inlined_width(field.refs.at(col));
if (width > column_max_widths.at(col)) {
column_max_widths[col] = width;
}
}
}
}
// Apply column info to every list
for (auto& field : curr_node.refs) {
field.formatting_config.list_element_column_widths = column_max_widths;
field.formatting_config.config_set = true;
}
}
int get_total_form_inlined_width(const FormatterTreeNode& curr_node) {
if (curr_node.token) {
return curr_node.token->length();
}
int width = 1;
for (const auto& ref : curr_node.refs) {
width += get_total_form_inlined_width(ref);
}
return width + 1;
}
bool form_contains_comment(const FormatterTreeNode& curr_node) {
@@ -232,13 +163,6 @@ bool form_contains_node_that_prevents_inlining(const FormatterTreeNode& curr_nod
bool can_node_be_inlined(const FormatterTreeNode& curr_node, int cursor_pos) {
using namespace formatter_rules;
if (curr_node.formatting_config.force_inline) {
// Ensure there are no comments, this still trumps this
if (form_contains_comment(curr_node)) {
return false;
}
return true;
}
// First off, we cannot inline the top level
if (curr_node.metadata.is_top_level) {
return false;
@@ -267,20 +191,17 @@ bool can_node_be_inlined(const FormatterTreeNode& curr_node, int cursor_pos) {
}
std::vector<std::string> apply_formatting(const FormatterTreeNode& curr_node,
std::vector<std::string> /*output*/ = {},
std::vector<std::string> output = {},
int cursor_pos = 0) {
using namespace formatter_rules;
if (!curr_node.token && curr_node.refs.empty()) {
// special case to handle an empty list
if (curr_node.node_prefix) {
return {fmt::format("{}()", curr_node.node_prefix.value())};
}
return {"()"};
}
// If its a token, just print the token and move on
if (curr_node.token) {
return {curr_node.token_str()};
return {curr_node.token.value()};
}
bool inline_form = can_node_be_inlined(curr_node, cursor_pos);
@@ -299,21 +220,13 @@ std::vector<std::string> apply_formatting(const FormatterTreeNode& curr_node,
// Add new line entry
if (ref.token) {
// Cleanup block-comments
std::string val = ref.token.value();
if (ref.metadata.node_type == "block_comment") {
const auto comment_lines = comments::format_block_comment(ref.token_str());
for (const auto& line : comment_lines) {
form_lines.push_back(line);
}
} else {
form_lines.push_back(ref.token_str());
}
if (!curr_node.metadata.is_top_level && i == (int)curr_node.refs.size() - 1 &&
(ref.metadata.is_comment)) {
// if there's an inline comment at the end of a form, we have to force the paren to the next
// line and do a new-line paren this is ugly, but we have no choice!
form_lines.push_back("");
// TODO - change this sanitization to return a list of lines instead of a single new-lined
// line
val = comments::format_block_comment(ref.token.value());
}
form_lines.push_back(val);
} else {
// If it's not a token, we have to recursively build up the form
// TODO - add the cursor_pos here
@@ -329,45 +242,23 @@ std::vector<std::string> apply_formatting(const FormatterTreeNode& curr_node,
if (i == (int)curr_node.refs.size() - 1 && form_lines.size() > 1 &&
(curr_node.formatting_config.hang_forms ||
curr_node.formatting_config.combine_first_two_lines)) {
form_lines.at(0) += fmt::format(" {}", str_util::ltrim(form_lines.at(1)));
form_lines.at(0) += fmt::format(" {}", form_lines.at(1));
form_lines.erase(form_lines.begin() + 1);
} else if ((i + 1) < (int)curr_node.refs.size()) {
const auto& next_ref = curr_node.refs.at(i + 1);
// combine the next inline comment or constant pair
if ((next_ref.metadata.node_type == "comment" && next_ref.metadata.is_inline) ||
(curr_node.formatting_config.has_constant_pairs &&
constant_pairs::is_element_second_in_constant_pair(curr_node, next_ref, i + 1)) ||
constant_pairs::is_element_second_in_constant_pair_new(curr_node.refs.at(i), next_ref)) {
// TODO
// has issues with not consolidating first lines, this should probably just be moved to
// outside this loop for simplicity, do it later
constant_pairs::is_element_second_in_constant_pair(curr_node, next_ref, i + 1))) {
if (next_ref.token) {
form_lines.at(form_lines.size() - 1) += fmt::format(" {}", next_ref.token_str());
form_lines.at(form_lines.size() - 1) += fmt::format(" {}", next_ref.token.value());
i++;
// We have to handle hang-consolidation here or else it will never be reached above!
if (i == (int)curr_node.refs.size() - 1 && form_lines.size() > 1 &&
(curr_node.formatting_config.hang_forms ||
curr_node.formatting_config.combine_first_two_lines)) {
form_lines.at(0) += fmt::format(" {}", form_lines.at(1));
form_lines.erase(form_lines.begin() + 1);
}
} else if (can_node_be_inlined(next_ref, cursor_pos)) {
const auto& lines = apply_formatting(next_ref, {}, cursor_pos); // TODO - cursor pos
for (const auto& line : lines) {
form_lines.at(form_lines.size() - 1) += fmt::format(" {}", line);
}
i++;
// We have to handle hang-consolidation here or else it will never be reached above!
if (i == (int)curr_node.refs.size() - 1 && form_lines.size() > 1 &&
(curr_node.formatting_config.hang_forms ||
curr_node.formatting_config.combine_first_two_lines)) {
form_lines.at(0) += fmt::format(" {}", form_lines.at(1));
form_lines.erase(form_lines.begin() + 1);
}
}
if (!curr_node.metadata.is_top_level && next_ref.metadata.node_type == "comment" &&
(i + 1) == (int)curr_node.refs.size()) {
form_lines.push_back("");
}
}
}
@@ -378,14 +269,8 @@ std::vector<std::string> apply_formatting(const FormatterTreeNode& curr_node,
}
// Consolidate any lines if the configuration requires it
// TODO there is a hack here so that multi-line forms that are consolidated still line up properly
// i have to make consolidate a more first-class feature of the config
// TODO - hacky, but prevents a bad situation, clean up
if (curr_node.formatting_config.inline_until_index(form_lines) &&
!str_util::contains(form_lines.at(0), ";")) {
if (curr_node.formatting_config.inline_until_index(form_lines)) {
std::vector<std::string> new_form_lines = {};
const auto original_form_head_width = str_util::split(form_lines.at(0), '\n').at(0).length();
bool consolidating_lines = true;
for (int i = 0; i < (int)form_lines.size(); i++) {
if (i < curr_node.formatting_config.inline_until_index(form_lines)) {
if (new_form_lines.empty()) {
@@ -394,79 +279,51 @@ std::vector<std::string> apply_formatting(const FormatterTreeNode& curr_node,
new_form_lines.at(0) += fmt::format(" {}", form_lines.at(i));
}
} else {
if (str_util::starts_with(form_lines.at(i), " ") && consolidating_lines) {
new_form_lines.push_back(fmt::format(
"{}{}", str_util::repeat(original_form_head_width, " "), form_lines.at(i)));
} else {
consolidating_lines = false;
new_form_lines.push_back(form_lines.at(i));
}
new_form_lines.push_back(form_lines.at(i));
}
}
form_lines = new_form_lines;
}
// Add any column padding
if (!curr_node.formatting_config.list_element_column_widths.empty()) {
for (int i = 0; i < (int)form_lines.size(); i++) {
const auto& token = form_lines.at(i);
if (i < (int)form_lines.size() - 1) {
form_lines[i] = str_util::pad_right(
token, curr_node.formatting_config.list_element_column_widths.at(i), ' ');
}
}
}
// Apply necessary indentation to each line and add parens
if (!curr_node.metadata.is_top_level) {
std::string form_surround_start = "(";
if (curr_node.node_prefix) {
form_surround_start = fmt::format("{}(", curr_node.node_prefix.value());
}
std::string form_surround_end = ")";
form_lines[0] = fmt::format("{}{}", form_surround_start, form_lines[0]);
form_lines[form_lines.size() - 1] =
fmt::format("{}{}", form_lines[form_lines.size() - 1], form_surround_end);
}
std::string curr_form = "";
if (curr_node.formatting_config.parent_mutable_extra_indent > 0) {
curr_form += str_util::repeat(curr_node.formatting_config.parent_mutable_extra_indent, " ");
}
if (inline_form) {
// NOTE - not sure about this, if we are inlining a form, it always makes sense to eliminate
// trailing whitespace the only issue i can foresee is related to strings that span multiple
// lines.
std::vector<std::string> new_form_lines = {};
for (const auto& form_line : form_lines) {
new_form_lines.push_back(str_util::ltrim(form_line));
}
form_lines = {fmt::format("{}", fmt::join(new_form_lines, " "))};
form_lines = {fmt::format("{}", fmt::join(form_lines, " "))};
} else {
bool currently_in_block_comment = false;
for (int i = 0; i < (int)form_lines.size(); i++) {
auto& line = form_lines.at(i);
if (str_util::contains(line, "|#")) {
currently_in_block_comment = false;
}
if (i > 0 && !currently_in_block_comment) {
if (i > 0) {
auto& line = form_lines.at(i);
line = fmt::format("{}{}",
str_util::repeat(curr_node.formatting_config.indentation_width_for_index(
curr_node.formatting_config, i),
" "),
line);
}
if (str_util::contains(line, "#|") && !str_util::contains(line, "|#")) {
currently_in_block_comment = true;
}
}
}
return form_lines;
}
std::string join_formatted_lines(const std::vector<std::string>& lines,
const std::string& line_ending) {
return fmt::format("{}", fmt::join(lines, line_ending));
std::string join_formatted_lines(const std::vector<std::string> lines) {
// TODO - respect original file line endings
return fmt::format("{}", fmt::join(lines, "\n"));
}
std::optional<std::string> formatter::format_code(const std::string& source) {
// Create a parser.
std::shared_ptr<TSParser> parser(ts_parser_new(), TreeSitterParserDeleter());
// Set the parser's language (JSON in this case).
ts_parser_set_language(parser.get(), tree_sitter_opengoal());
// Build a syntax tree based on source code stored in a string.
@@ -476,11 +333,7 @@ std::optional<std::string> formatter::format_code(const std::string& source) {
// Get the root node of the syntax tree.
TSNode root_node = ts_tree_root_node(tree.get());
if (ts_node_is_null(root_node)) {
lg::error("null root node");
return std::nullopt;
} else if (ts_node_has_error(root_node)) {
lg::error("grammar parsing error, go figure it out!");
if (ts_node_is_null(root_node) || ts_node_has_error(root_node)) {
return std::nullopt;
}
@@ -499,8 +352,7 @@ std::optional<std::string> formatter::format_code(const std::string& source) {
const auto formatted_lines = apply_formatting(formatting_tree.root);
// 4. Now we joint he lines together, it's easier when formatting to leave all lines independent
// so adding indentation is easier
const auto formatted_source =
join_formatted_lines(formatted_lines, file_util::get_majority_file_line_endings(source));
const auto formatted_source = join_formatted_lines(formatted_lines);
return formatted_source;
} catch (std::exception& e) {
lg::error("Unable to format code - {}", e.what());
+5
View File
@@ -3,8 +3,13 @@
#include <optional>
#include <string>
#include "common/formatter/rules/formatting_rules.h"
#include "common/formatter/rules/rule_config.h"
#include "tree_sitter/api.h"
// TODO:
// - Considering _eventually_ adding line-length heuristics
namespace formatter {
struct TreeSitterParserDeleter {
+10 -66
View File
@@ -2,7 +2,7 @@
#include "common/util/string_util.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
std::string get_source_code(const std::string& source, const TSNode& node) {
uint32_t start = ts_node_start_byte(node);
@@ -78,24 +78,16 @@ FormatterTree::FormatterTree(const std::string& source, const TSNode& root_node)
construct_formatter_tree_recursive(source, root_node, root);
}
const std::unordered_map<std::string, std::vector<std::string>> node_type_ignorable_contents = {
{"list_lit", {"(", ")"}}};
// TODO make an imperative version eventually
// TODO - cleanup duplication
void FormatterTree::construct_formatter_tree_recursive(const std::string& source,
TSNode curr_node,
FormatterTreeNode& tree_node,
std::optional<std::string> node_prefix) {
FormatterTreeNode& tree_node) {
if (ts_node_child_count(curr_node) == 0) {
auto new_node = FormatterTreeNode(source, curr_node);
new_node.node_prefix = node_prefix;
tree_node.refs.push_back(new_node);
tree_node.refs.push_back(FormatterTreeNode(source, curr_node));
return;
}
const std::string curr_node_type = ts_node_type(curr_node);
FormatterTreeNode list_node;
std::optional<std::string> next_node_prefix;
if (curr_node_type == "list_lit") {
list_node = FormatterTreeNode();
} else if (curr_node_type == "str_lit") {
@@ -105,73 +97,25 @@ void FormatterTree::construct_formatter_tree_recursive(const std::string& source
tree_node.refs.push_back(FormatterTreeNode(source, curr_node));
return;
} else if (curr_node_type == "quoting_lit") {
if (node_prefix) {
node_prefix.value() += "'";
} else {
node_prefix = "'";
}
construct_formatter_tree_recursive(source, ts_node_child(curr_node, 1), tree_node, node_prefix);
// same story for quoted symbols
// TODO - expect to have to add more here
tree_node.refs.push_back(FormatterTreeNode(source, curr_node));
return;
} else if (curr_node_type == "unquoting_lit") {
if (node_prefix) {
node_prefix.value() += ",";
} else {
node_prefix = ",";
}
construct_formatter_tree_recursive(source, ts_node_child(curr_node, 1), tree_node, node_prefix);
return;
} else if (curr_node_type == "quasi_quoting_lit") {
if (node_prefix) {
node_prefix.value() += "`";
} else {
node_prefix = "`";
}
construct_formatter_tree_recursive(source, ts_node_child(curr_node, 1), tree_node, node_prefix);
return;
} else if (curr_node_type == "unquote_splicing_lit") {
if (node_prefix) {
node_prefix.value() += ",@";
} else {
node_prefix = ",@";
}
construct_formatter_tree_recursive(source, ts_node_child(curr_node, 1), tree_node, node_prefix);
return;
}
std::vector<std::string> skippable_nodes = {};
if (node_type_ignorable_contents.find(curr_node_type) != node_type_ignorable_contents.end()) {
skippable_nodes = node_type_ignorable_contents.at(curr_node_type);
}
for (size_t i = 0; i < ts_node_child_count(curr_node); i++) {
const auto child_node = ts_node_child(curr_node, i);
[[maybe_unused]] auto debug_child = ts_node_string(child_node);
// We skip parens
const auto contents = get_source_code(source, child_node);
bool skip_node = false;
for (const auto& skippable_content : skippable_nodes) {
if (skippable_content == contents) {
skip_node = true;
break;
}
}
if (skip_node) {
if (contents == "(" || contents == ")") {
continue;
}
if (curr_node_type == "list_lit") {
construct_formatter_tree_recursive(source, child_node, list_node, {});
if (node_prefix) {
list_node.node_prefix = node_prefix;
}
construct_formatter_tree_recursive(source, child_node, list_node);
} else {
construct_formatter_tree_recursive(source, child_node, tree_node, node_prefix);
if (node_prefix && !tree_node.refs.empty()) {
tree_node.refs.at(tree_node.refs.size() - 1).node_prefix = node_prefix;
}
construct_formatter_tree_recursive(source, child_node, tree_node);
}
}
if (curr_node_type == "list_lit") {
// special case for empty lists
if (node_prefix && !list_node.node_prefix) {
list_node.node_prefix = node_prefix;
}
tree_node.refs.push_back(list_node);
}
}
+5 -17
View File
@@ -18,9 +18,8 @@
// we really care about is:
// - getting all the text tokens for the source code
// - having them in a proper, nested format
//
// TLDR - The treesitter format is complicated and highly nested, leading to some very hard to
// understand code. So my solution is atleast a 2-pass format.
// The treesitter format is complicated and highly nested, leading to some very hard to understand
// code. So my solution is a 2-pass format.
//
// Pass 1 - convert the AST into a simplified FormatterTree
// Pass 2 - use the simplified tree to output the final code
@@ -38,26 +37,16 @@ class FormatterTreeNode {
std::vector<FormatterTreeNode> refs;
Metadata metadata;
// The token is optional because list nodes do not contain a token, they just contain a bunch of
// eventually-containing token node refs
// eventually token node refs
std::optional<std::string> token;
std::optional<std::string> node_prefix;
formatter_rules::config::FormFormattingConfig formatting_config;
FormatterTreeNode() = default;
FormatterTreeNode(const std::string& source, const TSNode& node);
FormatterTreeNode(const Metadata& _metadata) : metadata(_metadata) {};
FormatterTreeNode(const Metadata& _metadata) : metadata(_metadata){};
bool is_list() const { return !token.has_value(); }
std::string token_str() const {
if (node_prefix && token) {
return node_prefix.value() + token.value();
}
if (token) {
return token.value();
}
return "";
}
};
// A FormatterTree has a very simple and crude tree structure where:
@@ -73,6 +62,5 @@ class FormatterTree {
private:
void construct_formatter_tree_recursive(const std::string& source,
TSNode curr_node,
FormatterTreeNode& tree_node,
std::optional<std::string> node_prefix = {});
FormatterTreeNode& tree_node);
};
+11 -70
View File
@@ -2,9 +2,11 @@
#include <set>
#include "rule_config.h"
#include "common/util/string_util.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace formatter_rules {
@@ -12,16 +14,12 @@ namespace formatter_rules {
// differentiate between a quoted symbol and a quoted form
const std::set<std::string> constant_types = {"kwd_lit", "num_lit", "str_lit",
"char_lit", "null_lit", "bool_lit"};
const std::set<std::string> constant_type_forms = {"meters", "seconds", "degrees"};
namespace constant_list {
bool is_constant_list(const FormatterTreeNode& node) {
if (!node.is_list() || node.refs.empty()) {
return false;
}
if (!node.refs.at(0).token) {
return true;
}
const auto& type = node.refs.at(0).metadata.node_type;
return constant_types.find(type) != constant_types.end();
}
@@ -40,28 +38,21 @@ bool should_insert_blank_line(const FormatterTreeNode& containing_node,
if (node.metadata.is_comment && node.metadata.num_blank_lines_following == 0) {
return false;
}
// If the next form is a comment and is inline, don't insert a new line
// If the next form is a comment and is inline, don't insert a comment
if ((index + 1) < (int)containing_node.refs.size() &&
containing_node.refs.at(index + 1).metadata.is_comment &&
containing_node.refs.at(index + 1).metadata.is_inline) {
return false;
}
if (node.formatting_config.elide_top_level_newline) {
if ((index + 1) < (int)containing_node.refs.size() &&
containing_node.refs.at(index + 1).metadata.is_comment) {
return true;
}
return false;
}
// TODO - only if the form doesn't fit on a single line
return true;
}
} // namespace blank_lines
namespace comments {
std::vector<std::string> format_block_comment(const std::string& comment) {
std::string format_block_comment(const std::string& comment) {
// Normalize block comments, remove any trailing or leading whitespace
// Only allow annotations on the first line, like #|@file
// Don't mess with internal indentation as the user might intend it to be a certain way.
@@ -82,22 +73,12 @@ std::vector<std::string> format_block_comment(const std::string& comment) {
// Remove trailing whitespace
comment_contents = str_util::rtrim(comment_contents);
// remove |#
if (str_util::ends_with(comment_contents, "|#")) {
comment_contents.pop_back();
comment_contents.pop_back();
}
// TODO - check suffix
comment_contents.pop_back();
comment_contents.pop_back();
comment_contents = str_util::rtrim(comment_contents);
std::vector<std::string> lines = {new_comment};
const auto contents_as_lines = str_util::split_string(comment_contents, "\n");
if (contents_as_lines.size() > 1) {
for (const auto& line : contents_as_lines) {
lines.push_back(line);
}
lines.push_back("|#");
} else {
lines.at(0) = fmt::format("{} {} |#", new_comment, str_util::trim(contents_as_lines.at(0)));
}
return lines;
new_comment += fmt::format("\n{}\n|#", comment_contents);
return new_comment;
}
} // namespace comments
@@ -122,46 +103,6 @@ bool is_element_second_in_constant_pair(const FormatterTreeNode& containing_node
return true;
}
// TODO - potentially remove the above
bool is_element_second_in_constant_pair_new(const FormatterTreeNode& prev_node,
const FormatterTreeNode& curr_node) {
if (prev_node.metadata.node_type == "kwd_lit") {
// Handle standard constant types
// TODO - pair up sym_names as well
if (constant_types.find(curr_node.metadata.node_type) != constant_types.end()) {
if (curr_node.metadata.node_type != "kwd_lit") {
// NOTE - there is ambiugity here which cannot be totally solved (i think?)
// if the element itself is also a keyword, assume this is two adjacent keywords and they
// should not be paired
return true;
}
}
// Quoted symbols
if (curr_node.metadata.node_type == "sym_name" && curr_node.node_prefix &&
(curr_node.node_prefix.value() == "'" || curr_node.node_prefix.value() == ",")) {
return true;
}
if (!curr_node.refs.empty()) {
// Constant forms special cases (ie. meters)
if (constant_type_forms.find(curr_node.refs.at(0).token_str()) != constant_type_forms.end()) {
return true;
}
// If they are just a list of symbol names (enum or simple method call)
bool all_symbols = true;
for (const auto& ref : curr_node.refs) {
if (ref.metadata.node_type != "sym_name") {
all_symbols = false;
break;
}
}
if (all_symbols) {
return true;
}
}
}
return false;
}
bool form_should_be_constant_paired(const FormatterTreeNode& node) {
// Criteria for a list to be constant paired:
// - needs to start with a non-symbol
+1 -3
View File
@@ -36,7 +36,7 @@ bool should_insert_blank_line(const FormatterTreeNode& containing_node,
//
// Reference - https://github.com/kkinnear/zprint/blob/main/doc/options/comments.md
namespace comments {
std::vector<std::string> format_block_comment(const std::string& comment);
std::string format_block_comment(const std::string& comment);
}
// Paired elements in a list will be kept in-line rather than the default new-line indentation
@@ -72,8 +72,6 @@ const static int min_pair_amount = 4;
bool is_element_second_in_constant_pair(const FormatterTreeNode& containing_node,
const FormatterTreeNode& node,
const int index);
bool is_element_second_in_constant_pair_new(const FormatterTreeNode& prev_node,
const FormatterTreeNode& curr_node);
bool form_should_be_constant_paired(const FormatterTreeNode& node);
} // namespace constant_pairs
+16 -276
View File
@@ -1,147 +1,24 @@
#include "rule_config.h"
#include "common/util/string_util.h"
#include "common/formatter/formatter_tree.h"
namespace formatter_rules {
namespace config {
static FormFormattingConfig new_inlinable_simple_flow_rule() {
return {.config_set = true, .hang_forms = false};
}
static FormFormattingConfig new_permissive_flow_rule() {
return {.config_set = true, .hang_forms = false, .combine_first_two_lines = true};
}
static FormFormattingConfig new_flow_rule(int start_index, bool has_constant_pairs = false) {
return {.config_set = true,
.hang_forms = false,
.inline_until_index =
[start_index](const std::vector<std::string>& /*curr_lines*/) { return start_index; },
.has_constant_pairs = has_constant_pairs};
}
static FormFormattingConfig new_function_rule(int start_index, bool has_constant_pairs = false) {
FormFormattingConfig cfg = {
.config_set = true,
.hang_forms = false,
.inline_until_index =
[start_index](const std::vector<std::string>& /*curr_lines*/) { return start_index; },
.has_constant_pairs = has_constant_pairs};
auto arg_list_config = std::make_shared<FormFormattingConfig>();
arg_list_config->force_inline = true;
arg_list_config->hang_forms = false;
cfg.index_configs.emplace(2, arg_list_config);
return cfg;
}
static FormFormattingConfig new_inlineable_flow_rule(int start_index,
bool has_constant_pairs = false) {
return {.config_set = true,
.hang_forms = false,
.inline_until_index =
[start_index](const std::vector<std::string>& curr_lines) {
int total_width = 0;
for (const auto& line : curr_lines) {
// Check for comments
// TODO - this shows how this isn't really the best strategy but it holds up
if (str_util::contains(line, ";")) {
// Can't inline, there's a comment!
return start_index;
}
total_width += line.length();
// an empty line implies a new-line was forced, this is bleeding implementation
// details, but fine for now
if (line.empty()) {
return start_index;
}
}
if (total_width <= 120) {
return (int)curr_lines.size();
}
return start_index;
},
.has_constant_pairs = has_constant_pairs};
}
static FormFormattingConfig new_defstate_rule(int start_index, bool has_constant_pairs = false) {
FormFormattingConfig cfg = {
.config_set = true,
.hang_forms = false,
.inline_until_index =
[start_index](const std::vector<std::string>& /*curr_lines*/) { return start_index; },
.has_constant_pairs = has_constant_pairs};
std::vector<int> state_handler_indexes = {4, 6, 8, 10,
12, 14}; // NOTE - not all of these have to be defined
for (const auto& index : state_handler_indexes) {
auto temp_config = std::make_shared<FormFormattingConfig>();
temp_config->prevent_inlining = true;
temp_config->parent_mutable_extra_indent = 2;
cfg.index_config_override.emplace(index, temp_config);
}
return cfg;
}
static FormFormattingConfig new_defmethod_rule(int start_index, bool has_constant_pairs = false) {
// TODO - might be nice to have a function that returns a config based on a given index, instead
// of hardcoding them!
// Right now this only works for non-`new` methods (else we may bleed into the body of a normal
// method)
auto arg_list_config = std::make_shared<FormFormattingConfig>();
arg_list_config->force_inline = true;
arg_list_config->hang_forms = false;
FormFormattingConfig cfg = {.config_set = true,
.hang_forms = false,
.inline_until_index =
[start_index](const std::vector<std::string>& curr_lines) {
if (curr_lines.size() >= 2 && curr_lines.at(1) == "new") {
// defmethod was changed to omit the type name for everything
// except the `new` method, so special case.
return start_index + 1;
}
return start_index;
},
.has_constant_pairs = has_constant_pairs};
cfg.index_configs.emplace(2, arg_list_config);
return cfg;
}
static FormFormattingConfig new_lambda_rule(int start_index, bool has_constant_pairs = false) {
FormFormattingConfig cfg = {.config_set = true,
.hang_forms = false,
.inline_until_index =
[start_index](const std::vector<std::string>& curr_lines) {
if (curr_lines.size() >= 2 && curr_lines.at(1) == ":behavior") {
// defmethod was changed to omit the type name for everything
// except the `new` method, so special case.
return start_index + 2;
}
return start_index;
},
.has_constant_pairs = has_constant_pairs};
return cfg;
}
static FormFormattingConfig new_defenum_rule() {
auto temp_list_config = std::make_shared<FormFormattingConfig>();
temp_list_config->force_inline = true;
temp_list_config->hang_forms = false;
return {
.config_set = true,
.hang_forms = false,
.inline_until_index = [](const std::vector<std::string>& /*curr_lines*/) { return 2; },
.has_constant_pairs = true,
.default_index_config = temp_list_config,
// TODO - this could be greatly simplified with C++20's designated initialization
FormFormattingConfig new_flow_rule(int start_index) {
FormFormattingConfig cfg;
cfg.hang_forms = false;
cfg.inline_until_index = [start_index](const std::vector<std::string>& curr_lines) {
return start_index;
};
return cfg;
}
static FormFormattingConfig new_deftype_rule(
FormFormattingConfig new_flow_rule_prevent_inlining_indexes(
int start_index,
int num_columns_to_compute_widths,
const std::vector<int>& inlining_preventation_indices) {
FormFormattingConfig cfg;
cfg.has_constant_pairs = true;
cfg.config_set = true;
cfg.hang_forms = false;
cfg.inline_until_index = [start_index](std::vector<std::string> curr_lines) {
if (curr_lines.size() >= 4 && curr_lines.at(3) == "()") {
@@ -151,71 +28,26 @@ static FormFormattingConfig new_deftype_rule(
};
for (const auto& index : inlining_preventation_indices) {
auto temp_config = std::make_shared<FormFormattingConfig>();
temp_config->config_set = true;
temp_config->prevent_inlining = true;
temp_config->hang_forms = false;
temp_config->indentation_width = 1;
auto temp_list_config = std::make_shared<FormFormattingConfig>();
temp_list_config->force_inline = true;
temp_list_config->hang_forms = false;
temp_config->default_index_config = temp_list_config;
if (index == 3) {
temp_config->determine_column_widths_for_list_elements = true;
temp_config->num_columns_to_compute_widths = num_columns_to_compute_widths;
}
cfg.index_configs.emplace(index, temp_config);
}
return cfg;
}
static FormFormattingConfig new_defproc_rule(
int start_index,
int num_columns_to_compute_widths,
const std::vector<int>& inlining_preventation_indices) {
FormFormattingConfig new_binding_rule() {
FormFormattingConfig cfg;
cfg.has_constant_pairs = true;
cfg.config_set = true;
cfg.hang_forms = false;
cfg.inline_until_index = [start_index](std::vector<std::string> curr_lines) {
// if (curr_lines.size() >= 4 && curr_lines.at(3) == "()") {
// return 4;
// }
return start_index;
};
for (const auto& index : inlining_preventation_indices) {
auto temp_config = std::make_shared<FormFormattingConfig>();
temp_config->config_set = true;
temp_config->prevent_inlining = true;
temp_config->hang_forms = false;
temp_config->indentation_width = 1;
auto temp_list_config = std::make_shared<FormFormattingConfig>();
temp_list_config->force_inline = false;
temp_list_config->hang_forms = false;
temp_config->default_index_config = temp_list_config;
if (index == 3) {
temp_config->determine_column_widths_for_list_elements = true;
temp_config->num_columns_to_compute_widths = num_columns_to_compute_widths;
}
cfg.index_configs.emplace(index, temp_config);
}
return cfg;
}
static FormFormattingConfig new_binding_rule(int form_head_width) {
FormFormattingConfig cfg;
cfg.config_set = true;
cfg.hang_forms = false;
cfg.combine_first_two_lines = true;
auto binding_list_config = std::make_shared<FormFormattingConfig>();
binding_list_config->config_set = true;
binding_list_config->hang_forms = false;
binding_list_config->indentation_width = 1;
binding_list_config->indentation_width_for_index = [form_head_width](FormFormattingConfig /*cfg*/,
int index) {
binding_list_config->indentation_width_for_index = [](FormFormattingConfig /*cfg*/, int index) {
if (index == 0) {
return 0;
}
return form_head_width;
return 4;
};
binding_list_config->should_prevent_inlining = [](FormFormattingConfig /*config*/, int num_refs) {
// Only prevent inlining a binding list, if there are more than 1 bindings
@@ -230,119 +62,27 @@ static FormFormattingConfig new_binding_rule(int form_head_width) {
return cfg;
}
static FormFormattingConfig new_inline_binding_rule(int form_head_width) {
FormFormattingConfig new_pair_rule(bool combine_first_two_expr) {
FormFormattingConfig cfg;
cfg.config_set = true;
cfg.hang_forms = false;
cfg.combine_first_two_lines = true;
auto binding_list_config = std::make_shared<FormFormattingConfig>();
binding_list_config->config_set = true;
binding_list_config->hang_forms = false;
binding_list_config->indentation_width = 1;
binding_list_config->indentation_width_for_index = [form_head_width](FormFormattingConfig /*cfg*/,
int index) {
if (index == 0) {
return 0;
}
return form_head_width;
};
binding_list_config->should_prevent_inlining = [](FormFormattingConfig /*config*/, int num_refs) {
return false;
};
cfg.index_configs.emplace(1, binding_list_config);
return cfg;
}
static FormFormattingConfig new_pair_rule(bool combine_first_two_expr) {
FormFormattingConfig cfg;
cfg.config_set = true;
cfg.hang_forms = false;
cfg.prevent_inlining = true;
cfg.combine_first_two_lines = combine_first_two_expr;
auto pair_config = std::make_shared<FormFormattingConfig>();
pair_config->config_set = true;
pair_config->hang_forms = false;
pair_config->indentation_width = 1;
cfg.default_index_config = pair_config;
return cfg;
}
static FormFormattingConfig new_top_level_inline_form(bool elide_new_line) {
return {.force_inline = true, .elide_top_level_newline = elide_new_line};
}
const std::unordered_map<std::string, FormFormattingConfig> opengoal_form_config = {
{"case", new_pair_rule(true)},
{"case-str", new_pair_rule(true)},
{"cond", new_pair_rule(false)},
{"#cond", new_pair_rule(false)},
{"in-package", new_top_level_inline_form(true)},
{"bundles", new_top_level_inline_form(true)},
{"require", new_top_level_inline_form(true)},
{"def-art-elt", new_top_level_inline_form(true)},
{"def-joint-node", new_top_level_inline_form(true)},
{"declare-file", new_top_level_inline_form(false)},
{"defenum", new_defenum_rule()},
{"defmethod", new_defmethod_rule(3)},
{"lambda", new_lambda_rule(2)},
{"deftype", new_deftype_rule(3, 1, {3, 4, 5, 6})},
{"defproc", new_defproc_rule(3, 1, {3, 4, 5, 6})},
{"suspend-for", new_flow_rule(2)},
{"spawn-proc", new_flow_rule(2)},
{"defmethod", new_flow_rule(3)},
{"deftype", new_flow_rule_prevent_inlining_indexes(3, {3, 4, 5})},
{"defun", new_flow_rule(3)},
{"defun-recursive", new_flow_rule(4)},
{"defun-debug-recursive", new_flow_rule(4)},
{"defun-debug", new_flow_rule(3)},
{"defbehavior", new_flow_rule(4)},
{"if", new_inlineable_flow_rule(2)},
{"aif", new_inlineable_flow_rule(2)},
{"#if", new_inlineable_flow_rule(2)},
{"define", new_permissive_flow_rule()},
{"def-mips2c", new_permissive_flow_rule()},
{"defconstant", new_permissive_flow_rule()},
{"defglobalconstant", new_permissive_flow_rule()},
{"defmethod-mips2c", new_permissive_flow_rule()},
{"define-extern", new_permissive_flow_rule()},
{"declare-type", new_permissive_flow_rule()},
{"defmacro", new_function_rule(3)},
{"desfun", new_function_rule(3)},
{"def-actor", new_flow_rule(2, true)},
{"defskelgroup", new_flow_rule(2, true)},
{"defpartgroup", new_flow_rule(2, true)},
{"defpart", new_flow_rule(2, true)},
{"defstate", new_defstate_rule(3, true)},
{"defevent", new_flow_rule(2)},
{"event", new_flow_rule(1)},
{"defpost", new_flow_rule(2)},
{"behavior", new_flow_rule(2)},
{"dotimes", new_flow_rule(2)},
{"dolist", new_flow_rule(2)},
{"process-spawn-function", new_flow_rule(2)},
{"let", new_binding_rule(4)},
{"protect", new_binding_rule(4)},
{"let*", new_binding_rule(5)},
{"rlet", new_binding_rule(5)},
{"mlet", new_binding_rule(5)},
{"let", new_binding_rule()},
{"when", new_flow_rule(2)},
{"awhen", new_flow_rule(2)},
{"unless", new_flow_rule(2)},
{"with-profiler", new_flow_rule(2)},
{"with-pc", new_flow_rule(0)},
{"#unless", new_flow_rule(2)},
{"#when", new_flow_rule(2)},
{"#when-game", new_flow_rule(2)},
{"countdown", new_flow_rule(2)},
{"until", new_flow_rule(2)},
{"loop", new_flow_rule(0)},
{"while", new_flow_rule(2)},
{"begin", new_flow_rule(0)},
{"seval", new_flow_rule(0)},
{"with-pp", new_flow_rule(0)},
{"with-gensyms", new_flow_rule(2)},
{"with-vf0", new_flow_rule(0)},
{"with-vf", new_inline_binding_rule(8)},
{"with-cnt-vif-block", new_inline_binding_rule(19)},
{"local-vars", new_inlinable_simple_flow_rule()},
{"with-dma-buffer-add-bucket", new_flow_rule(2)}};
} // namespace config
} // namespace formatter_rules
+5 -15
View File
@@ -10,11 +10,9 @@
namespace formatter_rules {
namespace config {
struct FormFormattingConfig {
bool config_set = false;
bool force_inline = false;
bool hang_forms =
true; // TODO - remove this eventually, it's only involved in setting the
// indentation width, which we can do via the new indentation_width function
// new
bool hang_forms = true; // TODO - remove this eventually, it's only involved in setting the
// indentation width, which we can do via the indentation_width function
int indentation_width =
2; // 2 for a flow // TODO - also remove this, prefer storing the first node's width in the
// metadata on the first pass, that's basically all this does
@@ -23,8 +21,8 @@ struct FormFormattingConfig {
bool combine_first_two_lines =
false; // NOTE - basically hang, but will probably stick around after hang is gone, may be
// redundant (inline_until_index!)
std::function<std::optional<int>(const std::vector<std::string>& /*curr_lines*/)>
inline_until_index = [](std::vector<std::string> /*curr_lines*/) { return std::nullopt; };
std::function<std::optional<int>(const std::vector<std::string>& curr_lines)> inline_until_index =
[](std::vector<std::string> curr_lines) { return std::nullopt; };
bool has_constant_pairs = false;
bool prevent_inlining = false; // TODO - duplicate of below
std::function<bool(FormFormattingConfig, int num_refs)> should_prevent_inlining =
@@ -32,14 +30,6 @@ struct FormFormattingConfig {
int parent_mutable_extra_indent = 0;
std::optional<std::shared_ptr<FormFormattingConfig>> default_index_config;
std::unordered_map<int, std::shared_ptr<FormFormattingConfig>> index_configs = {};
// TODO / NOTe - not fully implemented, only doing `parent_mutable_extra_indent` right now
std::unordered_map<int, std::shared_ptr<FormFormattingConfig>> index_config_override = {};
bool determine_column_widths_for_list_elements = false;
int num_columns_to_compute_widths = 0;
std::vector<int> list_element_column_widths = {};
bool elide_top_level_newline = false;
};
extern const std::unordered_map<std::string, FormFormattingConfig> opengoal_form_config;
+11 -41
View File
@@ -9,7 +9,7 @@
#include "common/util/FileUtil.h"
#include "common/common_types.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#include "third-party/json.hpp"
#ifdef OS_POSIX
@@ -26,8 +26,6 @@ u64 get_current_tid() {
}
#endif
#include "common/log/log.h"
#include "common/util/compress.h"
#include "common/util/string_util.h"
// clang-format on
u64 get_current_ts() {
@@ -36,25 +34,16 @@ u64 get_current_ts() {
GlobalProfiler::GlobalProfiler() {
m_t0 = get_current_ts();
m_nodes.resize(m_max_events);
set_max_events(65536);
}
void GlobalProfiler::update_event_buffer_size(size_t new_size) {
m_max_events = new_size;
m_nodes.resize(m_max_events);
}
void GlobalProfiler::set_waiting_for_event(const std::string& event_name) {
if (!event_name.empty()) {
m_waiting_for_event = event_name;
}
void GlobalProfiler::set_max_events(size_t event_count) {
ASSERT(!m_enabled);
m_nodes.resize(event_count);
}
void GlobalProfiler::event(const char* name, ProfNode::Kind kind) {
if (m_waiting_for_event && m_waiting_for_event.value() == name) {
m_ignore_events = true;
}
if (!m_enabled || m_ignore_events) {
if (!m_enabled) {
return;
}
size_t my_idx = (m_next_idx++ % m_nodes.size());
@@ -74,16 +63,12 @@ void GlobalProfiler::root_event() {
instant_event("ROOT");
}
size_t GlobalProfiler::get_next_idx() {
return (m_next_idx % m_nodes.size());
}
void GlobalProfiler::begin_event(const char* name) {
event(name, ProfNode::BEGIN);
}
void GlobalProfiler::end_event() {
if (!m_enabled || m_ignore_events) {
if (!m_enabled) {
return;
}
size_t my_idx = (m_next_idx++ % m_nodes.size());
@@ -102,10 +87,8 @@ void GlobalProfiler::set_enable(bool en) {
m_enabled = en;
}
void GlobalProfiler::dump_to_json() {
if (m_enabled) {
set_enable(false);
}
void GlobalProfiler::dump_to_json(const std::string& path) {
ASSERT(!m_enabled);
nlohmann::json json;
auto& trace_events = json["traceEvents"];
@@ -183,7 +166,7 @@ void GlobalProfiler::dump_to_json() {
lg::debug("out of order: {} {} {} ms", event.ts / 1000.f, info.debug / 1000.f,
(info.debug - event.ts) / 1000000.f);
lg::debug(" idx: {}, range {} {}", event_idx, info.lowest_at_target, info.highest_at_target);
lg::debug(" now: {}", m_next_idx.load());
lg::debug(" now: {}", m_next_idx);
}
info.debug = event.ts;
}
@@ -193,20 +176,7 @@ void GlobalProfiler::dump_to_json() {
t.second.highest_at_target);
}
if (m_enable_compression) {
const auto json_str = json.dump();
const auto compressed_data =
compression::compress_zstd_no_header(json_str.data(), json_str.size());
auto file_path = file_util::get_jak_project_dir() / "profile_data" /
fmt::format("prof-{}.json.zst", str_util::current_local_timestamp_no_colons());
file_util::create_dir_if_needed_for_file(file_path);
file_util::write_binary_file(file_path, compressed_data.data(), compressed_data.size());
} else {
auto file_path = file_util::get_jak_project_dir() / "profile_data" /
fmt::format("prof-{}.json", str_util::current_local_timestamp_no_colons());
file_util::create_dir_if_needed_for_file(file_path);
file_util::write_text_file(file_path, json.dump());
}
file_util::write_text_file(path, json.dump());
}
GlobalProfiler gprof;
+2 -16
View File
@@ -1,7 +1,6 @@
#pragma once
#include <atomic>
#include <optional>
#include <string>
#include <vector>
@@ -17,37 +16,24 @@ struct ProfNode {
class GlobalProfiler {
public:
GlobalProfiler();
size_t get_max_events() { return m_max_events; }
void update_event_buffer_size(size_t new_size);
void set_waiting_for_event(const std::string& event_name);
void set_max_events(size_t event_count);
void instant_event(const char* name);
void begin_event(const char* name);
void event(const char* name, ProfNode::Kind kind);
void end_event();
void clear();
void set_enable(bool en);
void dump_to_json();
void dump_to_json(const std::string& path);
void root_event();
bool is_enabled() { return m_enabled; }
size_t get_next_idx();
bool m_enable_compression = false;
private:
std::atomic_bool m_enabled = false;
size_t m_max_events = 65536;
u64 m_t0 = 0;
std::atomic_size_t m_next_idx = 0;
std::vector<ProfNode> m_nodes;
// this is very niche, but sometimes you want to capture up to a given event (ie. long startup)
// instead of having to make the user quit and record as fast as possible, we can instead just
// stop capturing events once we have received what we are looking for
std::optional<std::string> m_waiting_for_event = {};
bool m_ignore_events = false;
};
struct ScopedEvent {
ScopedEvent(GlobalProfiler* _prof) : prof(_prof) {};
ScopedEvent(const ScopedEvent&) = delete;
ScopedEvent& operator=(const ScopedEvent&) = delete;
GlobalProfiler* prof = nullptr;
+7 -12
View File
@@ -62,24 +62,21 @@ constexpr int LEVEL_MAX = 6;
constexpr int LEVEL_TOTAL = LEVEL_MAX + 1;
} // namespace jak2
// TODO copypaste from jak 2 for now
namespace jak3 {
// for now, we don't have the ability to extend the size of the symbol table
constexpr s32 GOAL_MAX_SYMBOLS = 0x4000;
constexpr s32 SYM_TABLE_MEM_SIZE = 0x30000;
// from the "off-by-one" symbol pointer
constexpr int SYM_TO_STRING_OFFSET = 0xff37;
constexpr int SYM_TO_HASH_OFFSET = 0x1fe6f;
// amount of levels in level heap
constexpr int LEVEL_MAX = 10;
// total amount of levels, including ones outside level heap (default-level)
constexpr int LEVEL_TOTAL = LEVEL_MAX + 1;
} // namespace jak3
namespace jakx {
// for now, we don't have the ability to extend the size of the symbol table
constexpr s32 GOAL_MAX_SYMBOLS = 0x4000;
// amount of levels in level heap
constexpr int LEVEL_MAX = 10;
// total amount of levels, including ones outside level heap (default-level)
constexpr int LEVEL_TOTAL = LEVEL_MAX + 1;
} // namespace jakx
constexpr s32 max_symbols(GameVersion version) {
switch (version) {
case GameVersion::Jak1:
@@ -87,9 +84,7 @@ constexpr s32 max_symbols(GameVersion version) {
case GameVersion::Jak2:
return jak2::GOAL_MAX_SYMBOLS;
case GameVersion::Jak3:
return jak3::GOAL_MAX_SYMBOLS;
case GameVersion::JakX:
return jakx::GOAL_MAX_SYMBOLS;
return jak2::GOAL_MAX_SYMBOLS;
}
}
+114 -8
View File
@@ -16,9 +16,7 @@
#include "common/util/string_util.h"
#include "common/util/unicode_util.h"
#include "fmt/args.h"
#include "fmt/base.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace goos {
Interpreter::Interpreter(const std::string& username) {
@@ -573,6 +571,104 @@ Object Interpreter::eval(Object obj, const std::shared_ptr<EnvironmentObject>& e
}
}
EnvironmentMap::EnvironmentMap() {
clear();
}
void EnvironmentMap::clear() {
m_entries.clear();
m_power_of_two_size = 3; // 2 ^ 3 = 8
m_entries.resize(8);
m_used_entries = 0;
m_next_resize = (m_entries.size() * kMaxUsed);
m_mask = 0b111;
}
Object* EnvironmentMap::lookup(InternedSymbolPtr str) {
if (m_entries.size() < 10) {
for (auto& e : m_entries) {
if (e.key == str.name_ptr) {
return &e.value;
}
}
return nullptr;
}
u32 hash = crc32((const u8*)&str.name_ptr, sizeof(const char*));
// probe
for (u32 i = 0; i < m_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = m_entries[slot_addr];
if (!slot.key) {
return nullptr;
} else {
if (slot.key != str.name_ptr) {
continue; // bad hash
}
return &slot.value;
}
}
// should be impossible to reach.
ASSERT_NOT_REACHED();
}
void EnvironmentMap::set(InternedSymbolPtr ptr, const Object& obj) {
u32 hash = crc32((const u8*)&ptr.name_ptr, sizeof(const char*));
// probe
for (u32 i = 0; i < m_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = m_entries[slot_addr];
if (!slot.key) {
// not found, insert!
slot.key = ptr.name_ptr;
slot.value = obj;
m_used_entries++;
if (m_used_entries >= m_next_resize) {
resize();
}
return;
} else {
if (slot.key == ptr.name_ptr) {
slot.value = obj;
return;
}
}
}
// should be impossible to reach.
ASSERT_NOT_REACHED();
}
void EnvironmentMap::resize() {
m_power_of_two_size++;
m_mask = (1U << m_power_of_two_size) - 1;
std::vector<Entry> new_entries(m_entries.size() * 2);
for (const auto& old_entry : m_entries) {
if (old_entry.key) {
bool done = false;
u32 hash = crc32((const u8*)&old_entry.key, sizeof(const char*));
for (u32 i = 0; i < new_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = new_entries[slot_addr];
if (!slot.key) {
slot.key = old_entry.key;
slot.value = std::move(old_entry.value);
done = true;
break;
}
}
ASSERT(done);
}
}
m_entries = std::move(new_entries);
m_next_resize = kMaxUsed * m_entries.size();
}
namespace {
/*!
@@ -1771,18 +1867,28 @@ Object Interpreter::eval_format(const Object& form,
throw_eval_error(form, "format string must be a string");
}
fmt::dynamic_format_arg_store<fmt::format_context> arg_store;
// Note: this might be relying on internal implementation details of libfmt to work properly
// and isn't a great solution.
std::vector<fmt::basic_format_arg<fmt::format_context>> args2;
std::vector<std::string> strings;
for (size_t i = 2; i < args.unnamed.size(); i++) {
if (args.unnamed.at(i).is_string()) {
arg_store.push_back(args.unnamed.at(i).as_string()->data);
strings.push_back(args.unnamed.at(i).as_string()->data);
} else {
arg_store.push_back(args.unnamed.at(i).print());
strings.push_back(args.unnamed.at(i).print());
}
}
auto formatted = fmt::vformat(format_str.as_string()->data, arg_store);
for (auto& x : strings) {
args2.push_back(fmt::detail::make_arg<fmt::format_context>(x));
}
auto formatted =
fmt::vformat(format_str.as_string()->data,
fmt::format_args(args2.data(), static_cast<unsigned>(args2.size())));
if (truthy(dest)) {
lg::print("{}", formatted.c_str());
lg::print(formatted.c_str());
}
return StringObject::make_new(formatted);
+1 -1
View File
@@ -47,7 +47,7 @@
#include "common/util/crc32.h"
#include "common/util/print_float.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace goos {
+10 -115
View File
@@ -55,9 +55,6 @@
#include "common/common_types.h"
#include "common/util/Assert.h"
#include "common/util/crc32.h"
#include "fmt/format.h"
namespace goos {
@@ -236,14 +233,10 @@ class Object {
ObjectType type = ObjectType::INVALID;
private:
bool disallow_hex_for_int = false;
public:
std::string print() const {
switch (type) {
case ObjectType::INTEGER:
return disallow_hex_for_int ? fmt::format("{}", integer_obj.value) : integer_obj.print();
return integer_obj.print();
case ObjectType::FLOAT:
return float_obj.print();
case ObjectType::CHAR:
@@ -284,14 +277,6 @@ class Object {
return o;
}
static Object make_integer_no_hex(IntType value) {
Object o;
o.type = ObjectType::INTEGER;
o.integer_obj.value = value;
o.disallow_hex_for_int = true;
return o;
}
static Object make_float(FloatType value) {
Object o;
o.type = ObjectType::FLOAT;
@@ -471,111 +456,23 @@ class PairObject : public HeapObject {
~PairObject() = default;
};
template <typename T>
class InternedPtrMap {
class EnvironmentMap {
public:
InternedPtrMap(const InternedPtrMap&) = delete;
InternedPtrMap& operator=(const InternedPtrMap&) = delete;
InternedPtrMap() { clear(); }
T* lookup(InternedSymbolPtr str) {
if (m_entries.size() < 10) {
for (auto& e : m_entries) {
if (e.key == str.name_ptr) {
return &e.value;
}
}
return nullptr;
}
u32 hash = crc32((const u8*)&str.name_ptr, sizeof(const char*));
// probe
for (u32 i = 0; i < m_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = m_entries[slot_addr];
if (!slot.key) {
return nullptr;
} else {
if (slot.key != str.name_ptr) {
continue; // bad hash
}
return &slot.value;
}
}
// should be impossible to reach.
ASSERT_NOT_REACHED();
}
void set(InternedSymbolPtr ptr, const T& obj) {
u32 hash = crc32((const u8*)&ptr.name_ptr, sizeof(const char*));
// probe
for (u32 i = 0; i < m_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = m_entries[slot_addr];
if (!slot.key) {
// not found, insert!
slot.key = ptr.name_ptr;
slot.value = obj;
m_used_entries++;
if (m_used_entries >= m_next_resize) {
resize();
}
return;
} else {
if (slot.key == ptr.name_ptr) {
slot.value = obj;
return;
}
}
}
// should be impossible to reach.
ASSERT_NOT_REACHED();
}
void clear() {
m_entries.clear();
m_power_of_two_size = 3; // 2 ^ 3 = 8
m_entries.resize(8);
m_used_entries = 0;
m_next_resize = (m_entries.size() * kMaxUsed);
m_mask = 0b111;
}
EnvironmentMap(const EnvironmentMap&) = delete;
EnvironmentMap& operator=(const EnvironmentMap&) = delete;
EnvironmentMap();
Object* lookup(InternedSymbolPtr ptr);
void set(InternedSymbolPtr ptr, const Object& obj);
void clear();
private:
struct Entry {
const char* key = nullptr;
T value;
Object value;
};
std::vector<Entry> m_entries;
void resize() {
m_power_of_two_size++;
m_mask = (1U << m_power_of_two_size) - 1;
std::vector<Entry> new_entries(m_entries.size() * 2);
for (const auto& old_entry : m_entries) {
if (old_entry.key) {
bool done = false;
u32 hash = crc32((const u8*)&old_entry.key, sizeof(const char*));
for (u32 i = 0; i < new_entries.size(); i++) {
u32 slot_addr = (hash + i) & m_mask;
auto& slot = new_entries[slot_addr];
if (!slot.key) {
slot.key = old_entry.key;
slot.value = std::move(old_entry.value);
done = true;
break;
}
}
ASSERT(done);
}
}
m_entries = std::move(new_entries);
m_next_resize = kMaxUsed * m_entries.size();
}
void resize();
int m_power_of_two_size = 0;
int m_used_entries = 0;
int m_next_resize = 0;
@@ -583,8 +480,6 @@ class InternedPtrMap {
static constexpr float kMaxUsed = 0.7;
};
using EnvironmentMap = InternedPtrMap<Object>;
class EnvironmentObject : public HeapObject {
public:
std::string name;
+1 -1
View File
@@ -1,6 +1,6 @@
#include "ParseHelpers.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace goos {
+1 -1
View File
@@ -15,7 +15,7 @@
#include "common/goos/PrettyPrinter2.h"
#include "common/util/Assert.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace pretty_print {
+6 -5
View File
@@ -3,7 +3,8 @@
#include "common/common_types.h"
#include "common/util/Assert.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#include "third-party/fmt/format.h"
namespace pretty_print {
@@ -294,7 +295,7 @@ void break_list(Node* node) {
}
} else if (name == "until" || name == "while" || name == "dotimes" || name == "countdown" ||
name == "when" || name == "behavior" || name == "lambda" || name == "defpart" ||
name == "define" || name == "suspend-for") {
name == "define") {
node->top_line_count = 2;
} else if (name == "let" || name == "let*" || name == "rlet" ||
name == "with-dma-buffer-add-bucket") {
@@ -340,9 +341,9 @@ void break_list(Node* node) {
void insert_required_breaks(const std::vector<Node*>& bfs_order) {
const std::unordered_set<std::string> always_break = {
"when", "defun-debug", "countdown", "case", "defun", "defmethod", "let", "until",
"while", "if", "dotimes", "cond", "else", "defbehavior", "with-pp", "rlet",
"defstate", "behavior", "defpart", "loop", "let*", "suspend-for"};
"when", "defun-debug", "countdown", "case", "defun", "defmethod", "let",
"until", "while", "if", "dotimes", "cond", "else", "defbehavior",
"with-pp", "rlet", "defstate", "behavior", "defpart", "loop", "let*"};
for (auto node : bfs_order) {
if (!node->break_list && node->kind == Node::Kind::LIST &&
node->child_nodes.at(0).kind == Node::Kind::ATOM) {
+1 -1
View File
@@ -6,7 +6,7 @@
#include "common/goos/Object.h"
#include "common/util/print_float.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace pretty_print {
namespace {
+58 -19
View File
@@ -12,11 +12,11 @@
#include "Reader.h"
#include "common/log/log.h"
#include "common/repl/util.h"
#include "common/util/FileUtil.h"
#include "common/util/font/font_utils.h"
#include "common/util/FontUtils.h"
#include "fmt/format.h"
#include "fmt/ranges.h"
#include "third-party/fmt/core.h"
namespace goos {
@@ -156,6 +156,39 @@ Reader::Reader() {
for (const char* c = bonus; *c; c++) {
m_valid_symbols_chars[(int)*c] = true;
}
// table of characters that are valid in source code:
for (auto& x : m_valid_source_text_chars) {
x = false;
}
for (int i = ' '; i <= '~'; i++) {
m_valid_source_text_chars[i] = true;
}
m_valid_source_text_chars[(int)'\n'] = true;
m_valid_source_text_chars[(int)'\t'] = true;
m_valid_source_text_chars[(int)'\r'] = true;
// allow every character that gets transformed to something else
for (auto& [version, font] : g_font_banks) {
for (auto& remap : *font->encode_info()) {
for (auto rc : remap.chars) {
m_valid_source_text_chars[(u8)rc] = true;
}
}
for (auto& remap : *font->replace_info()) {
for (auto rc : remap.to) {
m_valid_source_text_chars[(u8)rc] = true;
}
for (auto rc : remap.from) {
m_valid_source_text_chars[(u8)rc] = true;
}
}
}
m_valid_source_text_chars[0] = false;
}
bool Reader::is_valid_source_char(char c) const {
return m_valid_source_text_chars[(u8)c];
}
/*!
@@ -205,14 +238,9 @@ Object Reader::read_from_string(const std::string& str,
* Read a file
*/
Object Reader::read_from_file(const std::vector<std::string>& file_path, bool check_encoding) {
std::string file_descriptor = fmt::format("{}", fmt::join(file_path, "/"));
const auto joined_file_path = file_util::get_file_path(file_path);
std::string joined_path = fmt::format("{}", fmt::join(file_path, "/"));
if (!fs::exists(joined_file_path)) {
throw std::runtime_error(fmt::format("Cannot read {}, file doesn't exist", joined_file_path));
}
auto textFrag = std::make_shared<FileText>(joined_file_path, file_descriptor);
auto textFrag = std::make_shared<FileText>(file_util::get_file_path(file_path), joined_path);
db.insert(textFrag);
auto result = internal_read(textFrag, check_encoding);
@@ -233,6 +261,17 @@ Object Reader::internal_read(std::shared_ptr<SourceText> text,
fmt::format("Text file {} has invalid encoding", text->get_description()));
}
// validate the input
for (int offset = check_encoding ? 3 : 0; offset < text->get_size(); offset++) {
if (!is_valid_source_char(text->get_text()[offset])) {
// failed.
int line_number = text->get_line_idx(offset) + 1;
throw std::runtime_error(fmt::format("Invalid character found on line {} of {}: 0x{:x}",
line_number, text->get_description(),
(u8)text->get_text()[offset]));
}
}
// first create stream
TextStream ts(text);
@@ -257,6 +296,15 @@ Object Reader::internal_read(std::shared_ptr<SourceText> text,
}
}
bool Reader::check_string_is_valid(const std::string& str) const {
for (auto c : str) {
if (!is_valid_source_char(c)) {
return false;
}
}
return true;
}
/*!
* Given a stream starting at the first character of a token, get the token. Doesn't consume
* whitespace at the end and leaves the stream on the first character after the token.
@@ -852,13 +900,4 @@ std::string get_readable_string(const char* in) {
}
return result;
}
std::string get_byte_string(const char* in) {
std::string result;
while (*in) {
result += fmt::format("\\c{:02x}", uint8_t(*in));
in++;
}
return result;
}
} // namespace goos
+5 -2
View File
@@ -18,7 +18,7 @@
#include "common/goos/Object.h"
#include "common/goos/TextDB.h"
#include "common/repl/repl_wrapper.h"
#include "common/repl/util.h"
#include "common/util/Assert.h"
namespace goos {
@@ -75,6 +75,7 @@ class Reader {
const std::optional<std::string>& string_name = {});
std::optional<Object> read_from_stdin(const std::string& prompt, REPL::Wrapper& repl);
Object read_from_file(const std::vector<std::string>& file_path, bool check_encoding = false);
bool check_string_is_valid(const std::string& str) const;
SymbolTable symbolTable;
TextDb db;
@@ -100,10 +101,12 @@ class Reader {
void add_reader_macro(const std::string& shortcut, std::string replacement);
bool m_valid_symbols_chars[256];
bool m_valid_source_text_chars[256];
bool is_valid_source_char(char c) const;
std::unordered_map<std::string, std::string> m_reader_macros;
};
std::string get_byte_string(const char* in);
std::string get_readable_string(const char* in);
} // namespace goos
+4 -4
View File
@@ -15,7 +15,7 @@
#include "common/util/FileUtil.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace goos {
@@ -85,9 +85,9 @@ std::pair<int, int> SourceText::get_containing_line(int offset) {
/*!
* Read text from a file.
*/
FileText::FileText(const std::string& file_path, const std::string& description_name)
: m_filepath(file_path), m_desc_name(description_name) {
m_text = file_util::read_text_file(m_filepath);
FileText::FileText(const std::string& filename, const std::string& description_name)
: m_filename(filename), m_desc_name(description_name) {
m_text = file_util::read_text_file(m_filename);
build_offsets();
}
+3 -3
View File
@@ -41,7 +41,7 @@ class SourceText {
// want
virtual bool terminate_compiler_error() { return true; }
virtual ~SourceText() {};
virtual ~SourceText(){};
protected:
void build_offsets();
@@ -80,13 +80,13 @@ class ProgramString : public SourceText {
*/
class FileText : public SourceText {
public:
FileText(const std::string& file_path, const std::string& description_name);
FileText(const std::string& filename, const std::string& description_name);
std::string get_description() { return m_desc_name; }
~FileText() = default;
private:
std::string m_filepath;
std::string m_filename;
std::string m_desc_name;
};
-15
View File
@@ -54,21 +54,6 @@ struct LinkHeaderV4 {
uint32_t code_size; // length of object data before link data starts
};
struct LinkHeaderV5Core {
uint32_t length_to_get_to_code; // 4 length.. of link data?
uint16_t version; // 8
uint16_t unknown; // 10
uint32_t length_to_get_to_link; // 12
uint32_t link_length; // 16
uint8_t n_segments; // 20
char name[59]; // 21 (really??)
};
struct LinkHeaderV5 {
uint32_t type_tag; // 0 always 0?
LinkHeaderV5Core core;
};
// when a u32/s32 symbol link contains this value, (s7 + <val>) should be a 4-byte aligned address,
// not including the 1 byte symbol offset. (no effect in jak 1).
constexpr u32 LINK_SYM_NO_OFFSET_FLAG = 0xbadbeef;
+6 -32
View File
@@ -1,11 +1,10 @@
#include "log.h"
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <mutex>
#include "fmt/color.h"
#include "third-party/fmt/color.h"
#ifdef _WIN32 // see lg::initialize
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
@@ -107,28 +106,7 @@ void log_print(const char* message) {
}
if (gLogger.stdout_log_level < lg::level::off_unless_die) {
fmt::print("{}", message);
fflush(stdout);
fflush(stderr);
}
}
}
void log_vprintf(const char* format, va_list arg_list) {
{
// We always immediately flush prints because since it has no associated level
// it could be anything from a fatal error to a useless debug log.
std::lock_guard<std::mutex> lock(gLogger.mutex);
va_list arg_list_2;
va_copy(arg_list_2, arg_list);
if (gLogger.fp) {
// Log to File
vfprintf(gLogger.fp, format, arg_list);
fflush(gLogger.fp);
}
if (gLogger.stdout_log_level < lg::level::off_unless_die) {
vprintf(format, arg_list_2);
fmt::print(message);
fflush(stdout);
fflush(stderr);
}
@@ -136,11 +114,7 @@ void log_vprintf(const char* format, va_list arg_list) {
}
} // namespace internal
void printstd(const char* format, va_list arg_list) {
internal::log_vprintf(format, arg_list);
}
// how many extra log files for a single program should be kept
// how many extra log files for a single program should be kept?
constexpr int LOG_ROTATE_MAX = 10;
void set_file(const std::string& filename,
@@ -163,7 +137,7 @@ void set_file(const std::string& filename,
file_util::find_files_in_dir(fs::path(complete_filename).parent_path(),
std::regex(fmt::format("{}\\.(\\d\\.)?log", filename)));
for (const auto& file : old_log_files) {
lg::debug("removing {}", file.string());
lg::info("removing {}", file.string());
fs::remove(file);
}
// remove the oldest log file if there are more than LOG_ROTATE_MAX
@@ -172,9 +146,9 @@ void set_file(const std::string& filename,
// sort the names and remove them
existing_log_files = file_util::sort_filepaths(existing_log_files, true);
if (existing_log_files.size() > (LOG_ROTATE_MAX - 1)) {
lg::debug("removing {} log files", existing_log_files.size() - (LOG_ROTATE_MAX - 1));
lg::info("removing {} log files", existing_log_files.size() - (LOG_ROTATE_MAX - 1));
for (int i = 0; i < (int)existing_log_files.size() - (LOG_ROTATE_MAX - 1); i++) {
lg::debug("removing {}", existing_log_files.at(i).string());
lg::info("removing {}", existing_log_files.at(i).string());
fs::remove(existing_log_files.at(i));
}
}
+5 -9
View File
@@ -7,8 +7,8 @@
#endif
#include <string>
#include "fmt/color.h"
#include "fmt/format.h"
#include "third-party/fmt/color.h"
#include "third-party/fmt/core.h"
namespace lg {
@@ -38,7 +38,6 @@ namespace internal {
// log implementation stuff, not to be called by the user
void log_message(level log_level, LogTime& now, const char* message);
void log_print(const char* message);
void log_vprintf(const char* format, va_list arg_list);
} // namespace internal
void set_file(const std::string& filename,
@@ -61,24 +60,21 @@ void log(level log_level, const std::string& format, Args&&... args) {
#else
now.tim = time(nullptr);
#endif
std::string formatted_message = fmt::format(fmt::runtime(format), std::forward<Args>(args)...);
std::string formatted_message = fmt::format(format, std::forward<Args>(args)...);
internal::log_message(log_level, now, formatted_message.c_str());
}
template <typename... Args>
void print(const std::string& format, Args&&... args) {
std::string formatted_message = fmt::format(fmt::runtime(format), std::forward<Args>(args)...);
std::string formatted_message = fmt::format(format, std::forward<Args>(args)...);
internal::log_print(formatted_message.c_str());
}
template <typename... Args>
void print(const fmt::text_style& ts, const std::string& format, Args&&... args) {
std::string formatted_message = fmt::vformat(ts, format, fmt::make_format_args(args...));
std::string formatted_message = fmt::format(ts, format, std::forward<Args>(args)...);
internal::log_print(formatted_message.c_str());
}
// same as print but uses the C printf instead of fmt
void printstd(const char* format, va_list arg_list);
template <typename... Args>
void trace(const std::string& format, Args&&... args) {
log(level::trace, format, std::forward<Args>(args)...);
+1 -12
View File
@@ -2,7 +2,7 @@
#include <cmath>
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace math {
@@ -369,17 +369,6 @@ struct Matrix {
return result;
}
Matrix<T, Rows, Cols> transposed() const {
static_assert(Rows == Cols);
Matrix<T, Rows, Cols> ret;
for (int i = 0; i < Rows; i++) {
for (int j = 0; j < Cols; j++) {
ret(i, j) = operator()(j, i);
}
}
return ret;
}
template <int OtherCols>
Matrix<T, Rows, OtherCols> operator*(const Matrix<T, Cols, OtherCols>& y) const {
Matrix<T, Rows, OtherCols> result;
+1 -10
View File
@@ -2,26 +2,20 @@
#include "common/versions/versions.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace REPL {
void to_json(json& j, const Config& obj) {
j = json{
{"nreplPort", obj.nrepl_port},
{"gameVersionFolder", obj.game_version_folder},
{"numConnectToTargetAttempts", obj.target_connect_attempts},
{"asmFileSearchDirs", obj.asm_file_search_dirs},
{"keybinds", obj.keybinds},
{"perGameHistory", obj.per_game_history},
{"permissiveRedefinitions", obj.permissive_redefinitions},
};
}
void from_json(const json& j, Config& obj) {
// TODO - make a camelCase variant of json_serialize/deserialize macros
if (j.contains("nreplPort")) {
j.at("nreplPort").get_to(obj.nrepl_port);
}
if (j.contains("gameVersionFolder")) {
j.at("gameVersionFolder").get_to(obj.game_version_folder);
}
@@ -61,9 +55,6 @@ void from_json(const json& j, Config& obj) {
if (j.contains("perGameHistory")) {
j.at("perGameHistory").get_to(obj.per_game_history);
}
if (j.contains("permissiveRedefinitions")) {
j.at("permissiveRedefinitions").get_to(obj.permissive_redefinitions);
}
// if there is game specific configuration, override any values we just set
if (j.contains(version_to_game_name(obj.game_version))) {
from_json(j.at(version_to_game_name(obj.game_version)), obj);
+1 -13
View File
@@ -26,14 +26,11 @@ struct KeyBind {
void to_json(json& j, const KeyBind& obj);
void from_json(const json& j, KeyBind& obj);
// TODO - per-game config
struct Config {
GameVersion game_version;
Config(GameVersion _game_version) : game_version(_game_version) {};
Config(GameVersion _game_version) : game_version(_game_version){};
// this is the default REPL configuration
int nrepl_port = 8181;
int temp_nrepl_port = -1;
std::string game_version_folder;
int target_connect_attempts = 30;
std::vector<std::string> asm_file_search_dirs = {};
@@ -48,15 +45,6 @@ struct Config {
{KeyBind::Modifier::CTRL, "B", "Displays the most recently caught backtrace", "(:di)"},
{KeyBind::Modifier::CTRL, "N", "Full build of the game", "(mi)"}};
bool per_game_history = true;
bool permissive_redefinitions = false;
std::string iso_path;
int get_nrepl_port() {
if (temp_nrepl_port != -1) {
return temp_nrepl_port;
}
return nrepl_port;
}
};
void to_json(json& j, const Config& obj);
void from_json(const json& j, Config& obj);
+1 -1
View File
@@ -4,7 +4,7 @@
#include "common/cross_sockets/XSocket.h"
#include "common/versions/versions.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#ifdef _WIN32
#define NOMINMAX
+34 -61
View File
@@ -4,7 +4,7 @@
#include "common/cross_sockets/XSocket.h"
#include "common/versions/versions.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
#ifdef _WIN32
#define NOMINMAX
@@ -16,6 +16,10 @@
#include "common/log/log.h"
// clang-format on
// TODO - basically REPL to listen and inject commands into a running REPL
// - we will need a C++ side client as well which will let us communicate with the repl via for
// example, ImgUI
//
// TODO - The server also needs to eventually return the result of the evaluation
ReplServer::~ReplServer() {
@@ -27,18 +31,7 @@ ReplServer::~ReplServer() {
void ReplServer::post_init() {
// Add the listening socket to our set of sockets
lg::debug("[nREPL:{}:{}] awaiting connections", tcp_port, listening_socket);
}
void ReplServer::error_response(int socket, const std::string& error) {
std::string msg = fmt::format("[ERROR]: {}", error);
auto resp = write_to_socket(socket, msg.c_str(), msg.size());
if (resp == -1) {
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr),
ntohs(addr.sin_port), socket);
close_socket(socket);
client_sockets.erase(socket);
}
lg::info("[nREPL:{}:{}] awaiting connections", tcp_port, listening_socket);
}
void ReplServer::ping_response(int socket) {
@@ -59,6 +52,7 @@ std::optional<std::string> ReplServer::get_msg() {
// Add the server's main listening socket (where we accept clients from)
FD_SET(listening_socket, &read_sockets);
int max_sd = listening_socket;
for (const int& sock : client_sockets) {
if (sock > max_sd) {
@@ -70,11 +64,12 @@ std::optional<std::string> ReplServer::get_msg() {
}
// Wait for activity on _something_, with a timeout so we don't get stuck here on exit.
struct timeval timeout = {0, 100000};
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
auto activity = select(max_sd + 1, &read_sockets, NULL, NULL, &timeout);
if (activity < 0 && errno != EINTR) {
lg::error("[nREPL:{}] select error, returned: {}, errno: {}", tcp_port, activity,
strerror(errno));
if (activity < 0) { // TODO - || error!
return std::nullopt;
}
@@ -83,46 +78,44 @@ std::optional<std::string> ReplServer::get_msg() {
socklen_t addr_len = sizeof(addr);
auto new_socket = accept_socket(listening_socket, (sockaddr*)&addr, &addr_len);
if (new_socket < 0) {
if (new_socket != -1) {
lg::error("[nREPL:{}] accept error, returned: {}, errono: {}", tcp_port, new_socket,
strerror(errno));
}
// TODO - handle error
} else {
lg::info("[nREPL:{}]: New socket connection: {}:{}:{}", tcp_port, address_to_string(addr),
ntohs(addr.sin_port), new_socket);
// Say hello
ping_response(new_socket);
// Track the new socket
if ((int)client_sockets.size() < max_clients) {
client_sockets.insert(new_socket);
} else {
// Respond with NO and close the socket
lg::warn("[nREPL:{}]: Maximum clients reached. Rejecting connection.", tcp_port);
error_response(new_socket, "Maximum clients reached. Rejecting connection.");
close_socket(new_socket);
// TODO - Respond with NO
}
}
}
// Check all clients for activity
for (auto it = client_sockets.begin(); it != client_sockets.end();) {
int sock = *it;
// otherwise (and no matter what) check all the clients to see if they have sent us anything
// else its some IO operation on some other socket
//
// RACE - the first client wins
// TODO - there are ways to do this with iterators but, couldn't figure it out!
std::vector<int> sockets_to_scan(client_sockets.begin(), client_sockets.end());
for (const int& sock : sockets_to_scan) {
if (FD_ISSET(sock, &read_sockets)) {
// Attempt to read a header
// TODO - should this be in a loop?
auto req_bytes = read_from_socket(sock, header_buffer.data(), header_buffer.size());
if (req_bytes <= 0) {
if (req_bytes == 0) {
// Socket disconnected
// TODO - add a queue of messages in the REPL::Wrapper so we can print _BEFORE_ the prompt
// is output
if (req_bytes == 0) {
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr));
} else {
lg::warn("[nREPL:{}] Error reading from socket on {}: {}", tcp_port,
address_to_string(addr), strerror(errno));
}
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr),
ntohs(addr.sin_port), sock);
// Cleanup the socket and remove it from our set
close_socket(sock);
it = client_sockets.erase(it); // Erase and move to the next element
continue;
client_sockets.erase(sock);
} else {
// Otherwise, process the message
auto* header = (ReplServerHeader*)(header_buffer.data());
@@ -130,12 +123,7 @@ std::optional<std::string> ReplServer::get_msg() {
int expected_size = header->length;
int got = 0;
int tries = 0;
bool skip_to_next_socket = false;
while (got < expected_size) {
if (want_exit_callback()) {
lg::warn("[nREPL:{}] Terminating nREPL early", tcp_port);
return std::nullopt;
}
tries++;
if (tries > 100) {
break;
@@ -147,25 +135,11 @@ std::optional<std::string> ReplServer::get_msg() {
tcp_port, got, expected_size, buffer.size());
return std::nullopt;
}
auto bytes_read = read_from_socket(sock, buffer.data() + got, expected_size - got);
if (bytes_read <= 0) {
if (bytes_read == 0) {
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr));
} else {
lg::warn("[nREPL:{}] Error reading from socket on {}: {}", tcp_port,
address_to_string(addr), strerror(errno));
}
close_socket(sock);
it = client_sockets.erase(it); // Erase and move to the next element
skip_to_next_socket = true;
break;
auto x = read_from_socket(sock, buffer.data() + got, expected_size - got);
if (want_exit_callback()) {
return std::nullopt;
}
got += bytes_read;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
if (skip_to_next_socket) {
continue;
got += x > 0 ? x : 0;
}
switch (header->type) {
@@ -179,7 +153,6 @@ std::optional<std::string> ReplServer::get_msg() {
}
}
}
++it;
}
return std::nullopt;
}
-1
View File
@@ -27,6 +27,5 @@ class ReplServer : public XSocketServer {
fd_set read_sockets;
std::set<int> client_sockets = {};
void error_response(int socket, const std::string& error);
void ping_response(int socket);
};
-304
View File
@@ -1,304 +0,0 @@
#include "repl_wrapper.h"
#include "common/util/FileUtil.h"
#include "common/util/json_util.h"
#include "common/util/string_util.h"
#include "common/versions/versions.h"
#include "fmt/color.h"
#include "fmt/format.h"
#include "fmt/ranges.h"
#include "third-party/replxx/include/replxx.hxx"
namespace REPL {
void Wrapper::clear_screen() {
repl.clear_screen();
}
void Wrapper::print_welcome_message(const std::vector<std::string>& loaded_projects) {
std::string message;
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " ..:::::..\n");
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .:-----------:.\n");
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-----.");
message += fmt::format(fmt::emphasis::bold, " Welcome to OpenGOAL {}.{} [{}]",
versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR,
fmt::format(fg(fmt::color::gray), "{}", build_revision()));
if (!username.empty() && username != "#f" && username != "unknown") {
message += fmt::format(fg(fmt::color::light_green), " {}", username);
}
message += "!\n";
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .---.");
if (repl_config.game_version == GameVersion::Jak1) {
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::orange), "jak1"));
} else if (repl_config.game_version == GameVersion::Jak2) {
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::purple), "jak2"));
} else if (repl_config.game_version == GameVersion::Jak3) {
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::gold), "jak3"));
} else {
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::magenta), "jakx"));
}
const auto loaded_projects_str = fmt::format("{}", fmt::join(loaded_projects, ","));
message += fmt::format(fg(fmt::color::gray), "{}\n", loaded_projects_str);
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " . --- .");
message +=
fmt::format(" Project Path: {}\n",
fmt::styled(file_util::get_jak_project_dir().string(), fg(fmt::color::gray)));
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " - :===: -");
std::string effective_iso_path;
if (repl_config.iso_path.empty()) {
effective_iso_path = file_util::get_file_path({"iso_data"});
} else {
effective_iso_path = repl_config.iso_path;
}
message +=
fmt::format(" ISO Data Path: {}\n", fmt::styled(effective_iso_path, fg(fmt::color::gray)));
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " --. .--: :--. .--");
message += " nREPL:";
if (!nrepl_alive) {
message += fmt::format(fg(fmt::color::red), "DISABLED\n");
} else {
message += fmt::format(fg(fmt::color::light_green), " Listening on {}\n",
repl_config.get_nrepl_port());
}
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .=======. =======.");
message += " Source File Search Dirs: ";
const auto search_dir_string =
fmt::format("{}", fmt::join(repl_config.asm_file_search_dirs, ","));
message += fmt::format("[{}]\n", fmt::styled(search_dir_string, fg(fmt::color::gray)));
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-=====-. .-=====-");
message += fmt::format(" {} or {} for basic help and usage\n",
fmt::format(fg(fmt::color::cyan), "(repl-help)"),
fmt::format(fg(fmt::color::cyan), "(repl-keybinds)"));
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-===========-.");
message +=
fmt::format(" {} to connect to the game\n", fmt::format(fg(fmt::color::cyan), "(lt)"));
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-===-.");
message += fmt::format(" {} to recompile the active project.\n",
fmt::format(fg(fmt::color::cyan), "(mi)"));
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .\n");
fmt::print("{}", message);
}
void Wrapper::print_to_repl(const std::string& str) {
repl.print(str.data());
}
void Wrapper::set_history_max_size(size_t len) {
repl.set_max_history_size(len);
}
const char* Wrapper::readline(const std::string& prompt) {
return repl.input(prompt);
}
void Wrapper::add_to_history(const std::string& line) {
repl.history_add(line);
}
void Wrapper::save_history() {
fs::path path;
if (repl_config.per_game_history) {
path = file_util::get_user_config_dir() / game_version_names[repl_config.game_version] /
".opengoal.repl.history";
} else {
path = file_util::get_user_config_dir() / ".opengoal.repl.history";
}
file_util::create_dir_if_needed_for_file(path.string());
repl.history_save(path.string());
}
void Wrapper::load_history() {
fs::path path;
if (repl_config.per_game_history) {
path = file_util::get_user_config_dir() / game_version_names[repl_config.game_version] /
".opengoal.repl.history";
} else {
path = file_util::get_user_config_dir() / ".opengoal.repl.history";
}
if (fs::exists(path)) {
repl.history_load(path.string());
} else {
fmt::print("Couldn't locate REPL history file at '{}'\n", path.string());
}
}
std::pair<std::string, bool> Wrapper::get_current_repl_token(std::string const& context) {
// Find the current token
std::string token = "";
for (auto c = context.crbegin(); c != context.crend(); c++) {
if (std::isspace(*c)) {
break;
} else {
token = *c + token;
}
}
// If there is a preceeding '(' remove it
if (!token.empty() && token.at(0) == '(') {
token.erase(0, 1);
return {token, true};
}
return {token, false};
}
void Wrapper::print_help_message() {
fmt::print(fmt::emphasis::bold, "\nREPL Controls:\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(:clear)\n");
fmt::print(" - Clear the current screen\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(e)\n");
fmt::print(" - Exit the compiler\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(lt [ip-address] [port-number])\n");
fmt::print(
" - Connect the listener to a running target. The IP address defaults to `127.0.0.1` and the "
"port to `8112`\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(r [ip-address] [port-number])\n");
fmt::print(
" - Attempt to reset the target and reconnect. After this, the target will have nothing "
"loaded.\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(:status)\n");
fmt::print(" - Send a ping-like message to the target. Requires the target to be connected\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(shutdown-target)\n");
fmt::print(" - If the target is connected, make it exit\n");
fmt::print(fmt::emphasis::bold, "\nCompiling & Building:\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(mi)\n");
fmt::print(" - Build entire game\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(mng)\n");
fmt::print(" - Build game engine\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(m \"filename\")\n");
fmt::print(" - Compile an OpenGOAL source file\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(ml \"filename\")\n");
fmt::print(" - Compile and Load (or reload) an OpenGOAL source file\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(build-kernel)\n");
fmt::print(" - Build the GOAL kernel\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(make \"file-name\")\n");
fmt::print(
" - Build a file and any out-of-date dependencies. This file must be a target in the make "
"system.\n");
fmt::print(fmt::emphasis::bold, "\nOther:\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::magenta), "(gs)\n");
fmt::print(" - Enter a GOOS REPL\n");
}
void Wrapper::print_keybind_help() {
fmt::print(fmt::emphasis::bold, "\nREPL KeyBinds:\n");
for (const auto& bind : repl_config.keybinds) {
fmt::print("{}\n", fmt::styled(bind.string(), fmt::fg(fmt::color::cyan)));
fmt::print("{}\n", fmt::styled(bind.description, fmt::fg(fmt::color::gray)));
}
}
replxx::Replxx::key_press_handler_t Wrapper::commit_text_action(std::string text_to_commit) {
return [this, text_to_commit](char32_t code) {
repl.set_state(
replxx::Replxx::State(text_to_commit.c_str(), static_cast<int>(text_to_commit.size())));
return repl.invoke(replxx::Replxx::ACTION::COMMIT_LINE, code);
};
}
void Wrapper::init_settings() {
// NOTE - a nice popular project that uses replxx
// - https://github.com/ClickHouse/ClickHouse/blob/master/base/base/ReplxxLineReader.cpp#L366
repl.set_word_break_characters(" \t");
repl.set_complete_on_empty(false);
repl.set_indent_multiline(false);
repl.enable_bracketed_paste();
// Setup default keybinds
for (const auto& bind : repl_config.keybinds) {
char32_t code;
switch (bind.modifier) {
case KeyBind::Modifier::CTRL:
code = replxx::Replxx::KEY::control(bind.key.at(0));
break;
case KeyBind::Modifier::SHIFT:
code = replxx::Replxx::KEY::shift(bind.key.at(0));
break;
case KeyBind::Modifier::META:
code = replxx::Replxx::KEY::meta(bind.key.at(0));
break;
}
repl.bind_key(code, commit_text_action(bind.command));
}
}
void Wrapper::reload_startup_file() {
startup_file = load_user_startup_file(username, repl_config.game_version);
}
std::string find_repl_username() {
// Two options - either:
// 1. look for the `user.txt` file, which should only contain the username
// 2. if this is absent AND there is a single folder inside the "user" folder, use that as the
// username
auto user_dir = file_util::get_jak_project_dir() / "goal_src" / "user";
auto dirs = file_util::find_directories_in_dir(user_dir);
if (dirs.size() == 1) {
return dirs.at(0).filename().string();
}
std::regex allowed_chars("(^[0-9a-zA-Z\\-\\.\\!\\?<>]*$)");
if (file_util::file_exists((user_dir / "user.txt").string())) {
auto text = file_util::read_text_file(user_dir / "user.txt");
text = str_util::trim(text);
if (!text.empty() && std::regex_match(text, allowed_chars)) {
return text;
}
}
return "unknown";
}
fs::path get_startup_file_path(const std::string& username, const GameVersion game_version) {
// - first check to see if there is a game version specific startup file to prefer
auto game_specific_path = file_util::get_jak_project_dir() / "goal_src" / "user" / username /
fmt::format("startup-{}.gc", version_to_game_name(game_version));
if (file_util::file_exists(game_specific_path.string())) {
return game_specific_path;
}
return file_util::get_jak_project_dir() / "goal_src" / "user" / username / "startup.gc";
}
StartupFile load_user_startup_file(const std::string& username, const GameVersion game_version) {
// Check for a `startup.gc` file, each line will be executed on the REPL on startup
auto startup_file_path = get_startup_file_path(username, game_version);
StartupFile startup_file;
if (file_util::file_exists(startup_file_path.string())) {
auto data = file_util::read_text_file(startup_file_path);
auto startup_cmds = str_util::split(data);
bool found_run_on_listen_line = false;
for (const auto& cmd : startup_cmds) {
if (found_run_on_listen_line) {
startup_file.run_after_listen.push_back(cmd);
} else {
startup_file.run_before_listen.push_back(cmd);
}
if (str_util::contains(cmd, "og:run-below-on-listen")) {
found_run_on_listen_line = true;
}
}
}
return startup_file;
}
REPL::Config load_repl_config(const std::string& username,
const GameVersion game_version,
const int nrepl_port) {
auto repl_config_path =
file_util::get_jak_project_dir() / "goal_src" / "user" / username / "repl-config.json";
REPL::Config loaded_config(game_version);
if (file_util::file_exists(repl_config_path.string())) {
try {
auto repl_config_data =
parse_commented_json(file_util::read_text_file(repl_config_path), "repl-config.json");
from_json(repl_config_data, loaded_config);
loaded_config.temp_nrepl_port = nrepl_port;
return loaded_config;
} catch (std::exception& e) {
// do nothing
}
}
loaded_config.temp_nrepl_port = nrepl_port;
return loaded_config;
}
} // namespace REPL
-65
View File
@@ -1,65 +0,0 @@
#pragma once
#include <functional>
#include <optional>
#include <string>
#include <vector>
#include "config.h"
#include "third-party/replxx/include/replxx.hxx"
namespace REPL {
struct StartupFile {
std::vector<std::string> run_before_listen = {};
std::vector<std::string> run_after_listen = {};
};
class Wrapper {
public:
std::string username;
Config repl_config;
StartupFile startup_file;
bool nrepl_alive = false;
std::vector<std::string> examples{};
std::vector<std::pair<std::string, replxx::Replxx::Color>> regex_colors{};
Wrapper(GameVersion version) : repl_config(version) {}
Wrapper(const std::string& _username,
const Config& config,
const StartupFile& startup,
bool _nrepl_alive)
: username(_username),
repl_config(config),
startup_file(startup),
nrepl_alive(_nrepl_alive) {}
replxx::Replxx& get_repl() { return repl; }
void init_settings();
void reload_startup_file();
// Functionality / Commands
void clear_screen();
void print_to_repl(const std::string& str);
void print_welcome_message(const std::vector<std::string>& loaded_projects);
void set_history_max_size(size_t len);
const char* readline(const std::string& prompt);
void add_to_history(const std::string& line);
void save_history();
void load_history();
void print_help_message();
void print_keybind_help();
std::pair<std::string, bool> get_current_repl_token(std::string const& context);
private:
replxx::Replxx repl;
replxx::Replxx::key_press_handler_t commit_text_action(std::string text_to_commit);
std::vector<REPL::KeyBind> keybindings = {};
};
std::string find_repl_username();
StartupFile load_user_startup_file(const std::string& username, const GameVersion game_version);
REPL::Config load_repl_config(const std::string& username,
const GameVersion game_version,
const int nrepl_port);
} // namespace REPL
+261
View File
@@ -0,0 +1,261 @@
#include "util.h"
#include "common/util/FileUtil.h"
#include "common/util/json_util.h"
#include "common/util/string_util.h"
#include "common/versions/versions.h"
#include "third-party/fmt/color.h"
#include "third-party/fmt/core.h"
#include "third-party/replxx/include/replxx.hxx"
// TODO - expand a list of hints (ie. a hint for defun to show at a glance how to write a function,
// or perhaps, show the docstring for the current function being used?)
namespace REPL {
void Wrapper::clear_screen() {
repl.clear_screen();
}
void Wrapper::print_welcome_message() {
// TODO - dont print on std-out
// Welcome message / brief intro for documentation
std::string ascii;
ascii += " _____ _____ _____ _____ __ \n";
ascii += "| |___ ___ ___| __| | _ | | \n";
ascii += "| | | . | -_| | | | | | | |__ \n";
ascii += "|_____| _|___|_|_|_____|_____|__|__|_____|\n";
ascii += " |_| \n";
fmt::print(fmt::emphasis::bold | fg(fmt::color::orange), ascii);
fmt::print("Welcome to OpenGOAL {}.{}!\n", versions::GOAL_VERSION_MAJOR,
versions::GOAL_VERSION_MINOR);
fmt::print("Run {} or {} for help with common commands and REPL usage.\n",
fmt::styled("(repl-help)", fmt::emphasis::bold | fg(fmt::color::cyan)),
fmt::styled("(repl-keybinds)", fmt::emphasis::bold | fg(fmt::color::cyan)));
fmt::print("Run ");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(lt)");
fmt::print(" to connect to the local target.\n");
fmt::print("Run ");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(mi)");
fmt::print(" to rebuild the entire game.\n\n");
}
void Wrapper::print_to_repl(const std::string& str) {
repl.print(str.data());
}
void Wrapper::set_history_max_size(size_t len) {
repl.set_max_history_size(len);
}
const char* Wrapper::readline(const std::string& prompt) {
return repl.input(prompt);
}
void Wrapper::add_to_history(const std::string& line) {
repl.history_add(line);
}
void Wrapper::save_history() {
fs::path path;
if (repl_config.per_game_history) {
path = file_util::get_user_config_dir() / game_version_names[repl_config.game_version] /
".opengoal.repl.history";
} else {
path = file_util::get_user_config_dir() / ".opengoal.repl.history";
}
file_util::create_dir_if_needed_for_file(path.string());
repl.history_save(path.string());
}
void Wrapper::load_history() {
fs::path path;
if (repl_config.per_game_history) {
path = file_util::get_user_config_dir() / game_version_names[repl_config.game_version] /
".opengoal.repl.history";
} else {
path = file_util::get_user_config_dir() / ".opengoal.repl.history";
}
if (fs::exists(path)) {
repl.history_load(path.string());
} else {
fmt::print("Couldn't locate REPL history file at '{}'\n", path.string());
}
}
std::pair<std::string, bool> Wrapper::get_current_repl_token(std::string const& context) {
// Find the current token
std::string token = "";
for (auto c = context.crbegin(); c != context.crend(); c++) {
if (std::isspace(*c)) {
break;
} else {
token = *c + token;
}
}
// If there is a preceeding '(' remove it
if (!token.empty() && token.at(0) == '(') {
token.erase(0, 1);
return {token, true};
}
return {token, false};
}
void Wrapper::print_help_message() {
fmt::print(fmt::emphasis::bold, "\nREPL Controls:\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(:clear)\n");
fmt::print(" - Clear the current screen\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(e)\n");
fmt::print(" - Exit the compiler\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(lt [ip-address] [port-number])\n");
fmt::print(
" - Connect the listener to a running target. The IP address defaults to `127.0.0.1` and the "
"port to `8112`\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(r [ip-address] [port-number])\n");
fmt::print(
" - Attempt to reset the target and reconnect. After this, the target will have nothing "
"loaded.\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(:status)\n");
fmt::print(" - Send a ping-like message to the target. Requires the target to be connected\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(shutdown-target)\n");
fmt::print(" - If the target is connected, make it exit\n");
fmt::print(fmt::emphasis::bold, "\nCompiling & Building:\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(mi)\n");
fmt::print(" - Build entire game\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(mng)\n");
fmt::print(" - Build game engine\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(m \"filename\")\n");
fmt::print(" - Compile an OpenGOAL source file\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(ml \"filename\")\n");
fmt::print(" - Compile and Load (or reload) an OpenGOAL source file\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(build-kernel)\n");
fmt::print(" - Build the GOAL kernel\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(make \"file-name\")\n");
fmt::print(
" - Build a file and any out-of-date dependencies. This file must be a target in the make "
"system.\n");
fmt::print(fmt::emphasis::bold, "\nOther:\n");
fmt::print(fmt::emphasis::bold | fg(fmt::color::magenta), "(gs)\n");
fmt::print(" - Enter a GOOS REPL\n");
}
void Wrapper::print_keybind_help() {
fmt::print(fmt::emphasis::bold, "\nREPL KeyBinds:\n");
for (const auto& bind : repl_config.keybinds) {
fmt::print("{}\n", fmt::styled(bind.string(), fmt::fg(fmt::color::cyan)));
fmt::print("{}\n", fmt::styled(bind.description, fmt::fg(fmt::color::gray)));
}
}
replxx::Replxx::key_press_handler_t Wrapper::commit_text_action(std::string text_to_commit) {
return [this, text_to_commit](char32_t code) {
repl.set_state(
replxx::Replxx::State(text_to_commit.c_str(), static_cast<int>(text_to_commit.size())));
return repl.invoke(replxx::Replxx::ACTION::COMMIT_LINE, code);
};
}
void Wrapper::init_settings() {
// NOTE - a nice popular project that uses replxx
// - https://github.com/ClickHouse/ClickHouse/blob/master/base/base/ReplxxLineReader.cpp#L366
repl.set_word_break_characters(" \t");
repl.set_complete_on_empty(false);
repl.set_indent_multiline(false);
repl.enable_bracketed_paste();
// Setup default keybinds
for (const auto& bind : repl_config.keybinds) {
char32_t code;
switch (bind.modifier) {
case KeyBind::Modifier::CTRL:
code = replxx::Replxx::KEY::control(bind.key.at(0));
break;
case KeyBind::Modifier::SHIFT:
code = replxx::Replxx::KEY::shift(bind.key.at(0));
break;
case KeyBind::Modifier::META:
code = replxx::Replxx::KEY::meta(bind.key.at(0));
break;
}
repl.bind_key(code, commit_text_action(bind.command));
}
}
void Wrapper::reload_startup_file() {
startup_file = load_user_startup_file(username, repl_config.game_version);
}
std::string find_repl_username() {
// Two options - either:
// 1. look for the `user.txt` file, which should only contain the username
// 2. if this is absent AND there is a single folder inside the "user" folder, use that as the
// username
auto user_dir = file_util::get_jak_project_dir() / "goal_src" / "user";
auto dirs = file_util::find_directories_in_dir(user_dir);
if (dirs.size() == 1) {
return dirs.at(0).filename().string();
}
std::regex allowed_chars("(^[0-9a-zA-Z\\-\\.\\!\\?<>]*$)");
if (file_util::file_exists((user_dir / "user.txt").string())) {
auto text = file_util::read_text_file(user_dir / "user.txt");
text = str_util::trim(text);
if (!text.empty() && std::regex_match(text, allowed_chars)) {
return text;
}
}
return "unknown";
}
fs::path get_startup_file_path(const std::string& username, const GameVersion game_version) {
// - first check to see if there is a game version specific startup file to prefer
auto game_specific_path = file_util::get_jak_project_dir() / "goal_src" / "user" / username /
fmt::format("startup-{}.gc", version_to_game_name(game_version));
if (file_util::file_exists(game_specific_path.string())) {
return game_specific_path;
}
return file_util::get_jak_project_dir() / "goal_src" / "user" / username / "startup.gc";
}
StartupFile load_user_startup_file(const std::string& username, const GameVersion game_version) {
// Check for a `startup.gc` file, each line will be executed on the REPL on startup
auto startup_file_path = get_startup_file_path(username, game_version);
StartupFile startup_file;
if (file_util::file_exists(startup_file_path.string())) {
auto data = file_util::read_text_file(startup_file_path);
auto startup_cmds = str_util::split(data);
bool found_run_on_listen_line = false;
for (const auto& cmd : startup_cmds) {
if (found_run_on_listen_line) {
startup_file.run_after_listen.push_back(cmd);
} else {
startup_file.run_before_listen.push_back(cmd);
}
if (str_util::contains(cmd, "og:run-below-on-listen")) {
found_run_on_listen_line = true;
}
}
}
return startup_file;
}
REPL::Config load_repl_config(const std::string& username, const GameVersion game_version) {
auto repl_config_path =
file_util::get_jak_project_dir() / "goal_src" / "user" / username / "repl-config.json";
if (file_util::file_exists(repl_config_path.string())) {
try {
REPL::Config config(game_version);
auto repl_config_data =
parse_commented_json(file_util::read_text_file(repl_config_path), "repl-config.json");
from_json(repl_config_data, config);
return config;
} catch (std::exception& e) {
REPL::Config config(game_version);
}
}
return REPL::Config(game_version);
}
} // namespace REPL
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include <functional>
#include <optional>
#include <string>
#include <vector>
#include "config.h"
#include "third-party/replxx/include/replxx.hxx"
namespace REPL {
struct StartupFile {
std::vector<std::string> run_before_listen = {};
std::vector<std::string> run_after_listen = {};
};
class Wrapper {
replxx::Replxx repl;
public:
std::string username;
Config repl_config;
StartupFile startup_file;
std::vector<std::string> examples{};
std::vector<std::pair<std::string, replxx::Replxx::Color>> regex_colors{};
Wrapper(GameVersion version) : repl_config(version) {}
Wrapper(const std::string& _username, const Config& config, const StartupFile& startup)
: username(_username), repl_config(config), startup_file(startup) {}
replxx::Replxx& get_repl() { return repl; }
void init_settings();
void reload_startup_file();
// Functionality / Commands
void clear_screen();
void print_to_repl(const std::string& str);
void print_welcome_message();
void set_history_max_size(size_t len);
const char* readline(const std::string& prompt);
void add_to_history(const std::string& line);
void save_history();
void load_history();
void print_help_message();
void print_keybind_help();
std::pair<std::string, bool> get_current_repl_token(std::string const& context);
private:
replxx::Replxx::key_press_handler_t commit_text_action(std::string text_to_commit);
std::vector<REPL::KeyBind> keybindings = {};
};
std::string find_repl_username();
StartupFile load_user_startup_file(const std::string& username, const GameVersion game_version);
REPL::Config load_repl_config(const std::string& username, const GameVersion game_version);
} // namespace REPL
+3 -7
View File
@@ -5,7 +5,7 @@
#include "common/util/FileUtil.h"
#include "common/util/json_util.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
void open_subtitle_project(const std::string& project_kind,
const std::string& file_path,
@@ -61,10 +61,7 @@ const std::unordered_map<GameVersion, std::vector<std::string>> locale_lookup =
{GameVersion::Jak1,
{"en-US", "fr-FR", "de-DE", "es-ES", "it-IT", "jp-JP", "en-GB", "pt-PT", "fi-FI", "sv-SE",
"da-DK", "no-NO", "nl-NL", "pt-BR", "hu-HU", "ca-ES", "is-IS"}},
{GameVersion::Jak2, {"en-US", "fr-FR", "de-DE", "es-ES", "it-IT", "jp-JP", "ko-KR", "en-GB"}},
{GameVersion::Jak3,
{"en-US", "fr-FR", "de-DE", "es-ES", "it-IT", "cm-CM", "jp-JP", "ko-KR", "ru-RU", "pt-PT",
"nl-NL", "en-GB", "fi-FI"}}};
{GameVersion::Jak2, {"en-US", "fr-FR", "de-DE", "es-ES", "it-IT", "jp-JP", "ko-KR", "en-GB"}}};
std::string lookup_locale_code(const GameVersion game_version, const int language_id) {
if (locale_lookup.find(game_version) == locale_lookup.end() ||
@@ -76,8 +73,7 @@ std::string lookup_locale_code(const GameVersion game_version, const int languag
const std::unordered_map<GameVersion, std::vector<int>> language_ids_with_audio = {
{GameVersion::Jak1, {0, 1, 2, 3, 4, 5, 6}},
{GameVersion::Jak2, {0, 1, 2, 3, 4, 5, 6, 7}},
{GameVersion::Jak3, {0, 1, 2, 3, 4, 5, 11}}};
{GameVersion::Jak2, {0, 1, 2, 3, 4, 5, 6, 7}}};
bool dump_language_with_duplicates_from_base(const GameVersion game_version,
const int language_id) {
@@ -5,7 +5,7 @@
#include "subtitles_v2.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
void to_json(json& j, const SubtitleCutsceneLineMetadataV1& obj) {
json_serialize(frame_start);
@@ -5,7 +5,7 @@
#include "subtitles_v1.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
void to_json(json& j, const SubtitleLineMetadata& obj) {
json_serialize(frame_start);
@@ -89,60 +89,7 @@ const std::unordered_map<std::string, u16> jak2_speaker_name_to_enum_val = {
{"citizen-male", 31},
{"citizen-female", 32},
{"oracle", 33},
{"precursor", 34},
{"metalkor-before-consite", 35},
{"metalkor-intro", 36}};
// matches enum in `subtitle3-h.gc` with "none" (first) and "max" (last and removed)
const std::unordered_map<std::string, u16> jak3_speaker_name_to_enum_val = {
{"none", 0},
{"jak", 1},
{"darkjak", 2},
{"daxter", 3},
{"pecker", 4},
{"ashelin", 5},
{"veger", 6},
{"samos", 7},
{"damas", 8},
{"kleiver", 9},
{"seem", 10},
{"errol", 11},
{"errol-hologram", 12},
{"sig", 13},
{"torn", 14},
{"tess", 15},
{"guard", 16},
{"guard-a", 17},
{"guard-b", 18},
{"keira", 19},
{"vin", 20},
{"onin", 21},
{"jinx", 22},
{"wastelander-male", 23},
{"wastelander-female", 24},
{"citizen-male", 25},
{"citizen-female", 26},
{"marauder", 27},
{"oracle", 28},
{"precursor", 29},
{"ottsel-leader", 30},
{"ottsel-surfer", 31},
{"ottsel-dummy", 32},
{"ottsel-veger", 33},
{"ottsel-tess", 34},
{"computer", 35},
{"ottsel-voice-before-outro", 36},
{"ottsel-voice", 37},
{"krew", 38},
{"baron", 39},
{"scherr", 40},
{"arey", 41},
{"baldwin", 42},
{"schimpf", 43},
{"martinsen", 44},
{"phillips", 45},
{"yates", 46},
};
{"precursor", 34}};
GameSubtitlePackage read_json_files_v2(const GameSubtitleDefinitionFile& file_info) {
GameSubtitlePackage package;
@@ -307,21 +254,11 @@ void GameSubtitleBank::add_scenes_from_files(const GameSubtitlePackage& package)
}
}
// TODO - for jak 3+, this needs some game version context info (could infer from text version)
std::vector<std::string> GameSubtitleBank::speaker_names_ordered_by_enum_value() {
// Create a temporary vector of pairs (key, value)
std::vector<std::pair<std::string, u16>> temp_vec;
switch (m_text_version) {
case GameTextVersion::JAK2:
temp_vec = {jak2_speaker_name_to_enum_val.begin(), jak2_speaker_name_to_enum_val.end()};
break;
case GameTextVersion::JAK3:
temp_vec = {jak3_speaker_name_to_enum_val.begin(), jak3_speaker_name_to_enum_val.end()};
break;
default:
throw std::runtime_error(fmt::format("GameSubtitleBank: invalid game text version {} ({})",
(int)m_text_version,
get_text_version_name(m_text_version)));
}
std::vector<std::pair<std::string, u16>> temp_vec(jak2_speaker_name_to_enum_val.begin(),
jak2_speaker_name_to_enum_val.end());
// Sort the temporary vector based on the enum value in ascending order
std::sort(temp_vec.begin(), temp_vec.end(),
[](const auto& a, const auto& b) { return a.second < b.second; });
@@ -340,25 +277,13 @@ std::vector<std::string> GameSubtitleBank::speaker_names_ordered_by_enum_value()
}
u16 GameSubtitleBank::speaker_enum_value_from_name(const std::string& speaker_id) {
std::unordered_map<std::string, u16> enum_map;
switch (m_text_version) {
case GameTextVersion::JAK2:
enum_map = jak2_speaker_name_to_enum_val;
break;
case GameTextVersion::JAK3:
enum_map = jak3_speaker_name_to_enum_val;
break;
default:
throw std::runtime_error(fmt::format("GameSubtitleBank: invalid game text version {}",
get_text_version_name(m_text_version)));
}
if (enum_map.find(speaker_id) == enum_map.end()) {
if (jak2_speaker_name_to_enum_val.find(speaker_id) == jak2_speaker_name_to_enum_val.end()) {
throw std::runtime_error(
fmt::format("'{}' speaker could not be found in the enum value mapping, update it or fix "
"the invalid speaker!",
speaker_id));
}
return u16(enum_map.at(speaker_id));
return u16(jak2_speaker_name_to_enum_val.at(speaker_id));
}
SubtitleMetadataFile dump_bank_meta_v2(const GameVersion game_version,
@@ -448,7 +373,7 @@ bool GameSubtitleDB::write_subtitle_db_to_files(const GameVersion game_version)
file_util::write_text_file(dump_path, lines_file.dump(2));
}
} catch (std::exception& ex) {
lg::error("{}", ex.what());
lg::error(ex.what());
return false;
}
return true;
@@ -2,7 +2,7 @@
#include "common/serialization/subtitles/subtitles.h"
#include "common/util/Assert.h"
#include "common/util/font/font_utils.h"
#include "common/util/FontUtils.h"
#include "common/util/json_util.h"
struct SubtitleLineMetadata {
@@ -124,6 +124,7 @@ class GameSubtitleBank {
std::optional<std::string> m_file_base_path;
std::map<std::string, GameSubtitleSceneInfo> m_base_scenes;
std::map<std::string, GameSubtitleSceneInfo> m_lang_scenes;
std::map<std::string, GameSubtitleSceneInfo> m_scenes;
bool scene_exists(const std::string& name) const { return m_scenes.find(name) != m_scenes.end(); }
GameSubtitleSceneInfo new_scene_from_meta(
+12 -37
View File
@@ -2,7 +2,6 @@
#include "common/goos/ParseHelpers.h"
#include "common/goos/Reader.h"
#include "common/util/font/font_utils_korean.h"
int64_t get_int(const goos::Object& obj) {
if (obj.is_int()) {
@@ -45,7 +44,7 @@ std::string get_string(const goos::Object& x) {
void parse_text_goal(const goos::Object& data,
GameTextDB& db,
const GameTextDefinitionFile& /*file_info*/) {
GameTextFontBank* font = nullptr;
const GameTextFontBank* font = nullptr;
std::vector<std::shared_ptr<GameTextBank>> banks;
std::string possible_group_name;
@@ -123,15 +122,9 @@ void parse_text_goal(const goos::Object& data,
if (b_i >= int(banks.size())) {
throw std::runtime_error(fmt::format("Too many strings in text id #x{:x}", id));
}
if (font->is_language_id_korean(b_i)) {
// korean changes differently!
auto line = font->convert_utf8_to_game_korean(entry.as_string()->data);
banks[b_i]->set_line(id, line);
} else {
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[b_i]->set_line(id, line);
}
b_i++;
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[b_i++]->set_line(id, line);
} else {
throw std::runtime_error(fmt::format("Non-string value in text id #x{:x}", id));
}
@@ -172,15 +165,8 @@ void parse_text_goal(const goos::Object& data,
throw std::runtime_error(fmt::format("Too many strings in text id #x{:x}", id));
}
if (font->is_language_id_korean(i)) {
// handle korean differently!
auto line = font->convert_utf8_to_game_korean(entry.as_string()->data);
banks[i]->set_line(id, line);
} else {
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[i]->set_line(id, line);
}
i++;
auto line = font->convert_utf8_to_game(entry.as_string()->data);
banks[i++]->set_line(id, line);
} else {
throw std::runtime_error(fmt::format("Non-string value in text id #x{:x}", id));
}
@@ -244,20 +230,15 @@ void parse_text_json(const nlohmann::json& json,
} else {
bank = db.bank_by_id(file_info.group_name.value(), file_info.language_id);
}
GameTextFontBank* font = get_font_bank(file_info.text_version);
const GameTextFontBank* font = get_font_bank(file_info.text_version);
// Parse the file
for (const auto& [text_id, text_value] : json.items()) {
auto line_id = std::stoi(text_id, nullptr, 16);
if (text_value.is_string()) {
// single line replacement
if (font->is_language_id_korean(file_info.language_id)) {
auto line = font->convert_utf8_to_game_korean(text_value);
bank->set_line(line_id, line);
} else {
auto line = font->convert_utf8_to_game(text_value);
bank->set_line(line_id, line);
}
auto line = font->convert_utf8_to_game(text_value);
// TODO - lint duplicate line definitions across text files
bank->set_line(line_id, line);
} else if (text_value.is_array()) {
// multi-line replacement starting from line_id
// (e.g. for Jak 1 credits, start from x0b00)
@@ -266,14 +247,8 @@ void parse_text_json(const nlohmann::json& json,
throw std::runtime_error(fmt::format(
"Non string provided for line {} / text id #x{} of _credits", idx, line_id));
}
if (font->is_language_id_korean(file_info.language_id)) {
auto line = font->convert_utf8_to_game_korean(raw_line);
bank->set_line(line_id++, line); // increment line_id
} else {
auto line = font->convert_utf8_to_game(raw_line);
bank->set_line(line_id++, line); // increment line_id
}
auto line = font->convert_utf8_to_game(raw_line);
bank->set_line(line_id++, line); // increment line_id
}
} else {
// Unexpected value type
+1 -1
View File
@@ -11,7 +11,7 @@
#include "common/goos/Object.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/font/font_utils.h"
#include "common/util/FontUtils.h"
#include "common/util/json_util.h"
#include "common/versions/versions.h"
-350
View File
@@ -150,354 +150,6 @@ constexpr int FIX_FIXED_SYM_END_OFFSET = 0xec;
} // namespace jak2_symbols
namespace jak3_symbols {
constexpr int FIX_SYM_EMPTY_CAR = -0x8;
constexpr int S7_OFF_FIX_SYM_EMPTY_PAIR = -0x6 - 1;
constexpr int FIX_SYM_EMPTY_CDR = -0x4;
constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register)
constexpr int FIX_SYM_TRUE = 0x4; // GOAL boolean #t
constexpr int FIX_SYM_FUNCTION_TYPE = 0x8;
constexpr int FIX_SYM_BASIC = 0xc;
constexpr int FIX_SYM_STRING_TYPE = 0x10;
constexpr int FIX_SYM_SYMBOL_TYPE = 0x14;
constexpr int FIX_SYM_TYPE_TYPE = 0x18;
constexpr int FIX_SYM_OBJECT_TYPE = 0x1c;
constexpr int FIX_SYM_LINK_BLOCK = 0x20;
constexpr int FIX_SYM_INTEGER = 0x24;
constexpr int FIX_SYM_SINTEGER = 0x28;
constexpr int FIX_SYM_UINTEGER = 0x2c;
constexpr int FIX_SYM_BINTEGER = 0x30;
constexpr int FIX_SYM_INT8 = 0x34;
constexpr int FIX_SYM_INT16 = 0x38;
constexpr int FIX_SYM_INT32 = 0x3c;
constexpr int FIX_SYM_INT64 = 0x40;
constexpr int FIX_SYM_INT128 = 0x44;
constexpr int FIX_SYM_UINT8 = 0x48;
constexpr int FIX_SYM_UINT16 = 0x4c;
constexpr int FIX_SYM_UINT32 = 0x50;
constexpr int FIX_SYM_UINT64 = 0x54;
constexpr int FIX_SYM_UINT128 = 0x58;
constexpr int FIX_SYM_FLOAT = 0x5c;
constexpr int FIX_SYM_PROCESS_TREE = 0x60;
constexpr int FIX_SYM_PROCESS_TYPE = 0x64;
constexpr int FIX_SYM_THREAD = 0x68;
constexpr int FIX_SYM_STRUCTURE = 0x6c;
constexpr int FIX_SYM_PAIR_TYPE = 0x70;
constexpr int FIX_SYM_POINTER = 0x74;
constexpr int FIX_SYM_NUMBER = 0x78;
constexpr int FIX_SYM_ARRAY = 0x7c;
constexpr int FIX_SYM_VU_FUNCTION = 0x80;
constexpr int FIX_SYM_CONNECTABLE = 0x84;
constexpr int FIX_SYM_STACK_FRAME = 0x88;
constexpr int FIX_SYM_FILE_STREAM = 0x8c;
constexpr int FIX_SYM_HEAP = 0x90;
constexpr int FIX_SYM_NOTHING_FUNC = 0x94;
constexpr int FIX_SYM_DELETE_BASIC = 0x98;
constexpr int FIX_SYM_STATIC = 0x9c;
constexpr int FIX_SYM_GLOBAL_HEAP = 0xa0;
constexpr int FIX_SYM_DEBUG = 0xa4;
constexpr int FIX_SYM_LOADING_LEVEL = 0xa8;
constexpr int FIX_SYM_LOADING_PACKAGE = 0xac;
constexpr int FIX_SYM_PROCESS_LEVEL_HEAP = 0xb0;
constexpr int FIX_SYM_STACK = 0xb4;
constexpr int FIX_SYM_SCRATCH = 0xb8;
constexpr int FIX_SYM_SCRATCH_TOP = 0xbc;
constexpr int FIX_SYM_ZERO_FUNC = 0xc0;
constexpr int FIX_SYM_ASIZE_OF_BASIC_FUNC = 0xc4;
constexpr int FIX_SYM_COPY_BASIC_FUNC = 0xc8; // bugged name
constexpr int FIX_SYM_LEVEL = 0xcc;
constexpr int FIX_SYM_ART_GROUP = 0xd0;
constexpr int FIX_SYM_TEXTURE_PAGE_DIR = 0xd4;
constexpr int FIX_SYM_TEXTURE_PAGE = 0xd8;
constexpr int FIX_SYM_SOUND = 0xdc;
constexpr int FIX_SYM_DGO = 0xe0;
constexpr int FIX_SYM_TOP_LEVEL = 0xe4;
constexpr int FIX_SYM_QUOTE = 0xe8;
constexpr int FIX_SYM_LISTENER_LINK_BLOCK = 0xec;
constexpr int FIX_SYM_LISTENER_FUNCTION = 0xf0;
constexpr int FIX_SYM_STACK_TOP = 0xf4;
constexpr int FIX_SYM_STACK_BASE = 0xf8;
constexpr int FIX_SYM_STACK_SIZE = 0xfc;
constexpr int FIX_SYM_KERNEL_FUNCTION = 0x100;
constexpr int FIX_SYM_KERNEL_PACKAGES = 0x104;
constexpr int FIX_SYM_KERNEL_BOOT_MESSAGE = 0x108;
constexpr int FIX_SYM_KERNEL_BOOT_MODE = 0x10c;
constexpr int FIX_SYM_KERNEL_BOOT_LEVEL = 0x110;
constexpr int FIX_SYM_KERNEL_BOOT_ART_GROUP = 0x114;
constexpr int FIX_SYM_KERNEL_DEBUG = 0x118;
constexpr int FIX_SYM_KERNEL_VERSION = 0x11c;
constexpr int FIX_SYM_KERNEL_DISPATCHER = 0x120;
constexpr int FIX_SYM_SYNC_DISPATCHER = 0x124;
constexpr int FIX_SYM_PRINT_COLLUMN = 0x128;
constexpr int FIX_SYM_DEBUG_SEGMENT = 300;
constexpr int FIX_SYM_ENABLE_METHOD_SET = 0x130;
constexpr int FIX_SYM_SQL_RESULT = 0x134;
constexpr int FIX_SYM_COLLAPSE_QUOTE = 0x138;
constexpr int FIX_SYM_LEVEL_TYPE_LIST = 0x13C;
constexpr int FIX_SYM_DECI_COUNT = 0x140;
constexpr int FIX_SYM_USER = 0x144;
constexpr int FIX_SYM_VIDEO_MODE = 0x148;
constexpr int FIX_SYM_BOOT_VIDEO_MODE = 0x14C;
constexpr int FIX_SYM_BOOT = 0x150;
constexpr int FIX_SYM_DEMO = 0x154;
constexpr int FIX_SYM_DEMO_SHARED = 0x158;
constexpr int FIX_SYM_PREVIEW = 0x15C;
constexpr int FIX_SYM_KIOSK = 0x160;
constexpr int FIX_SYM_PLAY_BOOT = 0x164;
constexpr int FIX_SYM_SIN = 0x168;
constexpr int FIX_SYM_COS = 0x16C;
constexpr int FIX_SYM_PUT_DISPLAY_ENV = 0x170;
constexpr int FIX_SYM_SYNCV = 0x174;
constexpr int FIX_SYM_SYNC_PATH = 0x178;
constexpr int FIX_SYM_RESET_PATH = 0x17C;
constexpr int FIX_SYM_RESET_GRAPH = 0x180;
constexpr int FIX_SYM_DMA_SYNC = 0x184;
constexpr int FIX_SYM_GS_PUT_IMR = 0x188;
constexpr int FIX_SYM_GS_GET_IMR = 0x18C;
constexpr int FIX_SYM_GS_STORE_IMAGE = 400;
constexpr int FIX_SYM_FLUSH_CACHE = 0x194;
constexpr int FIX_SYM_CPAD_OPEN = 0x198;
constexpr int FIX_SYM_CPAD_GET_DATA = 0x19C;
constexpr int FIX_SYM_MOUSE_GET_DATA = 0x1A0;
constexpr int FIX_SYM_KEYBD_GET_DATA = 0x1A4;
constexpr int FIX_SYM_INSTALL_HANDLER = 0x1A8;
constexpr int FIX_SYM_INSTALL_DEBUG_HANDLER = 0x1AC;
constexpr int FIX_SYM_FILE_STREAM_OPEN = 0x1B0;
constexpr int FIX_SYM_FILE_STREAM_CLOSE = 0x1B4;
constexpr int FIX_SYM_FILE_STREAM_LENGTH = 0x1B8;
constexpr int FIX_SYM_FILE_STREAM_SEEK = 0x1BC;
constexpr int FIX_SYM_FILE_STREAM_READ = 0x1C0;
constexpr int FIX_SYM_FILE_STREAM_WRITE = 0x1C4;
constexpr int FIX_SYM_SCF_GET_LANGUAGE = 0x1C8;
constexpr int FIX_SYM_SCF_GET_TIME = 0x1CC;
constexpr int FIX_SYM_SCF_GET_ASPECT = 0x1D0;
constexpr int FIX_SYM_SCF_GET_VOLUME = 0x1D4;
constexpr int FIX_SYM_SCF_GET_TERRITORY = 0x1D8;
constexpr int FIX_SYM_SCF_GET_TIMEOUT = 0x1DC;
constexpr int FIX_SYM_SCF_GET_INACTIVE_TIMEOUT = 0x1E0;
constexpr int FIX_SYM_DMA_TO_IOP = 0x1E4;
constexpr int FIX_SYM_KERNEL_SHUTDOWN = 0x1E8;
constexpr int FIX_SYM_AYBABTU = 0x1EC;
constexpr int FIX_SYM_STRING_TO_SYMBOL = 0x1F0;
constexpr int FIX_SYM_SYMBOL_TO_STRING = 500;
constexpr int FIX_SYM_PRINT = 0x1F8;
constexpr int FIX_SYM_INSPECT = 0x1FC;
constexpr int FIX_SYM_LOAD = 0x200;
constexpr int FIX_SYM_LOADB = 0x204;
constexpr int FIX_SYM_LOADO = 0x208;
constexpr int FIX_SYM_UNLOAD = 0x20C;
constexpr int FIX_SYM_FORMAT = 0x210;
constexpr int FIX_SYM_MALLOC = 0x214;
constexpr int FIX_SYM_KMALLOC = 0x218;
constexpr int FIX_SYM_KMEMOPEN = 0x21C;
constexpr int FIX_SYM_KMEMCLOSE = 0x220;
constexpr int FIX_SYM_NEW_DYNAMIC_STRUCTURE = 0x224;
constexpr int FIX_SYM_METHOD_SET = 0x228;
constexpr int FIX_SYM_LINK = 0x22C;
constexpr int FIX_SYM_LINK_BUSY = 0x230;
constexpr int FIX_SYM_LINK_RESET = 0x234;
constexpr int FIX_SYM_LINK_BEGIN = 0x238;
constexpr int FIX_SYM_LINK_RESUME = 0x23C;
constexpr int FIX_SYM_DGO_LOAD = 0x240;
constexpr int FIX_SYM_SQL_QUERY = 0x244;
constexpr int FIX_SYM_MC_RUN = 0x248;
constexpr int FIX_SYM_MC_FORMAT = 0x24C;
constexpr int FIX_SYM_MC_UNFORMAT = 0x250;
constexpr int FIX_SYM_MC_CREATE_FILE = 0x254;
constexpr int FIX_SYM_MC_SAVE = 600;
constexpr int FIX_SYM_MC_LOAD = 0x25C;
constexpr int FIX_SYM_MC_CHECK_RESULT = 0x260;
constexpr int FIX_SYM_MC_GET_SLOT_INFO = 0x264;
constexpr int FIX_SYM_MC_MAKEFILE = 0x268;
constexpr int FIX_SYM_KSET_LANGUAGE = 0x26C;
constexpr int FIX_SYM_RPC_CALL = 0x270;
constexpr int FIX_SYM_RPC_BUSY = 0x274;
constexpr int FIX_SYM_TEST_LOAD_DGO_C = 0x278;
constexpr int FIX_SYM_SYMLINK2 = 0x27c;
constexpr int FIX_SYM_SYMLINK3 = 0x280;
constexpr int FIX_SYM_ULTIMATE_MEMCPY = 0x284;
constexpr int FIX_SYM_PLAY = 0x288;
constexpr int FIX_SYM_SYMBOL_STRING = 0x28c;
constexpr int FIX_SYM_KERNEL_SYMBOL_WARNINGS = 0x290;
constexpr int FIX_SYM_NETWORK_BOOTSTRAP = 0x294;
} // namespace jak3_symbols
namespace jakx_symbols {
constexpr int FIX_SYM_EMPTY_CAR = -0x8;
constexpr int S7_OFF_FIX_SYM_EMPTY_PAIR = -0x6 - 1;
constexpr int FIX_SYM_EMPTY_CDR = -0x4;
constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register)
constexpr int FIX_SYM_TRUE = 0x4; // GOAL boolean #t
constexpr int FIX_SYM_FUNCTION_TYPE = 0x8;
constexpr int FIX_SYM_BASIC = 0xc;
constexpr int FIX_SYM_STRING_TYPE = 0x10;
constexpr int FIX_SYM_SYMBOL_TYPE = 0x14;
constexpr int FIX_SYM_TYPE_TYPE = 0x18;
constexpr int FIX_SYM_OBJECT_TYPE = 0x1c;
constexpr int FIX_SYM_LINK_BLOCK = 0x20;
constexpr int FIX_SYM_INTEGER = 0x24;
constexpr int FIX_SYM_SINTEGER = 0x28;
constexpr int FIX_SYM_UINTEGER = 0x2c;
constexpr int FIX_SYM_BINTEGER = 0x30;
constexpr int FIX_SYM_INT8 = 0x34;
constexpr int FIX_SYM_INT16 = 0x38;
constexpr int FIX_SYM_INT32 = 0x3c;
constexpr int FIX_SYM_INT64 = 0x40;
constexpr int FIX_SYM_INT128 = 0x44;
constexpr int FIX_SYM_UINT8 = 0x48;
constexpr int FIX_SYM_UINT16 = 0x4c;
constexpr int FIX_SYM_UINT32 = 0x50;
constexpr int FIX_SYM_UINT64 = 0x54;
constexpr int FIX_SYM_UINT128 = 0x58;
constexpr int FIX_SYM_FLOAT = 0x5c;
constexpr int FIX_SYM_PROCESS_TREE = 0x60;
constexpr int FIX_SYM_PROCESS_TYPE = 0x64;
constexpr int FIX_SYM_THREAD = 0x68;
constexpr int FIX_SYM_STRUCTURE = 0x6c;
constexpr int FIX_SYM_PAIR_TYPE = 0x70;
constexpr int FIX_SYM_POINTER = 0x74;
constexpr int FIX_SYM_NUMBER = 0x78;
constexpr int FIX_SYM_ARRAY = 0x7c;
constexpr int FIX_SYM_VU_FUNCTION = 0x80;
constexpr int FIX_SYM_CONNECTABLE = 0x84;
constexpr int FIX_SYM_STACK_FRAME = 0x88;
constexpr int FIX_SYM_FILE_STREAM = 0x8c;
constexpr int FIX_SYM_HEAP = 0x90;
constexpr int FIX_SYM_NOTHING_FUNC = 0x94;
constexpr int FIX_SYM_DELETE_BASIC = 0x98;
constexpr int FIX_SYM_STATIC = 0x9c;
constexpr int FIX_SYM_GLOBAL_HEAP = 0xa0;
constexpr int FIX_SYM_DEBUG = 0xa4;
constexpr int FIX_SYM_LOADING_LEVEL = 0xa8;
constexpr int FIX_SYM_LOADING_PACKAGE = 0xac;
constexpr int FIX_SYM_PROCESS_LEVEL_HEAP = 0xb0;
constexpr int FIX_SYM_STACK = 0xb4;
constexpr int FIX_SYM_SCRATCH = 0xb8;
constexpr int FIX_SYM_SCRATCH_TOP = 0xbc;
constexpr int FIX_SYM_ZERO_FUNC = 0xc0;
constexpr int FIX_SYM_ASIZE_OF_BASIC_FUNC = 0xc4;
constexpr int FIX_SYM_COPY_BASIC_FUNC = 0xc8; // bugged name
constexpr int FIX_SYM_LEVEL = 0xcc;
constexpr int FIX_SYM_ART_GROUP = 0xd0;
constexpr int FIX_SYM_TEXTURE_PAGE_DIR = 0xd4;
constexpr int FIX_SYM_TEXTURE_PAGE = 0xd8;
constexpr int FIX_SYM_SOUND = 0xdc;
constexpr int FIX_SYM_DGO = 0xe0;
constexpr int FIX_SYM_TOP_LEVEL = 0xe4;
constexpr int FIX_SYM_QUOTE = 0xe8;
constexpr int FIX_SYM_LISTENER_LINK_BLOCK = 0xec;
constexpr int FIX_SYM_LISTENER_FUNCTION = 0xf0;
constexpr int FIX_SYM_STACK_TOP = 0xf4;
constexpr int FIX_SYM_STACK_BASE = 0xf8;
constexpr int FIX_SYM_STACK_SIZE = 0xfc;
constexpr int FIX_SYM_KERNEL_FUNCTION = 0x100;
constexpr int FIX_SYM_KERNEL_PACKAGES = 0x104;
constexpr int FIX_SYM_KERNEL_BOOT_MESSAGE = 0x108;
constexpr int FIX_SYM_KERNEL_BOOT_MODE = 0x10c;
constexpr int FIX_SYM_KERNEL_BOOT_LEVEL = 0x110;
constexpr int FIX_SYM_KERNEL_BOOT_ART_GROUP = 0x114;
constexpr int FIX_SYM_KERNEL_DEBUG = 0x118;
constexpr int FIX_SYM_KERNEL_VERSION = 0x11c;
constexpr int FIX_SYM_KERNEL_DISPATCHER = 0x120;
constexpr int FIX_SYM_SYNC_DISPATCHER = 0x124;
constexpr int FIX_SYM_PRINT_COLLUMN = 0x128;
constexpr int FIX_SYM_DEBUG_SEGMENT = 300;
constexpr int FIX_SYM_ENABLE_METHOD_SET = 0x130;
constexpr int FIX_SYM_SQL_RESULT = 0x134;
constexpr int FIX_SYM_COLLAPSE_QUOTE = 0x138;
constexpr int FIX_SYM_LEVEL_TYPE_LIST = 0x13C;
constexpr int FIX_SYM_DECI_COUNT = 0x140;
constexpr int FIX_SYM_USER = 0x144;
constexpr int FIX_SYM_VIDEO_MODE = 0x148;
constexpr int FIX_SYM_BOOT_VIDEO_MODE = 0x14C;
constexpr int FIX_SYM_BOOT = 0x150;
constexpr int FIX_SYM_DEMO = 0x154;
constexpr int FIX_SYM_DEMO_SHARED = 0x158;
constexpr int FIX_SYM_PREVIEW = 0x15C;
constexpr int FIX_SYM_KIOSK = 0x160;
constexpr int FIX_SYM_PLAY_BOOT = 0x164;
constexpr int FIX_SYM_SIN = 0x168;
constexpr int FIX_SYM_COS = 0x16C;
constexpr int FIX_SYM_PUT_DISPLAY_ENV = 0x170;
constexpr int FIX_SYM_SYNCV = 0x174;
constexpr int FIX_SYM_SYNC_PATH = 0x178;
constexpr int FIX_SYM_RESET_PATH = 0x17C;
constexpr int FIX_SYM_RESET_GRAPH = 0x180;
constexpr int FIX_SYM_DMA_SYNC = 0x184;
constexpr int FIX_SYM_GS_PUT_IMR = 0x188;
constexpr int FIX_SYM_GS_GET_IMR = 0x18C;
constexpr int FIX_SYM_GS_STORE_IMAGE = 400;
constexpr int FIX_SYM_FLUSH_CACHE = 0x194;
constexpr int FIX_SYM_CPAD_OPEN = 0x198;
constexpr int FIX_SYM_CPAD_GET_DATA = 0x19C;
constexpr int FIX_SYM_MOUSE_GET_DATA = 0x1A0;
constexpr int FIX_SYM_KEYBD_GET_DATA = 0x1A4;
constexpr int FIX_SYM_INSTALL_HANDLER = 0x1A8;
constexpr int FIX_SYM_INSTALL_DEBUG_HANDLER = 0x1AC;
constexpr int FIX_SYM_FILE_STREAM_OPEN = 0x1B0;
constexpr int FIX_SYM_FILE_STREAM_CLOSE = 0x1B4;
constexpr int FIX_SYM_FILE_STREAM_LENGTH = 0x1B8;
constexpr int FIX_SYM_FILE_STREAM_SEEK = 0x1BC;
constexpr int FIX_SYM_FILE_STREAM_READ = 0x1C0;
constexpr int FIX_SYM_FILE_STREAM_WRITE = 0x1C4;
constexpr int FIX_SYM_SCF_GET_LANGUAGE = 0x1C8;
constexpr int FIX_SYM_SCF_GET_TIME = 0x1CC;
constexpr int FIX_SYM_SCF_GET_ASPECT = 0x1D0;
constexpr int FIX_SYM_SCF_GET_VOLUME = 0x1D4;
constexpr int FIX_SYM_SCF_GET_TERRITORY = 0x1D8;
constexpr int FIX_SYM_SCF_GET_TIMEOUT = 0x1DC;
constexpr int FIX_SYM_SCF_GET_INACTIVE_TIMEOUT = 0x1E0;
constexpr int FIX_SYM_DMA_TO_IOP = 0x1E4;
constexpr int FIX_SYM_KERNEL_SHUTDOWN = 0x1E8;
constexpr int FIX_SYM_AYBABTU = 0x1EC;
constexpr int FIX_SYM_STRING_TO_SYMBOL = 0x1F0;
constexpr int FIX_SYM_SYMBOL_TO_STRING = 500;
constexpr int FIX_SYM_PRINT = 0x1F8;
constexpr int FIX_SYM_INSPECT = 0x1FC;
constexpr int FIX_SYM_LOAD = 0x200;
constexpr int FIX_SYM_LOADB = 0x204;
constexpr int FIX_SYM_LOADO = 0x208;
constexpr int FIX_SYM_UNLOAD = 0x20C;
constexpr int FIX_SYM_FORMAT = 0x210;
constexpr int FIX_SYM_MALLOC = 0x214;
constexpr int FIX_SYM_KMALLOC = 0x218;
constexpr int FIX_SYM_KMEMOPEN = 0x21C;
constexpr int FIX_SYM_KMEMCLOSE = 0x220;
constexpr int FIX_SYM_NEW_DYNAMIC_STRUCTURE = 0x224;
constexpr int FIX_SYM_METHOD_SET = 0x228;
constexpr int FIX_SYM_LINK = 0x22C;
constexpr int FIX_SYM_LINK_BUSY = 0x230;
constexpr int FIX_SYM_LINK_RESET = 0x234;
constexpr int FIX_SYM_LINK_BEGIN = 0x238;
constexpr int FIX_SYM_LINK_RESUME = 0x23C;
constexpr int FIX_SYM_DGO_LOAD = 0x240;
constexpr int FIX_SYM_SQL_QUERY = 0x244;
constexpr int FIX_SYM_MC_RUN = 0x248;
constexpr int FIX_SYM_MC_FORMAT = 0x24C;
constexpr int FIX_SYM_MC_UNFORMAT = 0x250;
constexpr int FIX_SYM_MC_CREATE_FILE = 0x254;
constexpr int FIX_SYM_MC_SAVE = 600;
constexpr int FIX_SYM_MC_LOAD = 0x25C;
constexpr int FIX_SYM_MC_CHECK_RESULT = 0x260;
constexpr int FIX_SYM_MC_GET_SLOT_INFO = 0x264;
constexpr int FIX_SYM_MC_MAKEFILE = 0x268;
constexpr int FIX_SYM_KSET_LANGUAGE = 0x26C;
constexpr int FIX_SYM_RPC_CALL = 0x270;
constexpr int FIX_SYM_RPC_BUSY = 0x274;
constexpr int FIX_SYM_TEST_LOAD_DGO_C = 0x278;
constexpr int FIX_SYM_SYMLINK2 = 0x27c;
constexpr int FIX_SYM_SYMLINK3 = 0x280;
constexpr int FIX_SYM_ULTIMATE_MEMCPY = 0x284;
constexpr int FIX_SYM_PLAY = 0x288;
constexpr int FIX_SYM_SYMBOL_STRING = 0x28c;
constexpr int FIX_SYM_KERNEL_SYMBOL_WARNINGS = 0x290;
constexpr int FIX_SYM_NETWORK_BOOTSTRAP = 0x294;
} // namespace jakx_symbols
constexpr int false_symbol_offset() {
return jak1_symbols::FIX_SYM_FALSE;
}
@@ -508,7 +160,6 @@ constexpr int true_symbol_offset(GameVersion version) {
return jak1_symbols::FIX_SYM_TRUE;
case GameVersion::Jak2:
case GameVersion::Jak3:
case GameVersion::JakX:
return jak2_symbols::FIX_SYM_TRUE;
}
}
@@ -519,7 +170,6 @@ constexpr int empty_pair_offset_from_s7(GameVersion version) {
return jak1_symbols::FIX_SYM_EMPTY_PAIR;
case GameVersion::Jak2:
case GameVersion::Jak3:
case GameVersion::JakX:
// minus 1 for the symbol table pointer's offset.
return jak2_symbols::S7_OFF_FIX_SYM_EMPTY_PAIR;
}
+1 -147
View File
@@ -42,153 +42,7 @@ std::vector<std::string> jak2_slots = {
"cas-toxic-slime-scroll-dest",
};
std::vector<std::string> jak3_slots = {
"skull-gem-dest",
"jakc-arm",
"jakc-eyebrow",
"jakc-face",
"jakc-finger",
"jakc-hair",
"jakchires-arm",
"jakchires-eye",
"jakchires-eyebrow",
"jakchires-eyelid",
"jakchires-facelft",
"jakchires-facert",
"jakchires-hair",
// default-water
"bomb-gradient",
"blue-beam-dest",
"lightjak-wings",
"mushroom-dest",
// default-warp
"shield-env-rim-dest",
// templea-water
"templea-waterfall-dest",
// templea-warp
"holograph-env-rim-dest",
// hanga-sprite
"glider-ring-dest2",
"glider-ring-dest",
// foresta-water
"fora-water-dest",
"fora-waterfall-01-dest",
"fora-water-wave-01-dest",
// forestb-water
"forb-water-dest",
"forb-waterfall-01-dest",
"forb-water-wave-01-dest",
// lforplnt-pris
"mh-gem-dest",
// lmhcitya-tfrag
"mhcitya-base-goo-01-dest",
// lmhcityb-tfrag
"mhcityb-base-goo-01-dest",
// mhcitya-pris
// "mhcity-de-door-skin-01-dest",
// templec-water
"tplc-water-dest",
// sewc-water
"sewer-water-01-c-dest",
"sewer-waterfall-01-c-dest",
"sewer-waterfall-02-c-dest",
"sewer-water-wave-01-c-dest",
"sewer-water-highlight-01-c-dest",
// sewd-water
"sewer-water-01-d-dest",
"sewer-waterfall-02-d-dest",
"sewer-water-highlight-01-d-dest",
"sewer-water-wave-01-d-dest",
"sewer-water-wave-02-d-dest",
"sewer-water-still-01-d-dest",
// sewe-water
"sewer-water-01-e-dest",
"sewer-waterfall-01-e-dest",
"sewer-waterfall-02-e-dest",
"sewer-water-highlight-01-e-dest",
// sewg-water
"sewer-water-01-g-dest",
"sewer-waterfall-02-g-dest",
"sewer-water-wave-01-g-dest",
// sewh-water
"sewer-water-01-h-dest",
"sewer-waterfall-02-h-dest",
"sewer-water-wave-02-h-dest",
"sewer-watefall-froth-01-h-dest",
// sewi-water
"sewer-water-still-01-i-dest",
"sewer-waterfall-02-i-dest",
"sewer-water-wave-01-i-dest",
// sewj-water
"sewer-waterfall-02-j-dest",
"sewer-watefall-froth-01-j-dest",
// sewl-water
"sewer-waterfall-02-l-dest",
"sewer-watefall-froth-01-l-dest",
// sewm-water
"sewer-water-01-m-dest",
"sewer-waterfall-01-m-dest",
"sewer-waterfall-02-m-dest",
"sewer-water-highlight-01-m-dest",
"sewer-water-wave-01-m-dest",
"sewer-water-still-01-m-dest",
"sewer-watefall-froth-01-m-dest",
// sewn-water
"sewer-waterfall-01-n-dest",
"sewer-waterfall-02-n-dest",
// "sewer-water-highlight-01-n-dest",
"sewer-water-wave-01-n-dest",
"sewer-water-still-01-n-dest",
// hanga-water
// "des-thermal-01-dest",
// desresc-warp
"sat-shield-dest",
// security
"security-env-dest",
"security-dot-dest",
// lgunnorm-water
"kg-target-c-forcefield-01-dest",
// templex-water
"temple-waterfall-dest",
// desertd-water
"des-waterfall-dest",
// towerb-water
"tow-energy-bridge-dest",
// factoryb-water
"hemi-gradient-dest",
"hemi-gradient-flames-dest",
// factoryc-alpha
"facc-convey-dest",
"facc-convey-02-dest",
// waspal-water
"waspala-water-dest",
"waspala-waterfall-dest",
// rubblea-water
"rub-water-dest",
// rubblea2-water
"rub-water-desta2",
// rubbleb-water
"rub-water-destb",
// rubblec-water
"rub-water-destc",
// nstb-quicksand
"nstb-quicksand-dest",
// ctyslumb-water
"ctyslumb-water-dest",
"ctyslumb-fountain-fall-dest",
// ctyslumc-water
"ctyslumc-water-dest",
"ctyslumc-fountain-fall-dest",
// mined-tfrag
"mined-pillar-side-dest",
"mined-pillar-top2side-dest",
"mined-pillar-top-dest",
// volcanoa-alpha
"vola-lava-01-dest",
"vola-lava-fall-dest",
// wasstada-alpha
"wstd-lava-base-dest",
};
std::vector<std::string> jak3_slots = {};
} // namespace
+1 -1
View File
@@ -10,7 +10,7 @@
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace {
std::string reg_kind_to_string(RegClass kind) {
-4
View File
@@ -28,12 +28,8 @@ struct MethodInfo {
std::string name;
TypeSpec type;
std::string defined_in_type;
// TODO - this might be redundant, but preventing an unknown breaking change, clean it up later
std::string type_name;
bool no_virtual = false;
// TODO - this is no longer used anymore...but was not removed from th4e struct?
bool overrides_parent = false;
// TODO - this is no longer used anymore...but was not removed from th4e struct?
bool only_overrides_docstring = false;
std::optional<std::string> docstring;
std::optional<std::string> overlay_name;
+1 -1
View File
@@ -9,7 +9,7 @@
#include "common/log/log.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace {
// debug prints for the reverse lookup
+1 -1
View File
@@ -7,7 +7,7 @@
#include <stdexcept>
#include "fmt/format.h"
#include "third-party/fmt/core.h"
bool TypeTag::operator==(const TypeTag& other) const {
return name == other.name && value == other.value;
-2
View File
@@ -124,12 +124,10 @@ class TypeSpec {
ASSERT(m_arguments);
return m_arguments->at(idx);
}
TypeSpec& get_arg(int idx) {
ASSERT(m_arguments);
return m_arguments->at(idx);
}
const TypeSpec& last_arg() const {
ASSERT(m_arguments);
ASSERT(!m_arguments->empty());
+10 -36
View File
@@ -14,8 +14,8 @@
#include "common/util/Assert.h"
#include "common/util/math_util.h"
#include "fmt/color.h"
#include "fmt/format.h"
#include "third-party/fmt/color.h"
#include "third-party/fmt/core.h"
namespace {
template <typename... Args>
@@ -28,7 +28,7 @@ template <typename... Args>
}
throw std::runtime_error(
fmt::format("Type Error: {}", fmt::format(fmt::runtime(str), std::forward<Args>(args)...)));
fmt::format("Type Error: {}", fmt::format(str, std::forward<Args>(args)...)));
}
} // namespace
@@ -548,7 +548,6 @@ MethodInfo TypeSystem::override_method(Type* type,
existing_info.name,
existing_info.type,
type->get_name(),
type->get_name(),
existing_info.no_virtual,
false,
true,
@@ -608,7 +607,6 @@ MethodInfo TypeSystem::declare_method(Type* type,
method_name,
ts,
type->get_name(),
type->get_name(),
no_virtual,
true,
false,
@@ -647,7 +645,6 @@ MethodInfo TypeSystem::declare_method(Type* type,
method_name,
ts,
type->get_name(),
type->get_name(),
no_virtual,
false,
false,
@@ -681,8 +678,8 @@ MethodInfo TypeSystem::overlay_method(Type* type,
}
// use the existing ID.
return type->add_method({existing_info.id, method_name, ts, type->get_name(), type->get_name(),
false, true, false, docstring, std::make_optional(method_overlay_name)});
return type->add_method({existing_info.id, method_name, ts, type->get_name(), false, true, false,
docstring, std::make_optional(method_overlay_name)});
}
MethodInfo TypeSystem::define_method(const std::string& type_name,
@@ -712,10 +709,8 @@ MethodInfo TypeSystem::define_method(Type* type,
// look up the method
MethodInfo existing_info;
bool got_existing = try_lookup_method(type, method_name, &existing_info);
if (got_existing) {
// The lookup will return a parents method, but the type should be equal
// to the type in question (it's a child)
existing_info.type_name = type->get_name();
// Update the docstring
existing_info.docstring = docstring;
int bad_arg_idx = -99;
@@ -763,7 +758,7 @@ MethodInfo TypeSystem::add_new_method(Type* type,
return existing;
} else {
return type->add_new_method(
{0, "new", ts, type->get_name(), type->get_name(), false, false, false, docstring, {}});
{0, "new", ts, type->get_name(), false, false, false, docstring, {}});
}
}
@@ -1100,7 +1095,6 @@ void TypeSystem::add_builtin_types(GameVersion version) {
break;
case GameVersion::Jak2:
case GameVersion::Jak3:
case GameVersion::JakX:
symbol_type = add_builtin_structure("object", "symbol", true);
symbol_type->override_offset(1);
break;
@@ -1448,21 +1442,6 @@ std::vector<std::string> TypeSystem::search_types_by_parent_type(
return results;
}
std::vector<std::string> TypeSystem::search_types_by_parent_type_strict(
const std::string& parent_type) {
std::vector<std::string> results = {};
for (const auto& [type_name, type_info] : m_types) {
// Only NullType's have no parent
if (!type_info->has_parent()) {
continue;
}
if (type_info->get_parent() == parent_type) {
results.push_back(type_name);
}
}
return results;
}
std::vector<std::string> TypeSystem::search_types_by_minimum_method_id(
const int minimum_method_id,
const std::optional<std::vector<std::string>>& existing_matches) {
@@ -1503,7 +1482,8 @@ std::vector<std::string> TypeSystem::search_types_by_size(
}
} else {
for (const auto& [type_name, type_info] : m_types) {
if (dynamic_cast<NullType*>(type_info.get())) {
// Only NullType's have no parent
if (!type_info->has_parent()) {
continue;
}
const auto size_of_type = m_types[type_name]->get_size_in_memory();
@@ -2133,7 +2113,7 @@ std::optional<std::string> find_best_field_in_structure(const TypeSystem& ts,
}
for (size_t i = start_field; i < (size_t)end_field; ++i) {
const auto& field = st->fields().at(i);
auto type = ts.lookup_type_allow_partial_def(field.type());
auto type = ts.lookup_type(field.type());
if (field.is_dynamic() || field.offset() > offset || field.user_placed() != want_fixed) {
continue;
}
@@ -2167,12 +2147,6 @@ std::optional<std::string> find_best_field_in_structure(const TypeSystem& ts,
} else {
int rel_offset = offset - field.offset();
// array case (and array encompasses what we want)
ASSERT_MSG(
type->get_size_in_memory() > 0,
fmt::format(
"In type {}, type size was 0 for array field {} of type {}, check that the type "
"is fully defined.",
st->get_name(), field.name(), field.type().print()));
int array_idx = rel_offset / type->get_size_in_memory();
if (!field.is_inline() &&
field.offset() + field.array_size() * type->get_load_size() > offset) {
-1
View File
@@ -278,7 +278,6 @@ class TypeSystem {
std::vector<std::string> search_types_by_parent_type(
const std::string& parent_type,
const std::optional<std::vector<std::string>>& existing_matches = {});
std::vector<std::string> search_types_by_parent_type_strict(const std::string& parent_type);
std::vector<std::string> search_types_by_minimum_method_id(
const int minimum_method_id,
+3 -5
View File
@@ -13,7 +13,7 @@
#include "common/util/BitUtils.h"
#include "common/util/string_util.h"
#include "fmt/format.h"
#include "third-party/fmt/core.h"
namespace {
const goos::Object& car(const goos::Object* x) {
@@ -58,11 +58,10 @@ EnumType* parse_defenum(const goos::Object& defenum,
auto& enum_name_obj = car(iter);
iter = cdr(iter);
// check for docstring
std::optional<std::string> maybe_docstring;
if (iter->is_pair() && car(iter).is_string()) {
// TODO - docstring - store and use docstring if coming from the compiler
if (symbol_metadata) {
maybe_docstring = str_util::trim_newline_indents(car(iter).as_string()->data);
symbol_metadata->docstring = maybe_docstring.value();
symbol_metadata->docstring = str_util::trim_newline_indents(car(iter).as_string()->data);
}
iter = cdr(iter);
}
@@ -158,7 +157,6 @@ EnumType* parse_defenum(const goos::Object& defenum,
if (is_type("integer", base_type, ts)) {
auto parent = ts->get_type_of_type<ValueType>(base_type.base_type());
auto new_type = std::make_unique<EnumType>(parent, name, is_bitfield, entries);
new_type->m_metadata.docstring = maybe_docstring;
new_type->set_runtime_type(parent->get_runtime_name());
return dynamic_cast<EnumType*>(ts->add_type(name, std::move(new_type)));
} else {
Loaded 100 of 10047 files, more files were not shown because too many files have changed in this diff. Show more