mirror of
https://github.com/open-goal/jak-project
synced 2026-06-19 16:00:12 -04:00
Merge branch 'master' into d/lang-pt
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
import glob
|
||||
|
||||
src_files = glob.glob("./goal_src/**/*.g[cs]", recursive=True)
|
||||
|
||||
# Find how many of each have been started
|
||||
|
||||
src_files_started = 0
|
||||
src_files_finished = 0
|
||||
data_files_started = 0
|
||||
|
||||
for f in src_files:
|
||||
with open(f, "r") as temp_file:
|
||||
lines = temp_file.readlines()
|
||||
line_count = len(lines)
|
||||
if line_count > 7:
|
||||
# Check to see if there are any TODOs
|
||||
if any("TODO" in string for string in lines):
|
||||
src_files_finished = src_files_finished + 1
|
||||
else:
|
||||
src_files_started = src_files_started + 1
|
||||
|
||||
import json
|
||||
with open('./docs/gh-pages-proj/src/config/progress.json', 'r+', encoding='utf-8') as f:
|
||||
data = {
|
||||
'jak1': {
|
||||
'fileProgress': {
|
||||
'src_files_total': len(src_files),
|
||||
'src_files_finished': src_files_finished,
|
||||
'src_files_started': src_files_started
|
||||
}
|
||||
}
|
||||
}
|
||||
f.seek(0)
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
f.truncate()
|
||||
@@ -1,63 +0,0 @@
|
||||
import glob
|
||||
import os
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
galleryLinks = {
|
||||
'jak1': {
|
||||
'name': "Jak 1",
|
||||
'media': [],
|
||||
},
|
||||
'jak2': {
|
||||
'name': "Jak 2",
|
||||
'media': [],
|
||||
},
|
||||
'jak3': {
|
||||
'name': "Jak 3",
|
||||
'media': [],
|
||||
},
|
||||
'jakx': {
|
||||
'name': "Jak X",
|
||||
'media': [],
|
||||
},
|
||||
'misc': {
|
||||
'name': "Miscellaneous",
|
||||
'media': [],
|
||||
}
|
||||
}
|
||||
|
||||
def get_links(key, folder_to_search):
|
||||
if os.path.isdir(folder_to_search):
|
||||
files = glob.glob(folder_to_search + "/*.png", recursive=True)
|
||||
files.extend(glob.glob(folder_to_search + "/*.jpg", recursive=True))
|
||||
files.extend(glob.glob(folder_to_search + "/*.jpeg", recursive=True))
|
||||
for f in files:
|
||||
galleryLinks[key]["media"].append({
|
||||
'fileName': os.path.basename(f),
|
||||
'timestamp': Path(f).stem.split("_")[1],
|
||||
'caption': Path(f).stem.split("_")[0].replace("-", " ").title(),
|
||||
'video': False
|
||||
})
|
||||
# get videos potentially
|
||||
if os.path.exists("{}/videos.json".format(folder_to_search)):
|
||||
with open("{}/videos.json".format(folder_to_search), 'r') as f:
|
||||
data = json.load(f)
|
||||
for video in data:
|
||||
galleryLinks[key]["media"].append({
|
||||
'link': video["link"].replace("watch?v=", "embed/"),
|
||||
'timestamp': video["timestamp"],
|
||||
'video': True
|
||||
})
|
||||
# sort by timestamp
|
||||
galleryLinks[key]["media"].sort(key=lambda x: x["timestamp"], reverse=True)
|
||||
|
||||
get_links('jak1', './docs/gh-pages-proj/src/assets/gallery/jak1')
|
||||
get_links('jak2', './docs/gh-pages-proj/src/assets/gallery/jak2')
|
||||
get_links('jak3', './docs/gh-pages-proj/src/assets/gallery/jak3')
|
||||
get_links('jakx', './docs/gh-pages-proj/src/assets/gallery/jakx')
|
||||
get_links('misc', './docs/gh-pages-proj/src/assets/gallery/misc')
|
||||
|
||||
with open('./docs/gh-pages-proj/src/config/gallery.json', 'r+', encoding='utf-8') as f:
|
||||
f.seek(0)
|
||||
json.dump(galleryLinks, f, ensure_ascii=False, indent=2)
|
||||
f.truncate()
|
||||
@@ -42,3 +42,11 @@ jobs:
|
||||
cmakePreset: "Release-linux-gcc"
|
||||
cachePrefix: ""
|
||||
secrets: inherit
|
||||
|
||||
# MacOS
|
||||
build_macos_clang:
|
||||
name: "🍎 MacOS"
|
||||
uses: ./.github/workflows/macos-build-clang.yaml
|
||||
with:
|
||||
cmakePreset: "Release-macos-clang"
|
||||
cachePrefix: ""
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Bump Version and Push Tag
|
||||
if: github.repository == 'open-goal/jak-project'
|
||||
id: tag_version
|
||||
uses: mathieudutour/github-tag-action@v6.0
|
||||
uses: mathieudutour/github-tag-action@v6.1
|
||||
with:
|
||||
github_token: ${{ secrets.BOT_PAT }}
|
||||
tag_prefix: v
|
||||
|
||||
@@ -17,9 +17,14 @@ jobs:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# 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 install clang-format clang-tidy
|
||||
sudo apt update
|
||||
sudo apt install clang-format
|
||||
clang-format -version
|
||||
|
||||
- name: Check Clang-Formatting
|
||||
|
||||
@@ -35,9 +35,10 @@ jobs:
|
||||
libxi-dev zip ninja-build
|
||||
|
||||
- name: Setup Buildcache
|
||||
uses: mikehardy/buildcache-action@v1.3.0
|
||||
uses: mikehardy/buildcache-action@v2.1.0
|
||||
with:
|
||||
cache_key: linux-ubuntu-20.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
|
||||
buildcache_tag: v0.28.1
|
||||
|
||||
- name: CMake Generation
|
||||
env:
|
||||
@@ -49,7 +50,7 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
|
||||
|
||||
- name: Build Project
|
||||
run: cmake --build build -j$((`nproc`+1))
|
||||
run: cmake --build build --parallel $((`nproc`))
|
||||
|
||||
- name: Run Tests
|
||||
env:
|
||||
|
||||
@@ -35,9 +35,10 @@ jobs:
|
||||
libxi-dev zip ninja-build
|
||||
|
||||
- name: Setup Buildcache
|
||||
uses: mikehardy/buildcache-action@v1.3.0
|
||||
uses: mikehardy/buildcache-action@v2.1.0
|
||||
with:
|
||||
cache_key: linux-ubuntu-20.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
|
||||
buildcache_tag: v0.28.1
|
||||
|
||||
- name: CMake Generation
|
||||
env:
|
||||
@@ -50,7 +51,7 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
|
||||
|
||||
- name: Build Project
|
||||
run: cmake --build build -j$((`nproc`+1)) -- -w dupbuild=warn
|
||||
run: cmake --build build --parallel $((`nproc`)) -- -w dupbuild=warn
|
||||
|
||||
- name: Run Tests - With Coverage
|
||||
working-directory: ./build
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
name: MacOS Build Clang
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cmakePreset:
|
||||
required: true
|
||||
type: string
|
||||
cachePrefix:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Clang
|
||||
runs-on: macos-12
|
||||
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@v3
|
||||
|
||||
- name: Install Package Dependencies
|
||||
run: brew install cmake nasm ninja
|
||||
|
||||
- name: Setup Buildcache
|
||||
uses: mikehardy/buildcache-action@v2.1.0
|
||||
with:
|
||||
cache_key: macos-12-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
|
||||
buildcache_tag: v0.28.1
|
||||
|
||||
- name: CMake Generation
|
||||
env:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
run: |
|
||||
cmake -B build --preset=${{ inputs.cmakePreset }} \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
|
||||
|
||||
# Disabled for now, not all build targets are valid
|
||||
# - name: Build Project
|
||||
# run: cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
|
||||
|
||||
# Temporary, selectively build those that work
|
||||
- name: Build Working Targets
|
||||
run: |
|
||||
cmake --build build --target extractor --parallel $((`sysctl -n hw.logicalcpu`)) && \
|
||||
cmake --build build --target offline-test --parallel $((`sysctl -n hw.logicalcpu`)) && \
|
||||
cmake --build build --target decompiler --parallel $((`sysctl -n hw.logicalcpu`)) && \
|
||||
cmake --build build --target lsp --parallel $((`sysctl -n hw.logicalcpu`)) && \
|
||||
cmake --build build --target goalc --parallel $((`sysctl -n hw.logicalcpu`))
|
||||
|
||||
- name: Run Tests
|
||||
continue-on-error: true # until macOS is stable
|
||||
env:
|
||||
GTEST_OUTPUT: "xml:opengoal-test-report.xml"
|
||||
run: ./test.sh
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: opengoal-macos-${{ inputs.cachePrefix }}
|
||||
if-no-files-found: error
|
||||
path: |
|
||||
./build/goalc/goalc
|
||||
./build/decompiler/extractor
|
||||
./build/game/gk
|
||||
./build/lsp/lsp
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
run: choco install ${{ github.workspace }}/third-party/nasm/nasm.2.15.05.nupkg
|
||||
|
||||
- name: Setup Buildcache
|
||||
uses: mikehardy/buildcache-action@v1.3.0
|
||||
uses: mikehardy/buildcache-action@v2.1.0
|
||||
with:
|
||||
cache_key: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
|
||||
|
||||
@@ -44,13 +44,13 @@ jobs:
|
||||
|
||||
- name: Build Project
|
||||
shell: cmd
|
||||
run: cmake --build build -j 2
|
||||
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=1 --gtest_filter="-*MANUAL_TEST*"
|
||||
run: ./build/bin/goalc-test.exe --gtest_color=yes --gtest_brief=0 --gtest_filter="-*MANUAL_TEST*"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: choco install ${{ github.workspace }}/third-party/nasm/nasm.2.15.05.nupkg
|
||||
|
||||
- name: Setup Buildcache
|
||||
uses: mikehardy/buildcache-action@v1.3.0
|
||||
uses: mikehardy/buildcache-action@v2.1.0
|
||||
with:
|
||||
cache_key: windows-2022-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }}
|
||||
|
||||
@@ -45,12 +45,12 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
set CL=/MP
|
||||
cmake --build build -j 2
|
||||
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=1 --gtest_filter="-*MANUAL_TEST*"
|
||||
./build/bin/goalc-test.exe --gtest_color=yes --gtest_brief=0 --gtest_filter="-*MANUAL_TEST*"
|
||||
|
||||
|
||||
+17
-3
@@ -3,12 +3,17 @@
|
||||
|
||||
# for clion
|
||||
cmake-build-debug/*
|
||||
cmake-build-debug--o0/*
|
||||
.idea/*
|
||||
build/*
|
||||
decompiler_out/*
|
||||
decompiler_out2/*
|
||||
logs/*
|
||||
|
||||
# for vscode/clangd
|
||||
.cache/*
|
||||
.DS_Store
|
||||
|
||||
# wsl apparently builds to here?
|
||||
linux-default/
|
||||
|
||||
@@ -27,12 +32,14 @@ savestate-out/
|
||||
savestate_out/
|
||||
failures/
|
||||
ee-results*.json
|
||||
search-results.json
|
||||
.env
|
||||
/search-results.json
|
||||
|
||||
# graphics debug
|
||||
debug_out/*
|
||||
gfx_dumps/*
|
||||
screenshots/*
|
||||
debug_out/
|
||||
gfx_dumps/
|
||||
screenshots/
|
||||
|
||||
# game stuff
|
||||
game_config/*
|
||||
@@ -49,3 +56,10 @@ svnrev.h
|
||||
ci-artifacts/
|
||||
out/build/
|
||||
__pycache__/
|
||||
|
||||
# sqlite stuff
|
||||
*.db
|
||||
*.db-journal
|
||||
|
||||
# backup files from OpenMaya
|
||||
*.bak
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
endOfLine: "auto"
|
||||
+77
-8
@@ -56,6 +56,49 @@
|
||||
"jak1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "offline-test.exe (bin\\offline-test.exe)",
|
||||
"name": "Tests - Offline Tests - Jak 2",
|
||||
"args": [
|
||||
"--iso_data_path",
|
||||
"${workspaceRoot}/iso_data/jak2",
|
||||
"--game",
|
||||
"jak2",
|
||||
"--pretty-print",
|
||||
"--num_threads",
|
||||
"32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "offline-test.exe (bin\\offline-test.exe)",
|
||||
"name": "Tests - Offline Tests - Jak 1 - Specific File",
|
||||
"args": [
|
||||
"--iso_data_path",
|
||||
"${workspaceRoot}/iso_data/jak1",
|
||||
"--game",
|
||||
"jak1",
|
||||
"--file",
|
||||
"wall-plat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "offline-test.exe (bin\\offline-test.exe)",
|
||||
"name": "Tests - Offline Tests - Jak 2 - Specific File",
|
||||
"args": [
|
||||
"--iso_data_path",
|
||||
"${workspaceRoot}/iso_data/jak2",
|
||||
"--game",
|
||||
"jak2",
|
||||
"--file",
|
||||
"fort-turret"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
@@ -84,6 +127,20 @@
|
||||
"name": "Game - Runtime (boot no debug)",
|
||||
"args": ["-boot", "-fakeiso", "-v"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Game - Jak 2 - Runtime (boot)",
|
||||
"args": ["-boot", "-fakeiso", "-debug", "-v", "-jak2"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Game - Jak 2 - Runtime (no boot)",
|
||||
"args": ["-fakeiso", "-debug", "-v", "-jak2"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
@@ -98,13 +155,6 @@
|
||||
"name": "REPL - Jak 2",
|
||||
"args": ["--user-auto", "--game", "jak2"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc.exe (bin\\goalc.exe)",
|
||||
"name": "REPL - Auto Listen",
|
||||
"args": ["--user-auto", "--auto-lt"]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
@@ -138,6 +188,18 @@
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Decompiler - Jak 2 - Extract",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak2_ntsc_v1.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out",
|
||||
"--config-override \"{\\\"decompile_code\\\": false}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
@@ -173,8 +235,15 @@
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "lsp.exe (bin\\lsp.exe)",
|
||||
"name" : "Run - LSP",
|
||||
"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}]"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": ["opengoal.opengoal", "naumovs.color-highlight"],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
Vendored
+3
-2
@@ -8,9 +8,10 @@
|
||||
"name": "Python",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/scripts/gsrc/update-from-decomp.py",
|
||||
"program": "${workspaceFolder}/scripts/gsrc/lint-gsrc-file.py",
|
||||
"console": "integratedTerminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": ["--game", "jak2", "--file", "vehicle-control"]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"og:ignore-from-loc": {
|
||||
"scope": "opengoal",
|
||||
"prefix": ["og:ignore-from-loc"],
|
||||
"body": [";; og:ignore-from-loc"],
|
||||
"description": "Marks the file to be excluded from the LoC count"
|
||||
},
|
||||
"og:ignore-errors": {
|
||||
"scope": "opengoal",
|
||||
"prefix": ["og:ignore-errors"],
|
||||
"body": [";; og:ignore-errors"],
|
||||
"description": "Ignore lines beginning with ';; ERROR:' or ';; WARN:' when copying decompiled code into it"
|
||||
},
|
||||
"og:ignore-form": {
|
||||
"scope": "opengoal",
|
||||
"prefix": ["og:ignore-form"],
|
||||
"body": [";; og:ignore-form:${1:substr}"],
|
||||
"description": "If the provided substr is found in the starting line of a form, the entire form will be omitted when copying decompiled code into the file"
|
||||
},
|
||||
}
|
||||
Vendored
+10
-2
@@ -1,4 +1,12 @@
|
||||
{
|
||||
"terminal.integrated.scrollback": 32000,
|
||||
"python.formatting.provider": "black",
|
||||
"[opengoal]": {
|
||||
"editor.quickSuggestions": {
|
||||
"other": true,
|
||||
"comments": true,
|
||||
"strings": true
|
||||
},
|
||||
"editor.wordBasedSuggestions": true,
|
||||
"editor.snippetSuggestions": "top"
|
||||
},
|
||||
"python.formatting.provider": "black"
|
||||
}
|
||||
|
||||
@@ -94,6 +94,35 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
endif()
|
||||
set(THIRDPARTY_IGNORED_WARNINGS "-w")
|
||||
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 \
|
||||
-Wextra \
|
||||
-Wno-cast-align \
|
||||
-Wcast-qual \
|
||||
-Wdisabled-optimization \
|
||||
-Wformat \
|
||||
-Wextra \
|
||||
-Wmissing-include-dirs \
|
||||
-Woverloaded-virtual \
|
||||
-Wredundant-decls \
|
||||
-Wshadow \
|
||||
-Wsign-promo \
|
||||
-O3 \
|
||||
-fdiagnostics-color=always"
|
||||
)
|
||||
|
||||
# additional c++ flags for release mode for our projects
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Release")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
endif()
|
||||
set(THIRDPARTY_IGNORED_WARNINGS "-w")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size -Wl,0x20000000")
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
message(STATUS "MSVC detected - Setting Defaults")
|
||||
|
||||
@@ -145,6 +174,13 @@ include_directories(SYSTEM third-party/inja)
|
||||
|
||||
# build repl library
|
||||
add_subdirectory(third-party/replxx EXCLUDE_FROM_ALL)
|
||||
|
||||
# SQLite - Jak 2/3's built in editor
|
||||
add_definitions(-DHAVE_USLEEP=1)
|
||||
add_definitions(-DSQLITE_THREADSAFE=1)
|
||||
add_definitions(-DSQLITE_ENABLE_RTREE)
|
||||
include_directories(third-party/SQLiteCpp/include)
|
||||
add_subdirectory(third-party/SQLiteCpp)
|
||||
string(REPLACE " ${THIRDPARTY_IGNORED_WARNINGS} " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# build common library
|
||||
|
||||
@@ -57,6 +57,16 @@
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/${presetName}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base-macos-release",
|
||||
"hidden": true,
|
||||
"inherits": "base",
|
||||
"binaryDir": "${sourceDir}/build/Release/bin",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/${presetName}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base-clang",
|
||||
"hidden": true,
|
||||
@@ -127,6 +137,12 @@
|
||||
"description": "Build with Clang as Release without Debug Symbols",
|
||||
"inherits": ["base-linux-release", "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-linux-clang-asan",
|
||||
"displayName": "Linux Release (clang-asan)",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://opengoal.dev/docs/intro" rel="nofollow"><img src="https://img.shields.io/badge/Documentation-Here-informational" alt="Documentation Badge" style="max-width:100%;"></a>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://github.com/open-goal/jak-project/workflows/Build/badge.svg"><img src="https://github.com/open-goal/jak-project/workflows/Build/badge.svg" alt="Linux and Windows Build" style="max-width:100%;"></a>
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://github.com/open-goal/jak-project/actions/workflows/build-matrix.yaml"><img src="https://github.com/open-goal/jak-project/actions/workflows/build-matrix.yaml/badge.svg" alt="Linux and Windows Build" style="max-width:100%;"></a>
|
||||
<a href="https://www.codacy.com/gh/open-goal/jak-project/dashboard?utm_source=github.com&utm_medium=referral&utm_content=open-goal/jak-project&utm_campaign=Badge_Coverage" rel="nofollow"><img src="https://app.codacy.com/project/badge/Coverage/29316d04a1644aa390c33be07289f3f5" alt="Codacy Badge" style="max-width:100%;"></a>
|
||||
<a href="https://www.codacy.com/gh/open-goal/jak-project/dashboard?utm_source=github.com&utm_medium=referral&utm_content=open-goal/jak-project&utm_campaign=Badge_Grade" rel="nofollow"><img src="https://app.codacy.com/project/badge/Grade/29316d04a1644aa390c33be07289f3f5" alt="Codacy Badge" style="max-width:100%;"></a>
|
||||
<a href="https://discord.gg/VZbXMHXzWv"><img src="https://img.shields.io/discord/756287461377703987" alt="Discord"></a>
|
||||
@@ -112,13 +112,13 @@ 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 the build directory you'll need to rerun the build command. Alteratively you can get the build via `docker cp`
|
||||
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.
|
||||
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.
|
||||
|
||||
Unfortently 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`
|
||||
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
|
||||
|
||||
@@ -244,13 +244,20 @@ Getting a running game involves 4 steps:
|
||||
|
||||
#### Extract Assets
|
||||
|
||||
First, setup your settings so the following scripts know which game you are using, and which version. In a terminal, 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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
> At the time of writing, only Jak 1 is expected to work end-to-end!
|
||||
@@ -292,6 +299,12 @@ Run the following to build the game:
|
||||
g > (mi)
|
||||
```
|
||||
|
||||
> IMPORTANT NOTE! If you're not using the black label version, you may hit issues trying to run `(mi)` in this step. An example error might include something like:
|
||||
>
|
||||
> `Input file iso_data/jak1/MUS/TWEAKVAL.MUS does not exist.`
|
||||
>
|
||||
> This is because other version paths are not currently accounted for in the build. A quick workaround is to rename both your `decompiler_out` and `iso_data` folders to use the black label naming, for example changing `decompiler_out/jak1_pal` to `decompiler_out/jak1` and `iso_data/jak1_pal` to `iso_data/jak1`, then running `(mi)` again.
|
||||
|
||||
#### Run the Game
|
||||
|
||||
Finally the game can be ran. Open a second terminal from the `jak-project` directory and run the following:
|
||||
|
||||
+55
-28
@@ -24,9 +24,13 @@ tasks:
|
||||
cmds:
|
||||
- 'python ./scripts/tasks/update-env.py --decomp_config ntscv2'
|
||||
set-decomp-pal:
|
||||
- 'python ./scripts/tasks/update-env.py --decomp_config pal'
|
||||
desc: "PAL region version"
|
||||
cmds:
|
||||
- 'python ./scripts/tasks/update-env.py --decomp_config pal'
|
||||
set-decomp-ntscjp:
|
||||
- 'python ./scripts/tasks/update-env.py --decomp_config ntscjp'
|
||||
desc: "NTSC-J region version"
|
||||
cmds:
|
||||
- 'python ./scripts/tasks/update-env.py --decomp_config ntscjp'
|
||||
# GENERAL
|
||||
extract:
|
||||
desc: "Extracts the game's assets from './iso_data' with the set decompiler config"
|
||||
@@ -34,7 +38,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 --config-override "{\\\"decompile_code\\\": false}"'
|
||||
- "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler \"./decompiler/config/{{.DECOMP_CONFIG}}\" \"./iso_data\" \"./decompiler_out\" --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:
|
||||
@@ -55,7 +59,7 @@ tasks:
|
||||
- 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 -fakeiso -debug -v"
|
||||
- "{{.GK_BIN_RELEASE_DIR}}/gk -fakeiso -debug -{{.GAME}} -v"
|
||||
# DEVELOPMENT
|
||||
repl:
|
||||
desc: "Start the REPL"
|
||||
@@ -63,10 +67,7 @@ tasks:
|
||||
- sh: test -f {{.GOALC_BIN_RELEASE_DIR}}/goalc{{.EXE_FILE_EXTENSION}}
|
||||
msg: "Couldn't locate compiler executable in '{{.GOALC_BIN_RELEASE_DIR}}/goalc'"
|
||||
cmds:
|
||||
- "{{.GOALC_BIN_RELEASE_DIR}}/goalc --game {{.GAME}}"
|
||||
repl-lt:
|
||||
cmds:
|
||||
- "{{.GOALC_BIN_RELEASE_DIR}}/goalc --auto-lt --game {{.GAME}}"
|
||||
- "{{.GOALC_BIN_RELEASE_DIR}}/goalc --user-auto --game {{.GAME}}"
|
||||
format:
|
||||
desc: "Format code"
|
||||
cmds:
|
||||
@@ -82,41 +83,67 @@ tasks:
|
||||
# DECOMPILING
|
||||
decomp:
|
||||
cmds:
|
||||
- '{{.DECOMP_BIN_RELEASE_DIR}}/decompiler "./decompiler/config/{{.DECOMP_CONFIG}}" "./iso_data" "./decompiler_out"'
|
||||
- "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler \"./decompiler/config/{{.DECOMP_CONFIG}}\" \"./iso_data\" \"./decompiler_out\" --config-override '{\"levels_extract\": false}'"
|
||||
decomp-file:
|
||||
cmds:
|
||||
- "{{.DECOMP_BIN_RELEASE_DIR}}/decompiler \"./decompiler/config/{{.DECOMP_CONFIG}}\" \"./iso_data\" \"./decompiler_out\" --config-override '{\"levels_extract\": false, \"allowed_objects\": [\"{{.FILE}}\"]}'"
|
||||
decomp-clean:
|
||||
cmds:
|
||||
- rm ./decompiler_out/**/*.asm
|
||||
- rm ./decompiler_out/**/*disasm.gc
|
||||
- python ./scripts/tasks/clean-decomp.py --game "{{.GAME}}"
|
||||
lint-gsrc-file:
|
||||
cmds:
|
||||
- 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}}
|
||||
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}}
|
||||
update-gsrc-file:
|
||||
cmds:
|
||||
- task: decomp-file
|
||||
- python ./scripts/gsrc/update-from-decomp.py --game "{{.GAME}}" --file {{.FILE}}
|
||||
- task: lint-gsrc-file
|
||||
# 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'
|
||||
watch-pcsx2:
|
||||
# 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:
|
||||
SAVESTATE_DIR: '{{default "." .SAVESTATE_DIR}}'
|
||||
# TESTS
|
||||
offline-tests:
|
||||
type-search:
|
||||
desc: Just an example to show it running
|
||||
cmds:
|
||||
- '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}}'
|
||||
- "{{.TYPESEARCH_BIN_RELEASE_DIR}}/type_searcher --output-path ./search-results.json --game {{.GAME}} --fields '[{\"type\":\"int16\",\"offset\":2},{\"type\":\"int16\",\"offset\":4}]'"
|
||||
# TESTS
|
||||
offline-tests: # ran by jenkins
|
||||
cmds:
|
||||
- '{{.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 --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --file {{.FILE}}'
|
||||
offline-tests-fast:
|
||||
cmds:
|
||||
- '{{.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/default-file-or-folder.py --path failures
|
||||
- cmd: '{{.OFFLINETEST_BIN_RELEASE_DIR}}/offline-test --iso_data_path "./iso_data/{{.GAME}}" --game {{.GAME}} --dump_current_output'
|
||||
- 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/
|
||||
- task: offline-tests
|
||||
# find-label-types:
|
||||
# cmds:
|
||||
# - python ./scripts/next-decomp-file.py --files "{{.FILES}}"
|
||||
# - task: decomp
|
||||
# - python ./scripts/find-label-types.py --file "{{.FILES}}"
|
||||
# check-gsrc-file:
|
||||
# cmds:
|
||||
# - python ./scripts/check-gsrc-file.py --files "{{.FILES}}"
|
||||
- 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: '{{.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}}
|
||||
- task: offline-test-file
|
||||
type-test:
|
||||
cmds:
|
||||
- cmd: '{{.GOALCTEST_BIN_RELEASE_DIR}}/goalc-test --gtest_brief=0 --gtest_filter="*MANUAL_TEST_TypeConsistencyWithBuildFirst*"'
|
||||
ignore_error: true
|
||||
- cmd: '{{.GOALCTEST_BIN_RELEASE_DIR}}/goalc-test --gtest_brief=0 --gtest_filter="*Jak2TypeConsistency*" --gtest_break_on_failure'
|
||||
|
||||
+10
-5
@@ -19,11 +19,12 @@ add_library(common
|
||||
goos/PrettyPrinter2.cpp
|
||||
goos/Reader.cpp
|
||||
goos/TextDB.cpp
|
||||
goos/ReplUtils.cpp
|
||||
repl/config.cpp
|
||||
repl/util.cpp
|
||||
log/log.cpp
|
||||
math/geometry.cpp
|
||||
nrepl/ReplClient.cpp
|
||||
nrepl/ReplServer.cpp
|
||||
repl/nrepl/ReplClient.cpp
|
||||
repl/nrepl/ReplServer.cpp
|
||||
type_system/defenum.cpp
|
||||
type_system/deftype.cpp
|
||||
type_system/state.cpp
|
||||
@@ -42,20 +43,24 @@ add_library(common
|
||||
util/DgoWriter.cpp
|
||||
util/diff.cpp
|
||||
util/FileUtil.cpp
|
||||
util/FontUtils.cpp
|
||||
util/json_util.cpp
|
||||
util/read_iso_file.cpp
|
||||
util/SimpleThreadGroup.cpp
|
||||
util/string_util.cpp
|
||||
util/Timer.cpp
|
||||
util/os.cpp
|
||||
util/print_float.cpp
|
||||
util/FontUtils.cpp
|
||||
util/FrameLimiter.cpp
|
||||
util/unicode_util.cpp)
|
||||
util/unicode_util.cpp
|
||||
util/term_util.cpp)
|
||||
|
||||
target_link_libraries(common fmt lzokay replxx libzstd_static)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(common wsock32 ws2_32 windowsapp)
|
||||
elseif(APPLE)
|
||||
# don't need anything special
|
||||
else()
|
||||
target_link_libraries(common stdc++fs)
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "audio_formats.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/BinaryWriter.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
@@ -254,7 +255,7 @@ void test_encode_adpcm(const std::vector<s16>& samples,
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
fmt::print("Range: {}\n", max_sample - min_sample);
|
||||
lg::debug("Range: {}", max_sample - min_sample);
|
||||
}
|
||||
|
||||
// see how many bits we need and pick shift.
|
||||
@@ -283,11 +284,11 @@ void test_encode_adpcm(const std::vector<s16>& samples,
|
||||
|
||||
if (filter_errors[best_filter] || best_filter != filter_debug[block_idx] ||
|
||||
best_shift != shift_debug[block_idx]) {
|
||||
fmt::print("Block {} me {}, {} : answer {} {}: ERR {}\n", block_idx, best_filter, best_shift,
|
||||
filter_debug[block_idx], shift_debug[block_idx], filter_errors[best_filter]);
|
||||
fmt::print("filter errors:\n");
|
||||
lg::error("Block {} me {}, {} : answer {} {}: ERR {}", block_idx, best_filter, best_shift,
|
||||
filter_debug[block_idx], shift_debug[block_idx], filter_errors[best_filter]);
|
||||
lg::error("filter errors:");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
fmt::print(" [{}] {} {}\n", i, filter_errors[i], filter_shifts[i]);
|
||||
lg::error(" [{}] {} {}", i, filter_errors[i], filter_shifts[i]);
|
||||
}
|
||||
ASSERT_MSG(false, fmt::format("prev: {} {}", prev_block_samples[0], prev_block_samples[1]));
|
||||
}
|
||||
|
||||
@@ -30,3 +30,7 @@ struct u128 {
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(u128) == 16, "u128");
|
||||
|
||||
#if defined __linux || defined __linux__ || defined __APPLE__
|
||||
#define OS_POSIX
|
||||
#endif
|
||||
|
||||
@@ -471,12 +471,14 @@ bool check_stopped(const ThreadID& tid, SignalInfo* out) {
|
||||
{
|
||||
auto exc = debugEvent.u.Exception.ExceptionRecord.ExceptionCode;
|
||||
if (is_other) {
|
||||
if (exc == EXCEPTION_BREAKPOINT) {
|
||||
out->kind = SignalInfo::BREAK;
|
||||
} else {
|
||||
// ignore exceptions outside goal thread
|
||||
ignore_debug_event();
|
||||
}
|
||||
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId,
|
||||
DBG_EXCEPTION_NOT_HANDLED);
|
||||
// if (exc == EXCEPTION_BREAKPOINT) {
|
||||
// out->kind = SignalInfo::BREAK;
|
||||
// } else {
|
||||
// // ignore exceptions outside goal thread
|
||||
// ignore_debug_event();
|
||||
// }
|
||||
} else {
|
||||
switch (exc) {
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
@@ -688,6 +690,54 @@ bool set_regs_now(const ThreadID& tid, const Regs& out) {
|
||||
// todo, set fprs.
|
||||
return true;
|
||||
}
|
||||
#elif __APPLE__
|
||||
ThreadID::ThreadID(const std::string& str) {}
|
||||
|
||||
std::string ThreadID::to_string() const {
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
ThreadID get_current_thread_id();
|
||||
bool attach_and_break(const ThreadID& tid);
|
||||
void allow_debugging();
|
||||
bool detach_and_resume(const ThreadID& tid) {
|
||||
return false;
|
||||
}
|
||||
bool get_regs_now(const ThreadID& tid, Regs* out) {
|
||||
return false;
|
||||
}
|
||||
bool set_regs_now(const ThreadID& tid, const Regs& in) {
|
||||
return false;
|
||||
}
|
||||
bool break_now(const ThreadID& tid) {
|
||||
return false;
|
||||
}
|
||||
bool cont_now(const ThreadID& tid) {
|
||||
return false;
|
||||
}
|
||||
bool open_memory(const ThreadID& tid, MemoryHandle* out);
|
||||
bool close_memory(const ThreadID& tid, MemoryHandle* handle) {
|
||||
return false;
|
||||
}
|
||||
bool read_goal_memory(u8* dest_buffer,
|
||||
int size,
|
||||
u32 goal_addr,
|
||||
const DebugContext& context,
|
||||
const MemoryHandle& mem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_goal_memory(const u8* src_buffer,
|
||||
int size,
|
||||
u32 goal_addr,
|
||||
const DebugContext& context,
|
||||
const MemoryHandle& mem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_stopped(const ThreadID& tid, SignalInfo* out) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* gpr_names[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#endif
|
||||
|
||||
namespace xdbg {
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
|
||||
/*!
|
||||
* Identification for a thread.
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
#ifdef __linux
|
||||
#include "common/common_types.h"
|
||||
#ifdef OS_POSIX
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
@@ -18,10 +19,11 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/log/log.h"
|
||||
// clang-format on
|
||||
|
||||
int open_socket(int af, int type, int protocol) {
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
return socket(af, type, protocol);
|
||||
#elif _WIN32
|
||||
WSADATA wsaData = {0};
|
||||
@@ -29,7 +31,7 @@ int open_socket(int af, int type, int protocol) {
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
printf("WSAStartup failed: %d\n", iResult);
|
||||
lg::error("WSAStartup failed: {}", iResult);
|
||||
return 1;
|
||||
}
|
||||
return socket(af, type, protocol);
|
||||
@@ -44,7 +46,7 @@ int connect_socket(int socket, sockaddr* addr, int nameLen) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
int accept_socket(int socket, sockaddr* addr, socklen_t* addrLen) {
|
||||
return accept(socket, addr, addrLen);
|
||||
}
|
||||
@@ -71,7 +73,7 @@ int accept_socket(int socket, sockaddr* addr, int* addrLen) {
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
printf("WSAStartup failed: %d\n", iResult);
|
||||
lg::error("WSAStartup failed: {}", iResult);
|
||||
return 1;
|
||||
}
|
||||
return accept(socket, addr, addrLen);
|
||||
@@ -105,7 +107,7 @@ void close_socket(int sock) {
|
||||
if (sock < 0) {
|
||||
return;
|
||||
}
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
close(sock);
|
||||
#elif _WIN32
|
||||
closesocket(sock);
|
||||
@@ -116,20 +118,20 @@ void close_socket(int sock) {
|
||||
int set_socket_option(int socket, int level, int optname, const void* optval, int optlen) {
|
||||
int ret = setsockopt(socket, level, optname, (const char*)optval, optlen);
|
||||
if (ret < 0) {
|
||||
printf("Failed to setsockopt(%d, %d, %d, _, _) - Error: %s\n", socket, level, optname,
|
||||
strerror(errno));
|
||||
lg::error("Failed to setsockopt({},{}, {}, _, _) - Error: {}", socket, level, optname,
|
||||
strerror(errno));
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (ret < 0) {
|
||||
int err = WSAGetLastError();
|
||||
printf("WSAGetLastError: %d\n", err);
|
||||
lg::error("WSAGetLastError: {}", err);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_socket_timeout(int socket, long microSeconds) {
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
struct timeval timeout = {};
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = microSeconds;
|
||||
@@ -141,25 +143,27 @@ int set_socket_timeout(int socket, long microSeconds) {
|
||||
return ret;
|
||||
#elif _WIN32
|
||||
DWORD timeout = microSeconds / 1000; // milliseconds
|
||||
// TODO - NOTE this might be bad / unreliable if the socket ends up being in a bad state
|
||||
// select() instead should be used, will do so if ends up being an issue in practice
|
||||
return set_socket_option(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
#endif
|
||||
}
|
||||
|
||||
int write_to_socket(int socket, const char* buf, int len) {
|
||||
int bytes_wrote = 0;
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
bytes_wrote = send(socket, buf, len, MSG_NOSIGNAL);
|
||||
#elif _WIN32
|
||||
bytes_wrote = send(socket, buf, len, 0);
|
||||
#endif
|
||||
if (bytes_wrote < 0) {
|
||||
fmt::print(stderr, "[XSocket:{}] Error writing to socket\n", socket);
|
||||
lg::error("[XSocket:{}] Error writing to socket", socket);
|
||||
}
|
||||
return bytes_wrote;
|
||||
}
|
||||
|
||||
int read_from_socket(int socket, char* buf, int len) {
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
return read(socket, buf, len);
|
||||
#elif _WIN32
|
||||
return recv(socket, buf, len, 0);
|
||||
@@ -167,7 +171,7 @@ int read_from_socket(int socket, char* buf, int len) {
|
||||
}
|
||||
|
||||
bool socket_timed_out() {
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
return errno == EAGAIN;
|
||||
#elif _WIN32
|
||||
auto err = WSAGetLastError();
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
#ifdef __linux
|
||||
#include "common/common_types.h"
|
||||
#ifdef OS_POSIX
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
@@ -24,11 +25,13 @@
|
||||
const int TCP_SOCKET_LEVEL = SOL_TCP;
|
||||
#elif _WIN32
|
||||
const int TCP_SOCKET_LEVEL = IPPROTO_TCP;
|
||||
#elif __APPLE__
|
||||
const int TCP_SOCKET_LEVEL = IPPROTO_TCP;
|
||||
#endif
|
||||
|
||||
int open_socket(int af, int type, int protocol);
|
||||
int connect_socket(int socket, sockaddr* addr, int nameLen);
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
int accept_socket(int socket, sockaddr* addr, socklen_t* addrLen);
|
||||
int select_and_accept_socket(int socket, sockaddr* addr, socklen_t* addrLen, int microSeconds);
|
||||
#elif _WIN32
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#endif
|
||||
#include "common/nrepl/ReplServer.h"
|
||||
#include "common/repl/nrepl/ReplServer.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
// clang-format on
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "XSocketServer.h"
|
||||
|
||||
#include "common/cross_sockets/XSocket.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#endif
|
||||
#include "common/log/log.h"
|
||||
// clang-format on
|
||||
|
||||
XSocketServer::XSocketServer(std::function<bool()> shutdown_callback,
|
||||
@@ -40,7 +42,7 @@ bool XSocketServer::init_server() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux
|
||||
#ifdef OS_POSIX
|
||||
int server_socket_opt = SO_REUSEADDR | SO_REUSEPORT;
|
||||
#elif _WIN32
|
||||
int server_socket_opt = SO_EXCLUSIVEADDRUSE;
|
||||
@@ -67,19 +69,19 @@ bool XSocketServer::init_server() {
|
||||
addr.sin_port = htons(tcp_port);
|
||||
|
||||
if (bind(listening_socket, (sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
fmt::print("[XSocketServer:{}] failed to bind\n", tcp_port);
|
||||
lg::error("[XSocketServer:{}] failed to bind", tcp_port);
|
||||
close_server_socket();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(listening_socket, 0) < 0) {
|
||||
fmt::print("[XSocketServer:{}] failed to listen\n", tcp_port);
|
||||
lg::error("[XSocketServer:{}] failed to listen", tcp_port);
|
||||
close_server_socket();
|
||||
return false;
|
||||
}
|
||||
|
||||
server_initialized = true;
|
||||
fmt::print("[XSocketServer:{}] initialized\n", tcp_port);
|
||||
lg::info("[XSocketServer:{}] initialized", tcp_port);
|
||||
post_init();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/cross_sockets/XSocket.h"
|
||||
|
||||
@@ -259,10 +259,10 @@ struct Texture {
|
||||
};
|
||||
|
||||
// Tfrag trees have several kinds:
|
||||
enum class TFragmentTreeKind { NORMAL, TRANS, DIRT, ICE, LOWRES, LOWRES_TRANS, INVALID };
|
||||
enum class TFragmentTreeKind { NORMAL, TRANS, DIRT, ICE, LOWRES, LOWRES_TRANS, WATER, INVALID };
|
||||
|
||||
constexpr const char* tfrag_tree_names[] = {"normal", "trans", "dirt", "ice",
|
||||
"lowres", "lowres-trans", "invalid"};
|
||||
constexpr const char* tfrag_tree_names[] = {"normal", "trans", "dirt", "ice",
|
||||
"lowres", "lowres-trans", "water", "invalid"};
|
||||
|
||||
// A tfrag model
|
||||
struct TfragTree {
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
constexpr float kClusterSize = 4096 * 40; // 100 in-game meters
|
||||
constexpr float kMasterOffset = 12000 * 4096;
|
||||
|
||||
std::pair<u64, u16> position_to_cluster_and_offset(float in) {
|
||||
in += kMasterOffset;
|
||||
if (in < 0) {
|
||||
fmt::print("negative: {}\n", in);
|
||||
lg::print("negative: {}\n", in);
|
||||
}
|
||||
ASSERT(in >= 0);
|
||||
int cluster_cell = (in / kClusterSize);
|
||||
@@ -69,4 +71,4 @@ void pack_tfrag_vertices(tfrag3::PackedTfragVertices* result,
|
||||
}
|
||||
|
||||
ASSERT(next_cluster_idx < UINT16_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-11
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/dma/dma_chain_read.h"
|
||||
#include "common/goal_constants.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
@@ -36,11 +37,11 @@ void diff_dma_chains(DmaFollower ref, DmaFollower dma) {
|
||||
auto ref_tag = ref.current_tag();
|
||||
auto dma_tag = dma.current_tag();
|
||||
if (ref_tag.kind != dma_tag.kind) {
|
||||
fmt::print("Bad dma tag kinds\n");
|
||||
lg::warn("Bad dma tag kinds");
|
||||
}
|
||||
|
||||
if (ref_tag.qwc != dma_tag.qwc) {
|
||||
fmt::print("Bad dma tag qwc: {} {}\n", ref_tag.qwc, dma_tag.qwc);
|
||||
lg::warn("Bad dma tag qwc: {} {}", ref_tag.qwc, dma_tag.qwc);
|
||||
}
|
||||
|
||||
auto ref_result = ref.read_and_advance();
|
||||
@@ -48,19 +49,19 @@ void diff_dma_chains(DmaFollower ref, DmaFollower dma) {
|
||||
|
||||
for (int i = 0; i < (int)ref_result.size_bytes; i++) {
|
||||
if (ref_result.data[i] != dma_result.data[i]) {
|
||||
fmt::print("Bad data ({} vs {}) at {} into transfer: {} {}\n", ref_result.data[i],
|
||||
dma_result.data[i], i, ref_tag.print(), dma_tag.print());
|
||||
lg::error("Bad data ({} vs {}) at {} into transfer: {} {}", ref_result.data[i],
|
||||
dma_result.data[i], i, ref_tag.print(), dma_tag.print());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref.ended()) {
|
||||
fmt::print("dma ended early\n");
|
||||
lg::warn("dma ended early");
|
||||
}
|
||||
|
||||
if (!dma.ended()) {
|
||||
fmt::print("dma had extra data\n");
|
||||
lg::warn("dma had extra data");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,12 +176,12 @@ const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool ver
|
||||
auto v2 = flatten_dma(DmaFollower(m_result.data.data(), m_result.start_offset));
|
||||
|
||||
if (ref != v2) {
|
||||
fmt::print("Verification has failed.\n");
|
||||
fmt::print("size diff: {} {}\n", ref.size(), v2.size());
|
||||
lg::error("Verification has failed.");
|
||||
lg::error("size diff: {} {}", ref.size(), v2.size());
|
||||
|
||||
for (size_t i = 0; i < std::min(ref.size(), v2.size()); i++) {
|
||||
if (ref[i] != v2[i]) {
|
||||
fmt::print("first diff at {}\n", i);
|
||||
lg::error("first diff at {}", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -188,10 +189,10 @@ const DmaData& FixedChunkDmaCopier::run(const void* memory, u32 offset, bool ver
|
||||
DmaFollower(m_result.data.data(), m_result.start_offset));
|
||||
ASSERT(false);
|
||||
} else {
|
||||
fmt::print("verification ok: {} bytes\n", ref.size());
|
||||
lg::debug("verification ok: {} bytes", ref.size());
|
||||
}
|
||||
}
|
||||
|
||||
m_result.stats.sync_time_ms = timer.getMs();
|
||||
return m_result;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -3,6 +3,7 @@
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
std::string reg_descriptor_name(GifTag::RegisterDescriptor reg) {
|
||||
switch (reg) {
|
||||
@@ -352,15 +353,17 @@ std::string GsTexa::print() const {
|
||||
std::string GsTex0::print() const {
|
||||
return fmt::format(
|
||||
"tbp0: {} tbw: {} psm: {} tw: {} th: {} tcc: {} tfx: {} cbp: {} cpsm: {} csm: {}\n", tbp0(),
|
||||
tbw(), psm(), tw(), th(), tcc(), tfx(), cbp(), cpsm(), csm());
|
||||
tbw(), fmt::underlying(psm()), tw(), th(), tcc(), fmt::underlying(tfx()), cbp(), cpsm(),
|
||||
csm());
|
||||
}
|
||||
|
||||
std::string GsPrim::print() const {
|
||||
return fmt::format("0x{:x}, kind {}\n", data, kind());
|
||||
return fmt::format("0x{:x}, kind {}\n", data, fmt::underlying(kind()));
|
||||
}
|
||||
|
||||
std::string GsFrame::print() const {
|
||||
return fmt::format("fbp: {} fbw: {} psm: {} fbmsk: {:x}\n", fbp(), fbw(), psm(), fbmsk());
|
||||
return fmt::format("fbp: {} fbw: {} psm: {} fbmsk: {:x}\n", fbp(), fbw(), fmt::underlying(psm()),
|
||||
fbmsk());
|
||||
}
|
||||
|
||||
std::string GsXYOffset::print() const {
|
||||
|
||||
@@ -7,23 +7,25 @@
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/json.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
u32 get_current_tid() {
|
||||
return (u32)pthread_self();
|
||||
#ifdef OS_POSIX
|
||||
u64 get_current_tid() {
|
||||
return (u64)pthread_self();
|
||||
}
|
||||
#else
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include "Processthreadsapi.h"
|
||||
u32 get_current_tid() {
|
||||
return (u32)GetCurrentThreadId();
|
||||
u64 get_current_tid() {
|
||||
return (u64)GetCurrentThreadId();
|
||||
}
|
||||
#endif
|
||||
#include "common/log/log.h"
|
||||
// clang-format on
|
||||
|
||||
u64 get_current_ts() {
|
||||
@@ -157,18 +159,17 @@ void GlobalProfiler::dump_to_json(const std::string& path) {
|
||||
// ts
|
||||
json_event["ts"] = (event.ts - lowest_ts) / 1000.f;
|
||||
if (event.ts < info.debug) {
|
||||
fmt::print("out of order: {} {} {} ms\n", event.ts / 1000.f, info.debug / 1000.f,
|
||||
(info.debug - event.ts) / 1000000.f);
|
||||
fmt::print(" idx: {}, range {} {}\n", event_idx, info.lowest_at_target,
|
||||
info.highest_at_target);
|
||||
fmt::print(" now: {}\n", m_next_idx);
|
||||
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);
|
||||
}
|
||||
info.debug = event.ts;
|
||||
}
|
||||
|
||||
for (auto& t : info_per_thread) {
|
||||
fmt::print("thread: {}: {} -> {}\n", t.first, t.second.lowest_at_target,
|
||||
t.second.highest_at_target);
|
||||
lg::debug("thread: {}: {} -> {}", t.first, t.second.lowest_at_target,
|
||||
t.second.highest_at_target);
|
||||
}
|
||||
|
||||
file_util::write_text_file(path, json.dump());
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
struct ProfNode {
|
||||
u64 ts;
|
||||
u64 tid;
|
||||
char name[32];
|
||||
enum Kind : u8 { BEGIN, END, INSTANT, UNUSED } kind = UNUSED;
|
||||
u32 tid;
|
||||
};
|
||||
|
||||
class GlobalProfiler {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "ParseHelpers.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/unicode_util.h"
|
||||
|
||||
@@ -159,7 +160,7 @@ HeapObject* Interpreter::intern_ptr(const std::string& name) {
|
||||
/*!
|
||||
* Display the REPL, which will run until the user executes exit.
|
||||
*/
|
||||
void Interpreter::execute_repl(ReplWrapper& repl) {
|
||||
void Interpreter::execute_repl(REPL::Wrapper& repl) {
|
||||
want_exit = false;
|
||||
while (!want_exit) {
|
||||
try {
|
||||
@@ -1590,7 +1591,7 @@ Object Interpreter::eval_format(const Object& form,
|
||||
fmt::format_args(args2.data(), static_cast<unsigned>(args2.size())));
|
||||
|
||||
if (truthy(dest)) {
|
||||
printf("%s", formatted.c_str());
|
||||
lg::print(formatted.c_str());
|
||||
}
|
||||
|
||||
return StringObject::make_new(formatted);
|
||||
|
||||
@@ -16,7 +16,7 @@ class Interpreter {
|
||||
public:
|
||||
Interpreter(const std::string& user_profile = "#f");
|
||||
~Interpreter();
|
||||
void execute_repl(ReplWrapper& repl);
|
||||
void execute_repl(REPL::Wrapper& repl);
|
||||
void throw_eval_error(const Object& o, const std::string& err);
|
||||
Object eval_with_rewind(const Object& obj, const std::shared_ptr<EnvironmentObject>& env);
|
||||
bool get_global_variable_by_name(const std::string& name, Object* dest);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "Reader.h"
|
||||
|
||||
#include "common/goos/PrettyPrinter2.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
namespace pretty_print {
|
||||
|
||||
@@ -29,11 +30,14 @@ struct Node {
|
||||
std::string atom_str;
|
||||
|
||||
// number of quotes this is wrapped in.
|
||||
u32 quoted = 0;
|
||||
enum class QuoteKind { QUOTE, UNQUOTE, QUASIQUOTE, UNQUOTE_SPLICING };
|
||||
std::vector<QuoteKind> quotes;
|
||||
|
||||
Node* parent = nullptr;
|
||||
u32 my_depth = 0;
|
||||
|
||||
int get_quote_length() const;
|
||||
|
||||
void link(Node* this_parent, std::vector<Node*>* bfs_order, u32 depth) {
|
||||
parent = this_parent;
|
||||
my_depth = depth;
|
||||
@@ -86,6 +90,30 @@ struct Node {
|
||||
u8 sub_elt_indent = 0;
|
||||
};
|
||||
|
||||
inline const std::string quote_symbol(Node::QuoteKind kind) {
|
||||
switch (kind) {
|
||||
case Node::QuoteKind::QUOTE:
|
||||
return "'";
|
||||
case Node::QuoteKind::QUASIQUOTE:
|
||||
return "`";
|
||||
case Node::QuoteKind::UNQUOTE:
|
||||
return ",";
|
||||
case Node::QuoteKind::UNQUOTE_SPLICING:
|
||||
return ",@";
|
||||
default:
|
||||
ASSERT_MSG(false, fmt::format("invalid quote kind {}", fmt::underlying(kind)));
|
||||
return "[invalid]";
|
||||
}
|
||||
}
|
||||
|
||||
int Node::get_quote_length() const {
|
||||
int out = 0;
|
||||
for (auto& q : quotes) {
|
||||
out += quote_symbol(q).length();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Node to_node(const goos::Object& obj) {
|
||||
switch (obj.type) {
|
||||
case goos::ObjectType::EMPTY_LIST:
|
||||
@@ -100,13 +128,22 @@ Node to_node(const goos::Object& obj) {
|
||||
return Node(obj.print());
|
||||
|
||||
case goos::ObjectType::PAIR: {
|
||||
// we've got three cases: quoted thing, proper list, improper list.
|
||||
// we've got four cases: quoted thing, unquoted thing, proper list, improper list.
|
||||
|
||||
// there's probably a better way to do this but i am lazy
|
||||
auto& first = obj.as_pair()->car;
|
||||
if (first.is_symbol() && first.as_symbol()->name == "quote") {
|
||||
if (first.is_symbol("quote")) {
|
||||
auto& second = obj.as_pair()->cdr;
|
||||
if (second.is_pair() && second.as_pair()->cdr.is_empty_list()) {
|
||||
Node result = to_node(second.as_pair()->car);
|
||||
result.quoted++;
|
||||
result.quotes.push_back(Node::QuoteKind::QUOTE);
|
||||
return result;
|
||||
}
|
||||
} else if (first.is_symbol("unquote")) {
|
||||
auto& second = obj.as_pair()->cdr;
|
||||
if (second.is_pair() && second.as_pair()->cdr.is_empty_list()) {
|
||||
Node result = to_node(second.as_pair()->car);
|
||||
result.quotes.push_back(Node::QuoteKind::UNQUOTE);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -148,13 +185,13 @@ void recompute_lengths(const std::vector<Node*>& bfs_order) {
|
||||
Node* node = *it;
|
||||
switch (node->kind) {
|
||||
case Node::Kind::ATOM:
|
||||
node->text_len = node->atom_str.length() + node->quoted;
|
||||
node->text_len = node->atom_str.length() + node->get_quote_length();
|
||||
break;
|
||||
case Node::Kind::IMPROPER_LIST:
|
||||
case Node::Kind::LIST: {
|
||||
if (node->break_list) {
|
||||
// special case compute first line length
|
||||
int first_line_len = 1 + node->quoted; // open paren + quotes
|
||||
int first_line_len = 1 + node->get_quote_length(); // open paren + quotes
|
||||
int nodes_on_first_line =
|
||||
std::min(int(node->child_nodes.size()), int(node->top_line_count));
|
||||
if (nodes_on_first_line > 0) {
|
||||
@@ -176,7 +213,7 @@ void recompute_lengths(const std::vector<Node*>& bfs_order) {
|
||||
|
||||
node->text_len = max_line_len;
|
||||
} else {
|
||||
node->text_len = 1 + node->quoted; // open paren + quotes
|
||||
node->text_len = 1 + node->get_quote_length(); // open paren + quotes
|
||||
for (auto& child : node->child_nodes) {
|
||||
node->text_len += (child.text_len + 1); // space or close paren.
|
||||
}
|
||||
@@ -231,7 +268,8 @@ void break_list(Node* node) {
|
||||
name == "when" || name == "behavior" || name == "lambda" || name == "defpart" ||
|
||||
name == "define") {
|
||||
node->top_line_count = 2;
|
||||
} else if (name == "let" || name == "let*" || name == "rlet") {
|
||||
} else if (name == "let" || name == "let*" || name == "rlet" ||
|
||||
name == "with-dma-buffer-add-bucket") {
|
||||
// special case for things like let.
|
||||
node->top_line_count = 2; // (let <defs>
|
||||
if (node->child_nodes.size() > 1 && node->child_nodes[1].child_nodes.size() > 1 &&
|
||||
@@ -276,7 +314,7 @@ 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"};
|
||||
"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) {
|
||||
@@ -292,7 +330,7 @@ int run_algorithm(const std::vector<Node*>& bfs_order, int line_length) {
|
||||
// - too long
|
||||
// - not already split.
|
||||
// the "magic" of v2 is:
|
||||
// the "too long" check above igores the sublist.
|
||||
// the "too long" check above ignores the sublist.
|
||||
|
||||
int num_broken = 0;
|
||||
std::optional<s32> min_depth;
|
||||
@@ -334,8 +372,8 @@ void append_node_to_string(const Node* node,
|
||||
for (int i = 0; i < init_indent_level; i++) {
|
||||
str.push_back(' ');
|
||||
}
|
||||
for (u32 i = 0; i < node->quoted; i++) {
|
||||
str.push_back('\'');
|
||||
for (auto q : node->quotes) {
|
||||
str.append(quote_symbol(q));
|
||||
}
|
||||
switch (node->kind) {
|
||||
case Node::Kind::ATOM:
|
||||
@@ -347,7 +385,7 @@ void append_node_to_string(const Node* node,
|
||||
str.push_back('(');
|
||||
size_t node_idx = 0;
|
||||
|
||||
int listing_indent = next_indent_level + node->quoted + node->sub_elt_indent;
|
||||
int listing_indent = next_indent_level + node->get_quote_length() + node->sub_elt_indent;
|
||||
int extra_indent = 0;
|
||||
int old_indent = listing_indent;
|
||||
if (node->top_line_count) {
|
||||
@@ -401,7 +439,7 @@ void append_node_to_string(const Node* node,
|
||||
} else {
|
||||
str.push_back('(');
|
||||
ASSERT(!node->child_nodes.empty());
|
||||
int listing_indent = next_indent_level + node->quoted;
|
||||
int listing_indent = next_indent_level + node->get_quote_length();
|
||||
int extra_indent = 1;
|
||||
int c0 = 0;
|
||||
for (auto& child : node->child_nodes) {
|
||||
|
||||
+18
-10
@@ -1,5 +1,11 @@
|
||||
#include "Printer.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/goos/Object.h"
|
||||
#include "common/util/print_float.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace pretty_print {
|
||||
@@ -19,9 +25,7 @@ const std::unordered_map<u32, std::string> const_floats = {{0x40490fda, "PI"},
|
||||
goos::Object float_representation(float value) {
|
||||
u32 int_value;
|
||||
memcpy(&int_value, &value, 4);
|
||||
u8 exp = (int_value >> 23) & 0xff;
|
||||
u32 mant = int_value & 0x7fffff;
|
||||
if ((exp == 0 && mant != 0) || exp == 0xff) {
|
||||
if (!proper_float(value)) {
|
||||
// lg::warn("PS2-incompatible float (0x{:08X}) detected! Writing as the-as cast.", int_value);
|
||||
return pretty_print::build_list("the-as", "float", fmt::format("#x{:x}", int_value));
|
||||
} else if (const_floats.find(int_value) != const_floats.end()) {
|
||||
@@ -34,6 +38,7 @@ goos::Object float_representation(float value) {
|
||||
}
|
||||
|
||||
std::unique_ptr<goos::Reader> pretty_printer_reader;
|
||||
std::mutex pretty_printer_reader_mutex;
|
||||
|
||||
goos::Reader& get_pretty_printer_reader() {
|
||||
if (!pretty_printer_reader) {
|
||||
@@ -43,9 +48,14 @@ goos::Reader& get_pretty_printer_reader() {
|
||||
}
|
||||
|
||||
goos::Object to_symbol(const std::string& str) {
|
||||
std::lock_guard<std::mutex> guard(pretty_printer_reader_mutex);
|
||||
return goos::SymbolObject::make_new(get_pretty_printer_reader().symbolTable, str);
|
||||
}
|
||||
|
||||
goos::Object new_string(const std::string& str) {
|
||||
return goos::StringObject::make_new(str);
|
||||
}
|
||||
|
||||
goos::Object build_list(const std::string& str) {
|
||||
return build_list(to_symbol(str));
|
||||
}
|
||||
@@ -65,14 +75,12 @@ goos::Object build_list(const std::vector<goos::Object>& objects) {
|
||||
// build a list out of an array of forms
|
||||
goos::Object build_list(const goos::Object* objects, int count) {
|
||||
ASSERT(count);
|
||||
auto car = objects[0];
|
||||
goos::Object cdr;
|
||||
if (count - 1) {
|
||||
cdr = build_list(objects + 1, count - 1);
|
||||
} else {
|
||||
cdr = goos::Object::make_empty_list();
|
||||
goos::Object result = goos::Object::make_empty_list();
|
||||
for (int i = count; i-- > 0;) {
|
||||
result = goos::PairObject::make_new(objects[i], result);
|
||||
}
|
||||
return goos::PairObject::make_new(car, cdr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// build a list out of a vector of strings that are converted to symbols
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace pretty_print {
|
||||
// string -> object (as a symbol)
|
||||
goos::Object to_symbol(const std::string& str);
|
||||
|
||||
goos::Object new_string(const std::string& str);
|
||||
|
||||
// list with a single symbol from a string
|
||||
goos::Object build_list(const std::string& str);
|
||||
|
||||
|
||||
+17
-19
@@ -11,8 +11,8 @@
|
||||
|
||||
#include "Reader.h"
|
||||
|
||||
#include "ReplUtils.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/repl/util.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/FontUtils.h"
|
||||
|
||||
@@ -194,7 +194,7 @@ bool Reader::is_valid_source_char(char c) const {
|
||||
/*!
|
||||
* Prompt the user and read the result.
|
||||
*/
|
||||
std::optional<Object> Reader::read_from_stdin(const std::string& prompt, ReplWrapper& repl) {
|
||||
std::optional<Object> Reader::read_from_stdin(const std::string& prompt, REPL::Wrapper& repl) {
|
||||
// escape code will make sure that we remove any color
|
||||
std::string prompt_full = "\033[0m" + prompt;
|
||||
|
||||
@@ -238,17 +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 joined_name;
|
||||
std::string joined_path = fmt::format("{}", fmt::join(file_path, "/"));
|
||||
|
||||
for (const auto& thing : file_path) {
|
||||
if (!joined_name.empty()) {
|
||||
joined_name += '/';
|
||||
}
|
||||
|
||||
joined_name += thing;
|
||||
}
|
||||
|
||||
auto textFrag = std::make_shared<FileText>(file_util::get_file_path(file_path), joined_name);
|
||||
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);
|
||||
@@ -291,11 +283,16 @@ Object Reader::internal_read(std::shared_ptr<SourceText> text,
|
||||
ts.seek_past_whitespace_and_comments();
|
||||
|
||||
// read list!
|
||||
auto objs = read_list(ts, false);
|
||||
if (add_top_level) {
|
||||
return PairObject::make_new(SymbolObject::make_new(symbolTable, "top-level"), objs);
|
||||
} else {
|
||||
return objs;
|
||||
try {
|
||||
auto objs = read_list(ts, false);
|
||||
if (add_top_level) {
|
||||
return PairObject::make_new(SymbolObject::make_new(symbolTable, "top-level"), objs);
|
||||
} else {
|
||||
return objs;
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
lg::print("{}", e.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +340,8 @@ Token Reader::get_next_token(TextStream& stream) {
|
||||
// Second - not a special token, so we read until we get a character that ends the token.
|
||||
while (stream.text_remains()) {
|
||||
char next = stream.peek();
|
||||
if (next == ' ' || next == '\n' || next == '\t' || next == ')' || next == ';' || next == '(') {
|
||||
if (next == ' ' || next == '\n' || next == '\t' || next == '\r' || next == ')' || next == ';' ||
|
||||
next == '(') {
|
||||
return t;
|
||||
} else {
|
||||
// not the end, so add to token.
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "ReplUtils.h"
|
||||
|
||||
#include "common/goos/Object.h"
|
||||
#include "common/goos/TextDB.h"
|
||||
#include "common/repl/util.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
namespace goos {
|
||||
@@ -74,7 +73,7 @@ class Reader {
|
||||
Object read_from_string(const std::string& str,
|
||||
bool add_top_level = true,
|
||||
const std::optional<std::string>& string_name = {});
|
||||
std::optional<Object> read_from_stdin(const std::string& prompt, ReplWrapper& repl);
|
||||
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;
|
||||
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
#include "ReplUtils.h"
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/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?)
|
||||
|
||||
using Replxx = replxx::Replxx;
|
||||
|
||||
void ReplWrapper::clear_screen() {
|
||||
repl.clear_screen();
|
||||
}
|
||||
|
||||
void ReplWrapper::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 ");
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(repl-help)");
|
||||
fmt::print(" for help with common commands and REPL usage.\n");
|
||||
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 ReplWrapper::print_to_repl(const std::string_view& str) {
|
||||
repl.print(str.data());
|
||||
}
|
||||
|
||||
void ReplWrapper::set_history_max_size(size_t len) {
|
||||
repl.set_max_history_size(len);
|
||||
}
|
||||
|
||||
const char* ReplWrapper::readline(const std::string& prompt) {
|
||||
return repl.input(prompt);
|
||||
}
|
||||
|
||||
void ReplWrapper::add_to_history(const std::string& line) {
|
||||
repl.history_add(line);
|
||||
}
|
||||
|
||||
void ReplWrapper::save_history() {
|
||||
fs::path 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 ReplWrapper::load_history() {
|
||||
fs::path 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> ReplWrapper::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 ReplWrapper::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 ReplWrapper::init_default_settings() {
|
||||
repl.set_word_break_characters(" \t");
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "third-party/replxx/include/replxx.hxx"
|
||||
|
||||
using Replxx = replxx::Replxx;
|
||||
|
||||
class ReplWrapper {
|
||||
Replxx repl;
|
||||
|
||||
public:
|
||||
ReplWrapper() {}
|
||||
Replxx& get_repl() { return repl; }
|
||||
void init_default_settings();
|
||||
|
||||
// Functionality / Commands
|
||||
void clear_screen();
|
||||
void print_to_repl(const std::string_view& 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();
|
||||
std::pair<std::string, bool> get_current_repl_token(std::string const& context);
|
||||
|
||||
std::vector<std::string> examples{};
|
||||
using cl = Replxx::Color;
|
||||
std::vector<std::pair<std::string, cl>> regex_colors{};
|
||||
};
|
||||
+33
-7
@@ -44,12 +44,12 @@ void log_message(level log_level, LogTime& now, const char* message) {
|
||||
char date_time_buffer[128];
|
||||
time_t now_seconds = now.tv.tv_sec;
|
||||
auto now_milliseconds = now.tv.tv_usec / 1000;
|
||||
strftime(date_time_buffer, 128, "%Y-%m-%d %H:%M:%S", localtime(&now_seconds));
|
||||
std::string date_string = fmt::format("[{}:{:03d}]", date_time_buffer, now_milliseconds);
|
||||
strftime(date_time_buffer, 128, "%M:%S", localtime(&now_seconds));
|
||||
std::string time_string = fmt::format("[{}:{:03d}]", date_time_buffer, now_milliseconds);
|
||||
#else
|
||||
char date_time_buffer[128];
|
||||
strftime(date_time_buffer, 128, "%Y-%m-%d %H:%M:%S", localtime(&now.tim));
|
||||
std::string date_string = fmt::format("[{}]", date_time_buffer);
|
||||
strftime(date_time_buffer, 128, "%M:%S", localtime(&now.tim));
|
||||
std::string time_string = fmt::format("[{}]", date_time_buffer);
|
||||
#endif
|
||||
|
||||
{
|
||||
@@ -57,7 +57,7 @@ void log_message(level log_level, LogTime& now, const char* message) {
|
||||
if (gLogger.fp && log_level >= gLogger.file_log_level) {
|
||||
// log to file
|
||||
std::string file_string =
|
||||
fmt::format("{} [{}] {}\n", date_string, log_level_names[int(log_level)], message);
|
||||
fmt::format("{} [{}] {}\n", time_string, log_level_names[int(log_level)], message);
|
||||
fwrite(file_string.c_str(), file_string.length(), 1, gLogger.fp);
|
||||
if (log_level >= gLogger.flush_level) {
|
||||
fflush(gLogger.fp);
|
||||
@@ -65,17 +65,43 @@ void log_message(level log_level, LogTime& now, const char* message) {
|
||||
}
|
||||
|
||||
if (log_level >= gLogger.stdout_log_level) {
|
||||
fmt::print("{} [", date_string);
|
||||
fmt::print("{} [", time_string);
|
||||
fmt::print(fg(log_colors[int(log_level)]), "{}", log_level_names[int(log_level)]);
|
||||
fmt::print("] {}\n", message);
|
||||
if (log_level >= gLogger.flush_level) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (log_level == level::die) {
|
||||
exit(-1);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
if (gLogger.fp) {
|
||||
fflush(gLogger.fp);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void log_print(const char* message) {
|
||||
{
|
||||
// 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);
|
||||
if (gLogger.fp) {
|
||||
// Log to File
|
||||
std::string msg(message);
|
||||
fwrite(msg.c_str(), msg.length(), 1, gLogger.fp);
|
||||
fflush(gLogger.fp);
|
||||
}
|
||||
|
||||
if (gLogger.stdout_log_level < lg::level::off) {
|
||||
fmt::print(message);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
#include "third-party/fmt/color.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace lg {
|
||||
@@ -27,6 +28,7 @@ enum class level { trace = 0, debug = 1, info = 2, warn = 3, error = 4, die = 5,
|
||||
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);
|
||||
} // namespace internal
|
||||
|
||||
void set_file(const std::string& filename);
|
||||
@@ -49,6 +51,17 @@ void log(level log_level, const std::string& format, 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(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::format(ts, format, std::forward<Args>(args)...);
|
||||
internal::log_print(formatted_message.c_str());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void trace(const std::string& format, Args&&... args) {
|
||||
log(level::trace, format, std::forward<Args>(args)...);
|
||||
|
||||
@@ -77,7 +77,17 @@ class Vector {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const T other) const {
|
||||
for (int i = 0; i < Size; i++) {
|
||||
if (m_data[i] != other) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const Vector<T, Size>& other) const { return !((*this) == other); }
|
||||
bool operator!=(const T other) const { return !((*this) == other); }
|
||||
|
||||
const T length() const { return std::sqrt(squared_length()); }
|
||||
|
||||
@@ -225,6 +235,15 @@ class Vector {
|
||||
return result + "]";
|
||||
}
|
||||
|
||||
std::string to_string_hex_word() const {
|
||||
std::string result = "[";
|
||||
for (auto x : m_data) {
|
||||
result.append(fmt::format("0x{:08x} ", x));
|
||||
}
|
||||
result.pop_back();
|
||||
return result + "]";
|
||||
}
|
||||
|
||||
T* data() { return m_data; }
|
||||
const T* data() const { return m_data; }
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "common/versions.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace REPL {
|
||||
void to_json(json& j, const Config& obj) {
|
||||
j = json{
|
||||
{"numConnectToTargetAttempts", obj.target_connect_attempts},
|
||||
{"asmFileSearchDirs", obj.asm_file_search_dirs},
|
||||
{"keybinds", obj.keybinds},
|
||||
};
|
||||
}
|
||||
|
||||
void from_json(const json& j, Config& obj) {
|
||||
if (j.contains("numConnectToTargetAttempts")) {
|
||||
j.at("numConnectToTargetAttempts").get_to(obj.target_connect_attempts);
|
||||
}
|
||||
if (j.contains("asmFileSearchDirs")) {
|
||||
j.at("asmFileSearchDirs").get_to(obj.asm_file_search_dirs);
|
||||
}
|
||||
if (j.contains("appendKeybinds")) {
|
||||
j.at("appendKeybinds").get_to(obj.append_keybinds);
|
||||
}
|
||||
if (j.contains("keybinds")) {
|
||||
std::vector<KeyBind> keybinds = j.at("keybinds");
|
||||
if (!obj.append_keybinds) {
|
||||
obj.keybinds = keybinds;
|
||||
} else {
|
||||
// append the keybinds
|
||||
// - start with the provided ones
|
||||
// - skip ones from the default set if they have the same key + modifier combination
|
||||
for (const auto& default_bind : obj.keybinds) {
|
||||
// check if it's a duplicate bind
|
||||
bool duplicate = false;
|
||||
for (const auto& new_bind : keybinds) {
|
||||
if (new_bind.key == default_bind.key && new_bind.modifier == default_bind.modifier) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!duplicate) {
|
||||
keybinds.push_back(default_bind);
|
||||
}
|
||||
}
|
||||
obj.keybinds = keybinds;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
std::string KeyBind::string() const {
|
||||
switch (modifier) {
|
||||
case KeyBind::Modifier::CTRL:
|
||||
return fmt::format("CTRL-{}", key);
|
||||
case KeyBind::Modifier::SHIFT:
|
||||
return fmt::format("SHIFT-{}", key);
|
||||
case KeyBind::Modifier::META:
|
||||
return fmt::format("META-{}", key);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const KeyBind& obj) {
|
||||
j = json{{"description", obj.description}, {"command", obj.command}, {"key", obj.key}};
|
||||
switch (obj.modifier) {
|
||||
case KeyBind::Modifier::CTRL:
|
||||
j["modifier"] = "ctrl";
|
||||
break;
|
||||
case KeyBind::Modifier::SHIFT:
|
||||
j["modifier"] = "shift";
|
||||
break;
|
||||
case KeyBind::Modifier::META:
|
||||
j["modifier"] = "meta";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const json& j, KeyBind& obj) {
|
||||
j.at("description").get_to(obj.description);
|
||||
j.at("command").get_to(obj.command);
|
||||
j.at("key").get_to(obj.key);
|
||||
auto modString = j.at("modifier").get<std::string>();
|
||||
if (modString == "ctrl") {
|
||||
obj.modifier = KeyBind::Modifier::CTRL;
|
||||
} else if (modString == "shift") {
|
||||
obj.modifier = KeyBind::Modifier::SHIFT;
|
||||
} else if (modString == "meta") {
|
||||
obj.modifier = KeyBind::Modifier::META;
|
||||
}
|
||||
}
|
||||
} // namespace REPL
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/versions.h"
|
||||
|
||||
#include "third-party/json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace REPL {
|
||||
struct KeyBind {
|
||||
// NOTE - in my experience, meta doesn't work on windows and shift is probably a bad idea when
|
||||
// typing text! but I leave it up to the user.
|
||||
enum class Modifier { CTRL, SHIFT, META };
|
||||
Modifier modifier;
|
||||
std::string key;
|
||||
std::string description;
|
||||
std::string command;
|
||||
|
||||
std::string string() const;
|
||||
};
|
||||
void to_json(json& j, const KeyBind& obj);
|
||||
void from_json(const json& j, KeyBind& obj);
|
||||
|
||||
struct Config {
|
||||
GameVersion game_version;
|
||||
Config(GameVersion _game_version) : game_version(_game_version){};
|
||||
|
||||
// this is the default REPL configuration
|
||||
int target_connect_attempts = 30;
|
||||
std::vector<std::string> asm_file_search_dirs = {};
|
||||
bool append_keybinds = true;
|
||||
std::vector<KeyBind> keybinds = {
|
||||
{KeyBind::Modifier::CTRL, "T", "Starts up the game runtime", "(test-play)"},
|
||||
{KeyBind::Modifier::CTRL, "Q", "Exit the REPL", "(e)"},
|
||||
{KeyBind::Modifier::CTRL, "L", "Listen to an available game process", "(lt)"},
|
||||
{KeyBind::Modifier::CTRL, "W",
|
||||
"Halt the attached process so you can re-launch a crashed game", "(:stop)"},
|
||||
{KeyBind::Modifier::CTRL, "G", "Attach the debugger to the process", "(dbgc)"},
|
||||
{KeyBind::Modifier::CTRL, "B", "Displays the most recently caught backtrace", "(:di)"},
|
||||
{KeyBind::Modifier::CTRL, "N", "Full build of the game", "(mi)"}};
|
||||
};
|
||||
void to_json(json& j, const Config& obj);
|
||||
void from_json(const json& j, Config& obj);
|
||||
|
||||
} // namespace REPL
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "ReplServer.h"
|
||||
|
||||
#include "common/cross_sockets/XSocket.h"
|
||||
#include <common/versions.h>
|
||||
#include "common/versions.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
@@ -107,8 +107,8 @@ std::optional<std::string> ReplServer::get_msg() {
|
||||
auto req_bytes = read_from_socket(sock, header_buffer.data(), header_buffer.size());
|
||||
if (req_bytes == 0) {
|
||||
// Socket disconnected
|
||||
// TODO - add a queue of messages in the ReplWrapper so we can print _BEFORE_ the prompt is
|
||||
// output
|
||||
// TODO - add a queue of messages in the REPL::Wrapper so we can print _BEFORE_ the prompt
|
||||
// is output
|
||||
fmt::print("[nREPL:{}] Client Disconnected: {}\n", tcp_port, inet_ntoa(addr.sin_addr),
|
||||
ntohs(addr.sin_port), sock);
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "util.h"
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/json_util.h"
|
||||
#include "common/util/string_util.h"
|
||||
#include "common/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 = 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 = 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");
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - command to print out keybinds
|
||||
|
||||
void Wrapper::reload_startup_file() {
|
||||
startup_file = load_user_startup_file(username);
|
||||
}
|
||||
|
||||
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 "#f";
|
||||
}
|
||||
|
||||
StartupFile load_user_startup_file(const std::string& username) {
|
||||
// Check for a `startup.gc` file, each line will be executed on the REPL on startup
|
||||
auto startup_file_path =
|
||||
file_util::get_jak_project_dir() / "goal_src" / "user" / username / "startup.gc";
|
||||
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
|
||||
@@ -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);
|
||||
REPL::Config load_repl_config(const std::string& username, const GameVersion game_version);
|
||||
} // namespace REPL
|
||||
@@ -389,7 +389,7 @@ void GameSubtitleGroups::hydrate_from_asset_file() {
|
||||
m_groups[key] = val.get<std::vector<std::string>>();
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
fmt::print("Bad subtitle group entry - {} - {}", key, ex.what());
|
||||
lg::print("Bad subtitle group entry - {} - {}", key, ex.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,6 @@ inline u32 rgba16_to_rgba32(u32 in) {
|
||||
}
|
||||
|
||||
// texture format enums
|
||||
enum class PSM { PSMCT16 = 0x02, PSMT8 = 0x13, PSMT4 = 0x14 };
|
||||
enum class PSM { PSMCT32 = 0x0, PSMCT16 = 0x02, PSMT8 = 0x13, PSMT4 = 0x14 };
|
||||
// clut format enums
|
||||
enum class CPSM { PSMCT32 = 0x0, PSMCT16 = 0x02 };
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
@@ -196,8 +197,8 @@ std::string Type::get_name() const {
|
||||
|
||||
std::string Type::get_runtime_name() const {
|
||||
if (!m_allow_in_runtime) {
|
||||
fmt::print("[TypeSystem] Tried to use type {} as a runtime type, which is not allowed.\n",
|
||||
get_name());
|
||||
lg::print("[TypeSystem] Tried to use type {} as a runtime type, which is not allowed.\n",
|
||||
get_name());
|
||||
throw std::runtime_error("get_runtime_name");
|
||||
}
|
||||
return m_runtime_name;
|
||||
@@ -885,9 +886,9 @@ int BasicType::get_offset() const {
|
||||
}
|
||||
|
||||
int BasicType::get_inline_array_start_alignment() const {
|
||||
if (m_pack) {
|
||||
if (m_pack || m_allow_misalign) {
|
||||
// make elements of inline array the minimum allowable alignment.
|
||||
int alignment = 8;
|
||||
int alignment = m_allow_misalign ? 4 : 8;
|
||||
// TODO - I don't know if GOAL actually did this check, maybe packed inline arrays could
|
||||
// violate these?
|
||||
for (const auto& field : m_fields) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "TypeSystem.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace {
|
||||
@@ -452,8 +454,8 @@ void try_reverse_lookup(const FieldReverseLookupInput& input,
|
||||
FieldReverseMultiLookupOutput* output,
|
||||
int max_count) {
|
||||
if (debug_reverse_lookup) {
|
||||
fmt::print(" try_reverse_lookup on {} offset {} deref {} stride {}\n", input.base_type.print(),
|
||||
input.offset, input.deref.has_value(), input.stride);
|
||||
lg::debug(" try_reverse_lookup on {} offset {} deref {} stride {}", input.base_type.print(),
|
||||
input.offset, input.deref.has_value(), input.stride);
|
||||
}
|
||||
|
||||
auto base_input_type = input.base_type.base_type();
|
||||
@@ -484,15 +486,15 @@ FieldReverseLookupOutput TypeSystem::reverse_field_lookup(
|
||||
|
||||
/*
|
||||
if (multi_result.results.size() > 1) {
|
||||
fmt::print("Multiple:\n");
|
||||
lg::print("Multiple:\n");
|
||||
for (auto& result : multi_result.results) {
|
||||
fmt::print(" [{}] [{}] ", result.total_score, result.result_type.print());
|
||||
lg::print(" [{}] [{}] ", result.total_score, result.result_type.print());
|
||||
for (auto& tok : result.tokens) {
|
||||
fmt::print("{} ", tok.print());
|
||||
lg::print("{} ", tok.print());
|
||||
}
|
||||
fmt::print("\n");
|
||||
lg::print("\n");
|
||||
}
|
||||
fmt::print("\n\n\n");
|
||||
lg::print("\n\n\n");
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -510,8 +512,8 @@ FieldReverseMultiLookupOutput TypeSystem::reverse_field_multi_lookup(
|
||||
const FieldReverseLookupInput& input,
|
||||
int max_count) const {
|
||||
if (debug_reverse_lookup) {
|
||||
fmt::print("reverse_field_lookup on {} offset {} deref {} stride {}\n", input.base_type.print(),
|
||||
input.offset, input.deref.has_value(), input.stride);
|
||||
lg::debug("reverse_field_lookup on {} offset {} deref {} stride {}", input.base_type.print(),
|
||||
input.offset, input.deref.has_value(), input.stride);
|
||||
}
|
||||
|
||||
FieldReverseMultiLookupOutput result;
|
||||
|
||||
@@ -94,7 +94,7 @@ class TypeSpec {
|
||||
void modify_tag(const std::string& tag_name, const std::string& tag_value);
|
||||
void add_or_modify_tag(const std::string& tag_name, const std::string& tag_value);
|
||||
|
||||
const std::string base_type() const { return m_type; }
|
||||
const std::string& base_type() const { return m_type; }
|
||||
|
||||
bool has_single_arg() const {
|
||||
if (m_arguments) {
|
||||
@@ -132,6 +132,12 @@ class TypeSpec {
|
||||
return m_arguments->back();
|
||||
}
|
||||
|
||||
TypeSpec& last_arg() {
|
||||
ASSERT(m_arguments);
|
||||
ASSERT(!m_arguments->empty());
|
||||
return m_arguments->back();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
if (!m_arguments) {
|
||||
return true;
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
|
||||
#include "TypeSystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/math_util.h"
|
||||
|
||||
@@ -18,11 +20,11 @@
|
||||
namespace {
|
||||
template <typename... Args>
|
||||
[[noreturn]] void throw_typesystem_error(const std::string& str, Args&&... args) {
|
||||
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Type Error! --\n");
|
||||
lg::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Type Error! --\n");
|
||||
if (!str.empty() && str.back() == '\n') {
|
||||
fmt::print(fg(fmt::color::yellow), str, std::forward<Args>(args)...);
|
||||
lg::print(fg(fmt::color::yellow), str, std::forward<Args>(args)...);
|
||||
} else {
|
||||
fmt::print(fg(fmt::color::yellow), str + '\n', std::forward<Args>(args)...);
|
||||
lg::print(fg(fmt::color::yellow), str + '\n', std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
throw std::runtime_error(
|
||||
@@ -59,9 +61,12 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr<Type> type)
|
||||
if (*kv->second != *type) {
|
||||
// exists, and we are trying to change it!
|
||||
|
||||
if (m_allow_redefinition) {
|
||||
fmt::print("[TypeSystem] Type {} was originally\n{}\nand is redefined as\n{}\n",
|
||||
kv->second->get_name(), kv->second->print(), type->print());
|
||||
// Check if the type is allowed to be redefined
|
||||
if (m_allow_redefinition ||
|
||||
std::find(m_types_allowed_to_be_redefined.begin(), m_types_allowed_to_be_redefined.end(),
|
||||
kv->second->get_name()) != m_types_allowed_to_be_redefined.end()) {
|
||||
lg::print("[TypeSystem] Type {} was originally\n{}\nand is redefined as\n{}\n",
|
||||
kv->second->get_name(), kv->second->print(), type->print());
|
||||
// extra dangerous, we have allowed type redefinition!
|
||||
|
||||
// keep the unique_ptr around, just in case somebody references this old type pointer.
|
||||
@@ -949,8 +954,9 @@ int TypeSystem::add_field_to_type(StructureType* type,
|
||||
int aligned_offset = align(offset, field_alignment);
|
||||
field.mark_as_user_placed();
|
||||
if (offset != aligned_offset) {
|
||||
throw_typesystem_error("Tried to place field {} at {}, but it is not aligned correctly\n",
|
||||
field_name, offset);
|
||||
throw_typesystem_error(
|
||||
"Tried to place field {} at {}, but it is not aligned correctly, requires {}\n",
|
||||
field_name, offset, field_alignment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1263,6 +1269,7 @@ int TypeSystem::get_size_in_type(const Field& field) const {
|
||||
"Attempted to use `{}` inline, this probably isn't what you wanted.\n",
|
||||
field_type->get_name());
|
||||
}
|
||||
// TODO - crashes LSP
|
||||
ASSERT(field_type->is_reference());
|
||||
return field.array_size() * align(field_type->get_size_in_memory(),
|
||||
field_type->get_inline_array_stride_alignment());
|
||||
@@ -1301,6 +1308,163 @@ int TypeSystem::get_size_in_type(const Field& field) const {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> TypeSystem::get_all_type_names() {
|
||||
std::vector<std::string> results = {};
|
||||
for (const auto& [type_name, type_info] : m_types) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<std::string> TypeSystem::search_types_by_parent_type(
|
||||
const std::string& parent_type,
|
||||
const std::optional<std::vector<std::string>>& existing_matches) {
|
||||
std::vector<std::string> results = {};
|
||||
// If we've been given a list of already matched types, narrow it down from there, otherwise
|
||||
// iterate through the entire map
|
||||
if (existing_matches) {
|
||||
for (const auto& type_name : existing_matches.value()) {
|
||||
if (typecheck_base_types(parent_type, type_name, false)) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& [type_name, type_info] : m_types) {
|
||||
// Only NullType's have no parent
|
||||
if (!type_info->has_parent()) {
|
||||
continue;
|
||||
}
|
||||
if (typecheck_base_types(parent_type, type_name, false)) {
|
||||
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) {
|
||||
std::vector<std::string> results = {};
|
||||
// If we've been given a list of already matched types, narrow it down from there, otherwise
|
||||
// iterate through the entire map
|
||||
if (existing_matches) {
|
||||
for (const auto& type_name : existing_matches.value()) {
|
||||
if (get_type_method_count(type_name) - 1 >= minimum_method_id) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& [type_name, type_info] : m_types) {
|
||||
if (get_type_method_count(type_name) - 1 >= minimum_method_id) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<std::string> TypeSystem::search_types_by_size(
|
||||
const int min_size,
|
||||
const std::optional<int> max_size,
|
||||
const std::optional<std::vector<std::string>>& existing_matches) {
|
||||
std::vector<std::string> results = {};
|
||||
// If we've been given a list of already matched types, narrow it down from there, otherwise
|
||||
// iterate through the entire map
|
||||
if (existing_matches) {
|
||||
for (const auto& type_name : existing_matches.value()) {
|
||||
const auto size_of_type = m_types[type_name]->get_size_in_memory();
|
||||
if (max_size && size_of_type <= max_size && size_of_type >= min_size) {
|
||||
results.push_back(type_name);
|
||||
} else if (!max_size && size_of_type == min_size) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& [type_name, type_info] : m_types) {
|
||||
// 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();
|
||||
if (max_size && size_of_type <= max_size && size_of_type >= min_size) {
|
||||
results.push_back(type_name);
|
||||
} else if (!max_size && size_of_type == min_size) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<std::string> TypeSystem::search_types_by_fields(
|
||||
const std::vector<TypeSearchFieldInput>& search_fields,
|
||||
const std::optional<std::vector<std::string>>& existing_matches) {
|
||||
// TODO - maybe support partial matches eventually
|
||||
std::vector<std::string> results = {};
|
||||
if (existing_matches) {
|
||||
for (const auto& type_name : existing_matches.value()) {
|
||||
// For each type, look at it's fields
|
||||
if (dynamic_cast<StructureType*>(m_types[type_name].get()) != nullptr) {
|
||||
bool type_valid = true;
|
||||
auto struct_type = dynamic_cast<StructureType*>(m_types[type_name].get());
|
||||
for (const auto& req_field : search_fields) {
|
||||
bool field_valid = false;
|
||||
// iterate through the type's fields until one is found with the right offset
|
||||
// once found, check the underlying type name, if it doesn't match it's invalid
|
||||
// if we don't find one with that offset, it's also invalid
|
||||
for (const auto& type_field : struct_type->fields()) {
|
||||
if (type_field.offset() == req_field.field_offset &&
|
||||
type_field.type().base_type() == req_field.field_type_name) {
|
||||
field_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!field_valid) {
|
||||
type_valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (type_valid) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& [type_name, type_info] : m_types) {
|
||||
// For each type, look at it's fields
|
||||
if (dynamic_cast<StructureType*>(type_info.get()) != nullptr) {
|
||||
bool type_valid = true;
|
||||
auto struct_type = dynamic_cast<StructureType*>(type_info.get());
|
||||
for (const auto& req_field : search_fields) {
|
||||
bool field_valid = false;
|
||||
// iterate through the type's fields until one is found with the right offset
|
||||
// once found, check the underlying type name, if it doesn't match it's invalid
|
||||
// if we don't find one with that offset, it's also invalid
|
||||
for (const auto& type_field : struct_type->fields()) {
|
||||
if (type_field.offset() == req_field.field_offset &&
|
||||
type_field.type().base_type() == req_field.field_type_name) {
|
||||
field_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!field_valid) {
|
||||
type_valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (type_valid) {
|
||||
results.push_back(type_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add a simple structure type - don't use this outside of add_builtin_types as it forces you to do
|
||||
* things in the wrong order.
|
||||
@@ -1407,11 +1571,11 @@ bool TypeSystem::typecheck_and_throw(const TypeSpec& expected,
|
||||
if (!success) {
|
||||
if (print_on_error) {
|
||||
if (error_source_name.empty()) {
|
||||
fmt::print("[TypeSystem] Got type \"{}\" when expecting \"{}\"\n", actual.print(),
|
||||
expected.print());
|
||||
lg::print("[TypeSystem] Got type \"{}\" when expecting \"{}\"\n", actual.print(),
|
||||
expected.print());
|
||||
} else {
|
||||
fmt::print("[TypeSystem] For {}, got type \"{}\" when expecting \"{}\"\n",
|
||||
error_source_name, actual.print(), expected.print());
|
||||
lg::print("[TypeSystem] For {}, got type \"{}\" when expecting \"{}\"\n", error_source_name,
|
||||
actual.print(), expected.print());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -259,6 +259,33 @@ class TypeSystem {
|
||||
|
||||
int get_size_in_type(const Field& field) const;
|
||||
|
||||
void add_type_to_allowed_redefinition_list(const std::string& type_name) {
|
||||
m_types_allowed_to_be_redefined.push_back(type_name);
|
||||
}
|
||||
|
||||
std::vector<std::string> get_all_type_names();
|
||||
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_minimum_method_id(
|
||||
const int minimum_method_id,
|
||||
const std::optional<std::vector<std::string>>& existing_matches = {});
|
||||
|
||||
std::vector<std::string> search_types_by_size(
|
||||
const int min_size,
|
||||
const std::optional<int> max_size,
|
||||
const std::optional<std::vector<std::string>>& existing_matches = {});
|
||||
|
||||
struct TypeSearchFieldInput {
|
||||
std::string field_type_name;
|
||||
int field_offset;
|
||||
};
|
||||
|
||||
std::vector<std::string> search_types_by_fields(
|
||||
const std::vector<TypeSearchFieldInput>& search_fields,
|
||||
const std::optional<std::vector<std::string>>& existing_matches = {});
|
||||
|
||||
private:
|
||||
std::string lca_base(const std::string& a, const std::string& b) const;
|
||||
bool typecheck_base_types(const std::string& expected,
|
||||
@@ -284,6 +311,7 @@ class TypeSystem {
|
||||
|
||||
std::vector<std::unique_ptr<Type>> m_old_types;
|
||||
|
||||
std::vector<std::string> m_types_allowed_to_be_redefined;
|
||||
bool m_allow_redefinition = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "deftype.h"
|
||||
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
@@ -59,7 +61,7 @@ EnumType* parse_defenum(const goos::Object& defenum,
|
||||
if (iter->is_pair() && car(iter).is_string()) {
|
||||
// TODO - docstring - store and use docstring if coming from the compiler
|
||||
if (symbol_metadata) {
|
||||
symbol_metadata->docstring = car(iter).as_string()->data;
|
||||
symbol_metadata->docstring = str_util::trim_newline_indents(car(iter).as_string()->data);
|
||||
}
|
||||
iter = cdr(iter);
|
||||
}
|
||||
@@ -84,8 +86,8 @@ EnumType* parse_defenum(const goos::Object& defenum,
|
||||
} else if (symbol_string(option_value) == "#f") {
|
||||
is_bitfield = false;
|
||||
} else {
|
||||
fmt::print("Invalid option {} to :bitfield option.\n", option_value.print());
|
||||
throw std::runtime_error("invalid bitfield option");
|
||||
throw std::runtime_error(
|
||||
fmt::format("Invalid option {} to :bitfield option.\n", option_value.print()));
|
||||
}
|
||||
} else if (option_name == ":copy-entries") {
|
||||
auto other_info = ts->try_enum_lookup(parse_typespec(ts, option_value));
|
||||
@@ -100,8 +102,7 @@ EnumType* parse_defenum(const goos::Object& defenum,
|
||||
entries[e.first] = e.second;
|
||||
}
|
||||
} else {
|
||||
fmt::print("Unknown option {} for defenum.\n", option_name);
|
||||
throw std::runtime_error("unknown option for defenum");
|
||||
throw std::runtime_error(fmt::format("Unknown option {} for defenum.\n", option_name));
|
||||
}
|
||||
|
||||
if (iter->is_pair()) {
|
||||
@@ -125,12 +126,13 @@ EnumType* parse_defenum(const goos::Object& defenum,
|
||||
if (!rest->is_empty_list()) {
|
||||
auto& value = car(rest);
|
||||
if (!value.is_int()) {
|
||||
fmt::print("Expected integer for enum value, got {}\n", value.print());
|
||||
throw std::runtime_error(
|
||||
fmt::format("Expected integer for enum value, got {}\n", value.print()));
|
||||
}
|
||||
|
||||
auto entry_val = value.integer_obj.value;
|
||||
if (!integer_fits(entry_val, type->get_load_size(), type->get_load_signed())) {
|
||||
fmt::print("Integer {} does not fit inside a {}\n", entry_val, type->get_name());
|
||||
lg::warn("Integer {} does not fit inside a {}", entry_val, type->get_name());
|
||||
}
|
||||
|
||||
if (!entries.size()) {
|
||||
@@ -140,7 +142,8 @@ EnumType* parse_defenum(const goos::Object& defenum,
|
||||
|
||||
rest = cdr(rest);
|
||||
if (!rest->is_empty_list()) {
|
||||
fmt::print("Got too many items in defenum {} entry {}\n", name, entry_name);
|
||||
throw std::runtime_error(
|
||||
fmt::format("Got too many items in defenum {} entry {}\n", name, entry_name));
|
||||
}
|
||||
|
||||
entries[entry_name] = entry_val;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "deftype.h"
|
||||
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
@@ -204,7 +206,7 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
// check for docstring
|
||||
std::optional<std::string> docstring;
|
||||
if (obj->is_pair() && car(obj).is_string()) {
|
||||
docstring = car(obj).as_string()->data;
|
||||
docstring = str_util::trim_newline_indents(car(obj).as_string()->data);
|
||||
obj = cdr(obj);
|
||||
}
|
||||
auto& args = car(obj);
|
||||
@@ -260,8 +262,8 @@ void declare_method(Type* type, TypeSystem* type_system, const goos::Object& def
|
||||
if (id != -1) {
|
||||
// method id assert!
|
||||
if (id != info.id) {
|
||||
printf("WARNING - ID assert failed on method %s of type %s (wanted %d got %d)\n",
|
||||
method_name.c_str(), type->get_name().c_str(), id, info.id);
|
||||
lg::print("WARNING - ID assert failed on method {} of type {} (wanted {} got {})\n",
|
||||
method_name.c_str(), type->get_name().c_str(), id, info.id);
|
||||
throw std::runtime_error("Method ID assert failed");
|
||||
}
|
||||
}
|
||||
@@ -402,7 +404,7 @@ StructureDefResult parse_structure_def(
|
||||
fmt::format("Process heap underflow in type {}: heap-base is {} vs. auto-detected {}",
|
||||
type->get_name(), flags.heap_base, auto_hb));
|
||||
//} else if (flags.heap_base != auto_hb) {
|
||||
// fmt::print("Type {} has manual heap-base ({} vs {}). This is fine. \n", type->get_name(),
|
||||
// lg::print("Type {} has manual heap-base ({} vs {}). This is fine. \n", type->get_name(),
|
||||
// flags.heap_base, auto_hb);
|
||||
}
|
||||
}
|
||||
@@ -587,7 +589,7 @@ DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
iter = cdr(iter);
|
||||
// check for docstring
|
||||
if (iter->is_pair() && car(iter).is_string()) {
|
||||
symbol_metadata.docstring = car(iter).as_string()->data;
|
||||
symbol_metadata.docstring = str_util::trim_newline_indents(car(iter).as_string()->data);
|
||||
iter = cdr(iter);
|
||||
}
|
||||
auto& field_list_obj = car(iter);
|
||||
@@ -623,14 +625,10 @@ DeftypeResult parse_deftype(const goos::Object& deftype,
|
||||
new_type->set_pack(true);
|
||||
}
|
||||
if (sr.allow_misaligned) {
|
||||
fmt::print(
|
||||
"[TypeSystem] :allow-misaligned was set on {}, which is a basic and cannot "
|
||||
"be misaligned\n",
|
||||
name);
|
||||
throw std::runtime_error("invalid pack option on basic");
|
||||
new_type->set_allow_misalign(true);
|
||||
}
|
||||
if (sr.always_stack_singleton) {
|
||||
fmt::print(
|
||||
lg::print(
|
||||
"[TypeSystem] :always-stack-singleton was set on {}, which is a basic and cannot "
|
||||
"be a stack singleton\n",
|
||||
name);
|
||||
|
||||
@@ -87,6 +87,22 @@ TypeSpec get_state_handler_type(StateHandler kind, const TypeSpec& state_type) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_state_handler_arg_names(StateHandler kind) {
|
||||
switch (kind) {
|
||||
case StateHandler::CODE:
|
||||
// can have args, but are arbitrary
|
||||
case StateHandler::ENTER:
|
||||
case StateHandler::TRANS:
|
||||
case StateHandler::POST:
|
||||
case StateHandler::EXIT:
|
||||
return {};
|
||||
case StateHandler::EVENT:
|
||||
return {"proc", "arg1", "event-type", "event"};
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
TypeSpec func_to_state_type(const TypeSpec& func_type, const TypeSpec& proc_type) {
|
||||
TypeSpec result("state");
|
||||
|
||||
@@ -17,6 +17,7 @@ StateHandler handler_name_to_kind(const std::string& name);
|
||||
std::string handler_kind_to_name(StateHandler kind);
|
||||
TypeSpec get_state_handler_type(const std::string& handler_name, const TypeSpec& state_type);
|
||||
TypeSpec get_state_handler_type(StateHandler kind, const TypeSpec& state_type);
|
||||
std::vector<std::string> get_state_handler_arg_names(StateHandler kind);
|
||||
|
||||
std::optional<TypeSpec> get_state_type_from_enter_and_code(const TypeSpec& enter_func_type,
|
||||
const TypeSpec& code_func_type,
|
||||
|
||||
@@ -6,20 +6,23 @@
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
void private_assert_failed(const char* expr,
|
||||
const char* file,
|
||||
int line,
|
||||
const char* function,
|
||||
const char* msg) {
|
||||
if (!msg || msg[0] == '\0') {
|
||||
fprintf(stderr, "Assertion failed: '%s'\n\tSource: %s:%d\n\tFunction: %s\n", expr, file, line,
|
||||
function);
|
||||
std::string log = fmt::format("Assertion failed: '{}'\n\tSource: {}:{}\n\tFunction: {}\n", expr,
|
||||
file, line, function);
|
||||
lg::die(log);
|
||||
} else {
|
||||
fprintf(stderr, "Assertion failed: '%s'\n\tMessage: %s\n\tSource: %s:%d\n\tFunction: %s\n",
|
||||
expr, msg, file, line, function);
|
||||
std::string log =
|
||||
fmt::format("Assertion failed: '{}'\n\tMessage: {}\n\tSource: {}:{}\n\tFunction: {}\n",
|
||||
expr, msg, file, line, function);
|
||||
lg::die(log);
|
||||
}
|
||||
fflush(stdout); // ensure any stdout logs are flushed before we terminate
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,14 @@
|
||||
#define ASSERT(EX) \
|
||||
(void)((EX) || (private_assert_failed(#EX, __FILE__, __LINE__, __PRETTY_FUNCTION__), 0))
|
||||
|
||||
#define ASSERT_NOT_REACHED() \
|
||||
(void)((private_assert_failed("not reached", __FILE__, __LINE__, __PRETTY_FUNCTION__), 0))
|
||||
|
||||
#define ASSERT_MSG(EXPR, STR) \
|
||||
(void)((EXPR) || (private_assert_failed(#EXPR, __FILE__, __LINE__, __PRETTY_FUNCTION__, STR), 0))
|
||||
|
||||
#define ASSERT_NOT_REACHED_MSG(STR) \
|
||||
(void)((private_assert_failed("not reached", __FILE__, __LINE__, __PRETTY_FUNCTION__, STR), 0))
|
||||
#else
|
||||
|
||||
#define ASSERT(EX) ((void)0)
|
||||
|
||||
@@ -76,6 +76,11 @@ fs::path get_user_memcard_dir(GameVersion game_version) {
|
||||
return get_user_config_dir() / game_version_name / "saves";
|
||||
}
|
||||
|
||||
fs::path get_user_misc_dir(GameVersion game_version) {
|
||||
auto game_version_name = game_version_names[game_version];
|
||||
return get_user_config_dir() / game_version_name / "misc";
|
||||
}
|
||||
|
||||
struct {
|
||||
bool initialized = false;
|
||||
fs::path path_to_data;
|
||||
@@ -139,7 +144,7 @@ bool setup_project_path(std::optional<fs::path> project_path_override) {
|
||||
if (project_path_override) {
|
||||
gFilePathInfo.path_to_data = *project_path_override;
|
||||
gFilePathInfo.initialized = true;
|
||||
fmt::print("Using explicitly set project path: {}\n", project_path_override->string());
|
||||
lg::info("Using explicitly set project path: {}", project_path_override->string());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -147,7 +152,7 @@ bool setup_project_path(std::optional<fs::path> project_path_override) {
|
||||
if (data_path) {
|
||||
gFilePathInfo.path_to_data = *data_path;
|
||||
gFilePathInfo.initialized = true;
|
||||
fmt::print("Using data path: {}\n", data_path->string());
|
||||
lg::info("Using data path: {}", data_path->string());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,11 +160,11 @@ bool setup_project_path(std::optional<fs::path> project_path_override) {
|
||||
if (development_repo_path) {
|
||||
gFilePathInfo.path_to_data = *development_repo_path;
|
||||
gFilePathInfo.initialized = true;
|
||||
fmt::print("Using development repo path: {}\n", *development_repo_path);
|
||||
lg::info("Using development repo path: {}", *development_repo_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
fmt::print("Failed to initialize project path.\n");
|
||||
lg::error("Failed to initialize project path.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -316,10 +321,23 @@ std::string base_name(const std::string& filename) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return filename.substr(pos);
|
||||
}
|
||||
|
||||
std::string base_name_no_ext(const std::string& filename) {
|
||||
size_t pos = 0;
|
||||
ASSERT(!filename.empty());
|
||||
for (size_t i = filename.size() - 1; i-- > 0;) {
|
||||
if (filename.at(i) == '/' || filename.at(i) == '\\') {
|
||||
pos = (i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string file_name = filename.substr(pos);
|
||||
return file_name.substr(0, file_name.find_last_of('.'));
|
||||
;
|
||||
}
|
||||
|
||||
void ISONameFromAnimationName(char* dst, const char* src) {
|
||||
// The Animation Name is a bunch of words separated by dashes
|
||||
|
||||
@@ -524,7 +542,7 @@ std::vector<fs::path> find_files_recursively(const fs::path& base_dir, const std
|
||||
std::vector<fs::path> files = {};
|
||||
for (auto& p : fs::recursive_directory_iterator(base_dir)) {
|
||||
if (p.is_regular_file()) {
|
||||
if (std::regex_match(fs::path(p.path()).filename().string(), pattern)) {
|
||||
if (std::regex_match(p.path().filename().string(), pattern)) {
|
||||
files.push_back(p.path());
|
||||
}
|
||||
}
|
||||
@@ -532,4 +550,14 @@ std::vector<fs::path> find_files_recursively(const fs::path& base_dir, const std
|
||||
return files;
|
||||
}
|
||||
|
||||
std::vector<fs::path> find_directories_in_dir(const fs::path& base_dir) {
|
||||
std::vector<fs::path> dirs = {};
|
||||
for (auto& p : fs::recursive_directory_iterator(base_dir)) {
|
||||
if (p.is_directory()) {
|
||||
dirs.push_back(p.path());
|
||||
}
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
} // namespace file_util
|
||||
|
||||
@@ -31,6 +31,7 @@ fs::path get_user_home_dir();
|
||||
fs::path get_user_config_dir();
|
||||
fs::path get_user_settings_dir(GameVersion game_version);
|
||||
fs::path get_user_memcard_dir(GameVersion game_version);
|
||||
fs::path get_user_misc_dir(GameVersion game_version);
|
||||
fs::path get_jak_project_dir();
|
||||
|
||||
bool create_dir_if_needed(const fs::path& path);
|
||||
@@ -51,6 +52,7 @@ bool is_printable_char(char c);
|
||||
std::string combine_path(const std::string& parent, const std::string& child);
|
||||
bool file_exists(const std::string& path);
|
||||
std::string base_name(const std::string& filename);
|
||||
std::string base_name_no_ext(const std::string& filename);
|
||||
void MakeISOName(char* dst, const char* src);
|
||||
void ISONameFromAnimationName(char* dst, const char* src);
|
||||
void assert_file_exists(const char* path, const char* error_message);
|
||||
@@ -58,4 +60,5 @@ bool dgo_header_is_compressed(const std::vector<u8>& data);
|
||||
std::vector<u8> decompress_dgo(const std::vector<u8>& data_in);
|
||||
FILE* open_file(const fs::path& path, const std::string& mode);
|
||||
std::vector<fs::path> find_files_recursively(const fs::path& base_dir, const std::regex& pattern);
|
||||
std::vector<fs::path> find_directories_in_dir(const fs::path& base_dir);
|
||||
} // namespace file_util
|
||||
|
||||
+303
-17
@@ -15,6 +15,7 @@
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -29,7 +30,8 @@ bool hex_char(char c) {
|
||||
|
||||
const std::unordered_map<std::string, GameTextVersion> sTextVerEnumMap = {
|
||||
{"jak1-v1", GameTextVersion::JAK1_V1},
|
||||
{"jak1-v2", GameTextVersion::JAK1_V2}};
|
||||
{"jak1-v2", GameTextVersion::JAK1_V2},
|
||||
{"jak2", GameTextVersion::JAK2}};
|
||||
|
||||
const std::string& get_text_version_name(GameTextVersion version) {
|
||||
for (auto& [name, ver] : sTextVerEnumMap) {
|
||||
@@ -37,7 +39,7 @@ const std::string& get_text_version_name(GameTextVersion version) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(fmt::format("invalid text version {}", version));
|
||||
throw std::runtime_error(fmt::format("invalid text version {}", fmt::underlying(version)));
|
||||
}
|
||||
|
||||
GameTextFontBank::GameTextFontBank(GameTextVersion version,
|
||||
@@ -119,6 +121,10 @@ std::string GameTextFontBank::replace_to_utf8(std::string& str) const {
|
||||
}
|
||||
std::string GameTextFontBank::replace_to_game(std::string& str) const {
|
||||
for (auto& info : *m_replace_info) {
|
||||
if (info.to.empty()) {
|
||||
// Skip empty replacements, else it's an infinite loop
|
||||
continue;
|
||||
}
|
||||
auto pos = str.find(info.to);
|
||||
while (pos != std::string::npos) {
|
||||
str.replace(pos, info.to.size(), info.from);
|
||||
@@ -157,6 +163,19 @@ std::string GameTextFontBank::convert_utf8_to_game(std::string str) const {
|
||||
return str;
|
||||
}
|
||||
|
||||
bool GameTextFontBank::valid_char_range(const char in) const {
|
||||
if (m_version == GameTextVersion::JAK1_V1 || m_version == GameTextVersion::JAK1_V2) {
|
||||
return ((in >= '0' && in <= '9') || (in >= 'A' && in <= 'Z') ||
|
||||
m_passthrus->find(in) != m_passthrus->end()) &&
|
||||
in != '\\';
|
||||
} else if (m_version == GameTextVersion::JAK2) {
|
||||
return ((in >= '0' && in <= '9') || (in >= 'A' && in <= 'Z') || (in >= 'a' && in <= 'z') ||
|
||||
m_passthrus->find(in) != m_passthrus->end()) &&
|
||||
in != '\\';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Turn a normal readable string into a string readable in the in-game font encoding and converts
|
||||
* \cXX escape sequences
|
||||
@@ -219,9 +238,7 @@ std::string GameTextFontBank::convert_game_to_utf8(const char* in) const {
|
||||
if (remap != nullptr) {
|
||||
result.append(remap->chars);
|
||||
in += remap->bytes.size() - 1;
|
||||
} else if (((*in >= '0' && *in <= '9') || (*in >= 'A' && *in <= 'Z') ||
|
||||
m_passthrus->find(*in) != m_passthrus->end()) &&
|
||||
*in != '\\') {
|
||||
} else if (valid_char_range(*in)) {
|
||||
result.push_back(*in);
|
||||
} else if (*in == '\n') {
|
||||
result += "\\n";
|
||||
@@ -250,9 +267,9 @@ static std::vector<ReplaceInfo> s_replace_info_null = {};
|
||||
* - Jak & Daxter: The Precursor Legacy (Black Label)
|
||||
*/
|
||||
|
||||
static std::unordered_set<char> s_passthrus = {'~', ' ', ',', '.', '-', '+', '(', ')',
|
||||
'!', ':', '?', '=', '%', '*', '/', '#',
|
||||
';', '<', '>', '@', '[', '_'};
|
||||
static std::unordered_set<char> s_passthrus_jak1 = {'~', ' ', ',', '.', '-', '+', '(', ')',
|
||||
'!', ':', '?', '=', '%', '*', '/', '#',
|
||||
';', '<', '>', '@', '[', '_'};
|
||||
|
||||
static std::vector<EncodeInfo> s_encode_info_jak1 = {
|
||||
// random
|
||||
@@ -578,10 +595,10 @@ static std::vector<ReplaceInfo> s_replace_info_jak1 = {
|
||||
{"~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_SQUARE>"}, // custom
|
||||
};
|
||||
|
||||
GameTextFontBank g_font_bank_jak1(GameTextVersion::JAK1_V1,
|
||||
&s_encode_info_jak1,
|
||||
&s_replace_info_jak1,
|
||||
&s_passthrus);
|
||||
GameTextFontBank g_font_bank_jak1_v1(GameTextVersion::JAK1_V1,
|
||||
&s_encode_info_jak1,
|
||||
&s_replace_info_jak1,
|
||||
&s_passthrus_jak1);
|
||||
|
||||
/*!
|
||||
* ================================
|
||||
@@ -815,12 +832,281 @@ static std::vector<EncodeInfo> s_encode_info_jak1_v2 = {
|
||||
GameTextFontBank g_font_bank_jak1_v2(GameTextVersion::JAK1_V2,
|
||||
&s_encode_info_jak1_v2,
|
||||
&s_replace_info_jak1,
|
||||
&s_passthrus);
|
||||
&s_passthrus_jak1);
|
||||
|
||||
/*!
|
||||
* ================================
|
||||
* GAME TEXT FONT BANK - JAK 2
|
||||
* ================================
|
||||
* This font is used in:
|
||||
* - Jak 2 - NTSC - v1
|
||||
*/
|
||||
|
||||
static std::unordered_set<char> s_passthrus_jak2 = {'~', ' ', ',', '.', '-', '+', '(', ')',
|
||||
'!', ':', '?', '=', '%', '*', '/', '#',
|
||||
';', '<', '>', '@', '[', '_'};
|
||||
|
||||
static std::vector<ReplaceInfo> s_replace_info_jak2 = {
|
||||
// other
|
||||
{"A~Y~-21H~-5Vº~Z", "Å"},
|
||||
{"N~Y~-6Hº~Z~+10H", "Nº"},
|
||||
{"~+4VÇ~-4V", "ç"},
|
||||
|
||||
// tildes
|
||||
{"N~Y~-22H~-4V<TIL>~Z", "Ñ"},
|
||||
{"n~Y~-24H~-4V<TIL>~Z", "ñ"},
|
||||
{"A~Y~-21H~-5V<TIL>~Z", "Ã"},
|
||||
{"O~Y~-22H~-4V<TIL>~Z", "Õ"},
|
||||
|
||||
// acute accents
|
||||
{"A~Y~-21H~-5V'~Z", "Á"},
|
||||
{"A~Y~-26H~-8V'~Z", "<Á_V2>"}, // unfortunate...
|
||||
{"a~Y~-25H~-5V'~Z", "á"},
|
||||
{"E~Y~-23H~-9V'~Z", "É"},
|
||||
{"e~Y~-26H~-5V'~Z", "é"},
|
||||
{"I~Y~-19H~-5V'~Z", "Í"},
|
||||
{"i~Y~-19H~-8V'~Z", "í"},
|
||||
{"O~Y~-22H~-4V'~Z", "Ó"},
|
||||
{"o~Y~-26H~-4V'~Z", "ó"},
|
||||
{"U~Y~-24H~-3V'~Z", "Ú"},
|
||||
{"u~Y~-24H~-3V'~Z", "ú"},
|
||||
|
||||
// circumflex
|
||||
{"A~Y~-20H~-4V^~Z", "Â"},
|
||||
{"a~Y~-24H~-5V^~Z", "â"},
|
||||
{"E~Y~-20H~-5V^~Z", "Ê"},
|
||||
{"e~Y~-25H~-4V^~Zt", "ê"},
|
||||
{"I~Y~-19H~-5V^~Z", "Î"},
|
||||
{"i~Y~-19H~-8V^~Z", "î"},
|
||||
{"O~Y~-20H~-4V^~Z", "Ô"},
|
||||
{"o~Y~-25H~-4V^~Z", "ô"},
|
||||
{"U~Y~-24H~-3V^~Z", "Û"},
|
||||
{"u~Y~-23H~-3V^~Z", "û"},
|
||||
|
||||
// grave accents
|
||||
{"A~Y~-26H~-8V`~Z", "À"},
|
||||
{"a~Y~-25H~-5V`~Z", "à"},
|
||||
{"E~Y~-23H~-9V`~Z", "È"},
|
||||
{"e~Y~-26H~-5V`~Z", "è"},
|
||||
{"I~Y~-19H~-5V`~Z", "Ì"},
|
||||
{"i~Y~-19H~-8V`~Z", "ì"},
|
||||
{"O~Y~-22H~-4V`~Z", "Ò"},
|
||||
{"o~Y~-26H~-4V`~Z", "ò"},
|
||||
{"U~Y~-24H~-3V`~Z", "Ù"},
|
||||
{"u~Y~-24H~-3V`~Z", "ù"},
|
||||
|
||||
// umlaut
|
||||
{"A~Y~-26H~-8V¨~Z", "Ä"},
|
||||
{"a~Y~-25H~-5V¨~Z", "ä"},
|
||||
{"E~Y~-20H~-5V¨~Z", "Ë"},
|
||||
{"I~Y~-19H~-5V¨~Z", "Ï"},
|
||||
{"O~Y~-26H~-8V¨~Z", "Ö"},
|
||||
{"o~Y~-26H~-4V¨~Z", "ö"},
|
||||
{"U~Y~-25H~-8V¨~Z", "Ü"},
|
||||
{"u~Y~-24H~-3V¨~Z", "ü"},
|
||||
|
||||
// dakuten katakana
|
||||
{"~Yウ~Z゛", "ヴ"},
|
||||
{"~Yカ~Z゛", "ガ"},
|
||||
{"~Yキ~Z゛", "ギ"},
|
||||
{"~Yク~Z゛", "グ"},
|
||||
{"~Yケ~Z゛", "ゲ"},
|
||||
{"~Yコ~Z゛", "ゴ"},
|
||||
{"~Yサ~Z゛", "ザ"},
|
||||
{"~Yシ~Z゛", "ジ"},
|
||||
{"~Yス~Z゛", "ズ"},
|
||||
{"~Yセ~Z゛", "ゼ"},
|
||||
{"~Yソ~Z゛", "ゾ"},
|
||||
{"~Yタ~Z゛", "ダ"},
|
||||
{"~Yチ~Z゛", "ヂ"},
|
||||
{"~Yツ~Z゛", "ヅ"},
|
||||
{"~Yテ~Z゛", "デ"},
|
||||
{"~Yト~Z゛", "ド"},
|
||||
{"~Yハ~Z゛", "バ"},
|
||||
{"~Yヒ~Z゛", "ビ"},
|
||||
{"~Yフ~Z゛", "ブ"},
|
||||
{"~Yヘ~Z゛", "ベ"},
|
||||
{"~Yホ~Z゛", "ボ"},
|
||||
// handakuten katakana
|
||||
{"~Yハ~Z゜", "パ"},
|
||||
{"~Yヒ~Z゜", "ピ"},
|
||||
{"~Yフ~Z゜", "プ"},
|
||||
{"~Yヘ~Z゜", "ペ"},
|
||||
{"~Yホ~Z゜", "ポ"},
|
||||
// dakuten hiragana
|
||||
{"~Yか~Z゛", "が"},
|
||||
{"~Yき~Z゛", "ぎ"},
|
||||
{"~Yく~Z゛", "ぐ"},
|
||||
{"~Yけ~Z゛", "げ"},
|
||||
{"~Yこ~Z゛", "ご"},
|
||||
{"~Yさ~Z゛", "ざ"},
|
||||
{"~Yし~Z゛", "じ"},
|
||||
{"~Yす~Z゛", "ず"},
|
||||
{"~Yせ~Z゛", "ぜ"},
|
||||
{"~Yそ~Z゛", "ぞ"},
|
||||
{"~Yた~Z゛", "だ"},
|
||||
{"~Yち~Z゛", "ぢ"},
|
||||
{"~Yつ~Z゛", "づ"},
|
||||
{"~Yて~Z゛", "で"},
|
||||
{"~Yと~Z゛", "ど"},
|
||||
{"~Yは~Z゛", "ば"},
|
||||
{"~Yひ~Z゛", "び"},
|
||||
{"~Yふ~Z゛", "ぶ"},
|
||||
{"~Yへ~Z゛", "べ"},
|
||||
{"~Yほ~Z゛", "ぼ"},
|
||||
// handakuten hiragana
|
||||
{"~Yは~Z゜", "ぱ"},
|
||||
{"~Yひ~Z゜", "ぴ"},
|
||||
{"~Yふ~Z゜", "ぷ"},
|
||||
{"~Yへ~Z゜", "ぺ"},
|
||||
{"~Yほ~Z゜", "ぽ"},
|
||||
// japanese punctuation
|
||||
{",~+8H", "、"},
|
||||
{"~+8H ", " "},
|
||||
|
||||
// (hack) special case kanji
|
||||
{"~~", "世"},
|
||||
|
||||
// playstation buttons
|
||||
// - face
|
||||
{"~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_X>"},
|
||||
{"~Y~22L<~Z~Y~26L;~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_TRIANGLE>"},
|
||||
{"~Y~22L<~Z~Y~25L@~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_CIRCLE>"},
|
||||
{"~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<PAD_SQUARE>"},
|
||||
// - dpad
|
||||
{"~Y~22L<SYM7>~Z~3L~+17H~-13V<SYM8>~Z~22L~+17H~+14V<SYM9>~Z~22L~+32H<SYM10>~Z~+56H",
|
||||
"<PAD_DPAD_UP>"},
|
||||
{"~Y~22L<SYM7>~Z~3L~+17H~-13V<SYM8>~Z~3L~+17H~+14V<SYM9>~Z~22L~+32H<SYM10>~Z~+56H",
|
||||
"<PAD_DPAD_DOWN>"},
|
||||
{"~Y~22L<SYM7>~Z~22L~+17H~-13V<SYM8>~Z~22L~+17H~+14V<SYM9>~Z~22L~+32H<SYM10>~Z~+56H",
|
||||
"<PAD_DPAD_ANY>"},
|
||||
// - shoulder
|
||||
{"~Y~22L~-2H~-12V<SYM1><SYM2>~Z~22L~-2H~+17V<SYM3><SYM4>~Z~1L~+4H~+3V<SYM5>~Z~+38H",
|
||||
"<PAD_L1>"},
|
||||
{"~Y~22L~-2H~-12V<SYM1><SYM2>~Z~22L~-2H~+17V<SYM3><SYM4>~Z~1L~+6H~+3V<SYM6>~Z~+38H",
|
||||
"<PAD_R1>"},
|
||||
{"~Y~22L~-2H~-6V<SYM11><SYM12>~Z~22L~-2H~+16V<SYM15><SYM14>~Z~1L~+5H~-2V<SYM13>~Z~+38H",
|
||||
"<PAD_R2>"},
|
||||
{"~Y~22L~-2H~-6V<SYM11><SYM12>~Z~22L~-2H~+16V<SYM15><SYM14>~Z~1L~+5H~-2V<SYM22>~Z~+38H",
|
||||
"<PAD_L2>"},
|
||||
// - analog
|
||||
{"~1L~+8H~Y<SYM16>~Z~6L~-16H<SYM21>~Z~+16h~6L<SYM20>~Z~6L~-15V<SYM24>~Z~+13V~6L<SYM28>~Z~-10H~+"
|
||||
"9V~"
|
||||
"6L<SYM17>~Z~+10H~+9V~6L<SYM31>~Z~-10H~-11V~6L<SYM23>~Z~+10H~-11V~6L<SYM29>~Z~+32H",
|
||||
"<PAD_ANALOG_ANY>"},
|
||||
{"~Y~1L~+8H<SYM16>~Z~6L~-8H<SYM21>~Z~+24H~6L<SYM20>~Z~+40H", "<PAD_ANALOG_LEFT_RIGHT>"},
|
||||
{"~Y~1L<SYM16>~Z~6L~-15V<SYM24>~Z~+13V~6L<SYM28>~Z~+26H", "<PAD_ANALOG_UP_DOWN>"},
|
||||
|
||||
// icons
|
||||
{"~Y~6L<~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<ICON_MISSION_COMPLETE>"},
|
||||
{"~Y~3L<~Z~Y~1L>~Z~Y~23L[~Z~+26H", "<ICON_MISSION_TODO>"},
|
||||
|
||||
// flags
|
||||
{"~Y~6L<SYM18>~Z~+15H~1L<SYM18>~Z~+30H~3L<SYM18>~Z~+45H", "<FLAG_ITALIAN>"},
|
||||
{"~Y~5L<SYM19>~Z~3L<SYM32>~<SYM26>~-1H~Y~5L<SYM19>~Z~3L<SYM32>~Z~+26H", "<FLAG_SPAIN>"},
|
||||
{"~Y~39L~~~Z~3L<SYM33>~Z~5L<SYM25>~<SYM26>~-1H~Y~39L~~~Z~3L<SYM33>~Z~5L<SYM25>~Z~+26H",
|
||||
"<FLAG_GERMAN>"},
|
||||
{"~Y~7L<SYM18>~Z~+15H~1L<SYM18>~Z~+30H~3L<SYM18>~Z~+47H", "<FLAG_FRANCE>"},
|
||||
{"~Y~1L<SYM19>~Z~3L<SYM34>~Z~7L<SYM37>~<SYM26>~-1H~Y~1L<SYM19>~Z~3L<SYM30>~Z~7L<SYM42>~Z~+26H",
|
||||
"<FLAG_USA>"},
|
||||
{"~Y~1L<SYM19>~Z~3L<SYM35>~Z~7L<SYM38>~<SYM26>~-1H~Y~1L<SYM19>~Z~3L<SYM40>~Z~+26H",
|
||||
"<FLAG_UK>"},
|
||||
{"~Y~1L<SYM19>~Z~39L<SYM36>~<SYM26>~-1H~Y~1L<SYM19>~Z~39L<SYM39>~Z~-11H~7L<SYM41>~Z~-11H~3L<"
|
||||
"SYM43>~Z~+26H",
|
||||
"<FLAG_JAPAN>"},
|
||||
{"~Y~1L<SYM19>~<SYM26>~-1H~Y~1L<SYM19>~Z~-11H~3L<SYM27>~Z~+26H", "<FLAG_SOUTH_KOREA>"},
|
||||
|
||||
// weird stuff
|
||||
// - descenders
|
||||
{"~+7Vp~-7V", "p"},
|
||||
{"~+7Vy~-7V", "y"},
|
||||
{"~+7Vg~-7V", "g"},
|
||||
{"~+7Vq~-7V", "q"},
|
||||
{"~+1Vj~-1V", "j"},
|
||||
|
||||
{"\\\\\\\\", "\\n"},
|
||||
|
||||
// - symbols and ligatures
|
||||
{"~-4H~-3V\\c19~+3V~-4H",
|
||||
"<SUPERSCRIPT_QUOTE>"}, // used for the 4<__> place in spanish. the 5th uses the same
|
||||
// character but looks different...?
|
||||
{"~Y~-6Hº~Z~+10H", "°"},
|
||||
|
||||
// Color / Emphasis
|
||||
{"~[~1L", "<COLOR_WHITE>"},
|
||||
{"~[~32L", "<COLOR_DEFAULT>"}};
|
||||
|
||||
static std::vector<EncodeInfo> s_encode_info_jak2 = {
|
||||
{"_", {0x03}}, // large space
|
||||
{"ˇ", {0x10}}, // caron
|
||||
{"`", {0x11}}, // grave accent
|
||||
{"'", {0x12}}, // apostrophe
|
||||
{"^", {0x13}}, // circumflex
|
||||
{"<TIL>", {0x14}}, // tilde
|
||||
{"¨", {0x15}}, // umlaut
|
||||
{"º", {0x16}}, // numero/overring
|
||||
{"¡", {0x17}}, // inverted exclamation mark
|
||||
{"¿", {0x18}}, // inverted question mark
|
||||
{"Ç", {0x1d}}, // c-cedilla
|
||||
|
||||
{"ß", {0x1f}}, // eszett
|
||||
|
||||
{"œ", {0x5e}}, // ligature o+e
|
||||
// Re-purposed japanese/korean symbols that are used as part of drawing icons/flags/pad buttons
|
||||
// TODO - japanese and korean encodings
|
||||
{"<SYM26>", {0x5d}},
|
||||
|
||||
{"<SYM33>", {0x7f}},
|
||||
{"<SYM25>", {0x80}},
|
||||
{"<SYM18>", {0x81}},
|
||||
|
||||
{"<SYM19>", {0x85}},
|
||||
{"<SYM27>", {0x86}},
|
||||
{"<SYM36>", {0x87}},
|
||||
{"<SYM39>", {0x88}},
|
||||
{"<SYM43>", {0x89}},
|
||||
{"<SYM41>", {0x8a}},
|
||||
{"<SYM32>", {0x8b}},
|
||||
{"<SYM34>", {0x8c}},
|
||||
{"<SYM30>", {0x8d}},
|
||||
{"<SYM37>", {0x8e}},
|
||||
{"<SYM42>", {0x8f}},
|
||||
{"<SYM40>", {0x90}},
|
||||
{"<SYM16>", {0x91}},
|
||||
|
||||
{"<SYM6>", {0x94}},
|
||||
{"<SYM5>", {0x95}},
|
||||
{"<SYM13>", {0x96}},
|
||||
{"<SYM22>", {0x97}},
|
||||
{"<SYM28>", {0x98}},
|
||||
{"<SYM31>", {0x99}},
|
||||
{"<SYM35>", {0x9a}},
|
||||
{"<SYM38>", {0x9b}},
|
||||
{"<SYM24>", {0x9c}},
|
||||
{"<SYM23>", {0x9d}},
|
||||
{"<SYM21>", {0x9e}},
|
||||
{"<SYM17>", {0x9f}},
|
||||
{"<SYM9>", {0xa0}},
|
||||
{"<SYM7>", {0xa1}},
|
||||
{"<SYM8>", {0xa2}},
|
||||
{"<SYM10>", {0xa3}},
|
||||
{"<SYM20>", {0xa4}},
|
||||
{"<SYM29>", {0xa5}},
|
||||
{"<SYM1>", {0xa6}},
|
||||
{"<SYM2>", {0xa7}},
|
||||
{"<SYM11>", {0xa8}},
|
||||
{"<SYM12>", {0xa9}},
|
||||
{"<SYM3>", {0xb0}},
|
||||
{"<SYM4>", {0xb1}},
|
||||
{"<SYM14>", {0xb3}},
|
||||
{"<SYM15>", {0xb2}},
|
||||
};
|
||||
|
||||
GameTextFontBank g_font_bank_jak2(GameTextVersion::JAK2,
|
||||
&s_encode_info_null,
|
||||
&s_replace_info_null,
|
||||
&s_passthrus);
|
||||
&s_encode_info_jak2,
|
||||
//&s_replace_info_null,
|
||||
&s_replace_info_jak2,
|
||||
&s_passthrus_jak2);
|
||||
|
||||
/*!
|
||||
* ========================
|
||||
@@ -830,7 +1116,7 @@ GameTextFontBank g_font_bank_jak2(GameTextVersion::JAK2,
|
||||
*/
|
||||
|
||||
std::map<GameTextVersion, GameTextFontBank*> g_font_banks = {
|
||||
{GameTextVersion::JAK1_V1, &g_font_bank_jak1},
|
||||
{GameTextVersion::JAK1_V1, &g_font_bank_jak1_v1},
|
||||
{GameTextVersion::JAK1_V2, &g_font_bank_jak1_v2},
|
||||
{GameTextVersion::JAK2, &g_font_bank_jak2}};
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ class GameTextFontBank {
|
||||
|
||||
GameTextVersion version() const { return m_version; }
|
||||
|
||||
// TODO - methods would help make this code a lot better for different game versions
|
||||
// hacking it for now
|
||||
bool valid_char_range(const char in) const;
|
||||
|
||||
std::string convert_utf8_to_game_with_escape(const std::string& str) const;
|
||||
std::string convert_utf8_to_game(std::string str) const;
|
||||
std::string convert_game_to_utf8(const char* in) const;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
double FrameLimiter::round_to_nearest_60fps(double current) {
|
||||
double one_frame = 1.f / 60.f;
|
||||
int frames_missed = (current / one_frame); // rounds down
|
||||
@@ -11,7 +13,7 @@ double FrameLimiter::round_to_nearest_60fps(double current) {
|
||||
return (frames_missed + 1) * one_frame;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef OS_POSIX
|
||||
|
||||
FrameLimiter::FrameLimiter() {}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "Timer.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -37,7 +39,7 @@ int Timer::clock_gettime_monotonic(struct timespec* tv) const {
|
||||
#endif
|
||||
|
||||
void Timer::start() {
|
||||
#ifdef __linux__
|
||||
#ifdef OS_POSIX
|
||||
clock_gettime(CLOCK_MONOTONIC, &_startTime);
|
||||
#elif _WIN32
|
||||
clock_gettime_monotonic(&_startTime);
|
||||
@@ -46,7 +48,7 @@ void Timer::start() {
|
||||
|
||||
int64_t Timer::getNs() const {
|
||||
struct timespec now = {};
|
||||
#ifdef __linux__
|
||||
#ifdef OS_POSIX
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
#elif _WIN32
|
||||
clock_gettime_monotonic(&now);
|
||||
|
||||
+22
-1
@@ -1,6 +1,26 @@
|
||||
#include "crc32.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __arm__
|
||||
#include <arm_acle.h>
|
||||
u32 crc32(const u8* data, size_t size) {
|
||||
u32 result = 0xffffffff;
|
||||
while (size >= 4) {
|
||||
u32 x;
|
||||
memcpy(&x, data, 4);
|
||||
data += 4;
|
||||
size -= 4;
|
||||
result = __crc32w(result, x);
|
||||
}
|
||||
while (size) {
|
||||
result = __crc32b(result, *data);
|
||||
data++;
|
||||
size--;
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
#else
|
||||
#include <immintrin.h>
|
||||
|
||||
u32 crc32(const u8* data, size_t size) {
|
||||
@@ -18,4 +38,5 @@ u32 crc32(const u8* data, size_t size) {
|
||||
size--;
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -296,6 +296,8 @@ std::vector<std::string> SplitString(const ::std::string& str, char delimiter =
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_diff {
|
||||
std::string diff_strings(const std::string& lhs, const std::string& rhs) {
|
||||
if (!lhs.empty() && !rhs.empty()) {
|
||||
const std::vector<std::string> lhs_lines = SplitString(lhs);
|
||||
@@ -310,3 +312,4 @@ std::string diff_strings(const std::string& lhs, const std::string& rhs) {
|
||||
std::vector<std::string> split_string(const ::std::string& str, char delimiter) {
|
||||
return SplitString(str, delimiter);
|
||||
}
|
||||
} // namespace google_diff
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@
|
||||
/*!
|
||||
* Diff two strings. This uses the code from gtest's diff implementation.
|
||||
*/
|
||||
namespace google_diff {
|
||||
std::string diff_strings(const std::string& lhs, const std::string& rhs);
|
||||
|
||||
std::vector<std::string> split_string(const ::std::string& str, char delimiter = '\n');
|
||||
} // namespace google_diff
|
||||
|
||||
@@ -21,6 +21,13 @@ size_t get_peak_rss() {
|
||||
#ifdef _WIN32
|
||||
// windows has a __cpuid
|
||||
#include <intrin.h>
|
||||
#elif __APPLE__
|
||||
// for now, just return 0's.
|
||||
void __cpuidex(int result[4], int eax, int ecx) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
result[i] = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// using int to be compatible with msvc's intrinsic
|
||||
void __cpuidex(int result[4], int eax, int ecx) {
|
||||
|
||||
@@ -29,6 +29,14 @@ std::string meters_to_string(float value, bool append_trailing_decimal) {
|
||||
return float_to_string(value / METER_LENGTH, append_trailing_decimal);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wrapper around float_to_string, for printing degrees. Unlike float_to_string, it does not append
|
||||
* decimals by default.
|
||||
*/
|
||||
std::string degrees_to_string(float value, bool append_trailing_decimal) {
|
||||
return float_to_string(value / DEGREES_LENGTH, append_trailing_decimal);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a fixed point value to a float. Fixed point values usually end up with strange numbers
|
||||
* that were definitely not what was written when we do a naive conversion. This function
|
||||
@@ -200,3 +208,15 @@ int float_to_cstr(float value, char* buffer, bool append_trailing_decimal) {
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
bool proper_float(float value) {
|
||||
u32 int_value;
|
||||
memcpy(&int_value, &value, 4);
|
||||
u8 exp = (int_value >> 23) & 0xff;
|
||||
u32 mant = int_value & 0x7fffff;
|
||||
if ((exp == 0 && mant != 0) || exp == 0xff || !std::isfinite(value)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,7 @@ float fixed_point_to_float(s64 value, s64 scale);
|
||||
std::string fixed_point_to_string(s64 value, s64 scale, bool append_trailing_decimal = false);
|
||||
std::string float_to_string(float value, bool append_trailing_decimal = true);
|
||||
std::string meters_to_string(float value, bool append_trailing_decimal = false);
|
||||
std::string degrees_to_string(float value, bool append_trailing_decimal = false);
|
||||
std::string seconds_to_string(s64 value, bool append_trailing_decimal = false);
|
||||
int float_to_cstr(float value, char* buffer, bool append_trailing_decimal = true);
|
||||
bool proper_float(float value);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "third-party/zstd/lib/common/xxhash.h"
|
||||
|
||||
IsoFile::IsoFile() {
|
||||
root.is_dir = true;
|
||||
}
|
||||
@@ -102,7 +104,7 @@ void unpack_entry(FILE* fp,
|
||||
file_util::write_binary_file(path_to_entry.string(), buffer.data(), buffer.size());
|
||||
iso.files_extracted++;
|
||||
if (iso.shouldHash) {
|
||||
xxh::hash_t<64> hash = xxh::xxhash<64>(buffer);
|
||||
auto hash = XXH64(buffer.data(), buffer.size(), 0);
|
||||
iso.hashes.push_back(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "third-party/xxhash.hpp"
|
||||
|
||||
struct IsoFile {
|
||||
struct Entry {
|
||||
bool is_dir = false;
|
||||
@@ -29,7 +27,7 @@ struct IsoFile {
|
||||
bool shouldHash = false;
|
||||
// There is no reason to map to the files, as we don't retain mappings of each file's expected
|
||||
// hash
|
||||
std::vector<xxh::hash64_t> hashes = {};
|
||||
std::vector<uint64_t> hashes = {};
|
||||
|
||||
IsoFile();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace set_util {
|
||||
template <typename T>
|
||||
std::unordered_set<T> intersection(std::unordered_set<T> set1, std::unordered_set<T> set2) {
|
||||
if (set2.size() < set1.size()) {
|
||||
auto temp = set1;
|
||||
set1 = set2;
|
||||
set2 = temp;
|
||||
}
|
||||
std::unordered_set<T> m(set1.begin(), set1.end());
|
||||
std::unordered_set<T> res;
|
||||
for (auto a : set2)
|
||||
if (m.count(a)) {
|
||||
res.insert(a);
|
||||
m.erase(a);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
} // namespace set_util
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "string_util.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "common/util/diff.h"
|
||||
|
||||
namespace str_util {
|
||||
|
||||
const std::string WHITESPACE = " \n\r\t\f\v";
|
||||
|
||||
bool contains(const std::string& s, const std::string& substr) {
|
||||
return s.find(substr) != std::string::npos;
|
||||
}
|
||||
|
||||
bool starts_with(const std::string& s, const std::string& prefix) {
|
||||
return s.rfind(prefix) == 0;
|
||||
}
|
||||
|
||||
bool ends_with(const std::string& s, const std::string& suffix) {
|
||||
if (s.length() >= suffix.length()) {
|
||||
return !s.compare(s.length() - suffix.length(), suffix.length(), suffix);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ltrim(const std::string& s) {
|
||||
size_t start = s.find_first_not_of(WHITESPACE);
|
||||
return (start == std::string::npos) ? "" : s.substr(start);
|
||||
}
|
||||
|
||||
std::string rtrim(const std::string& s) {
|
||||
size_t end = s.find_last_not_of(WHITESPACE);
|
||||
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
||||
}
|
||||
|
||||
std::string trim(const std::string& s) {
|
||||
return rtrim(ltrim(s));
|
||||
}
|
||||
|
||||
std::string trim_newline_indents(const std::string& s) {
|
||||
auto lines = split(s, '\n');
|
||||
std::vector<std::string> trimmed_lines;
|
||||
std::transform(lines.begin(), lines.end(), std::back_inserter(trimmed_lines),
|
||||
[](const std::string& line) { return ltrim(line); });
|
||||
return join(trimmed_lines, "\n");
|
||||
}
|
||||
|
||||
std::string join(const std::vector<std::string>& strs, const std::string& join_with) {
|
||||
std::string out;
|
||||
for (size_t i = 0; i < strs.size(); i++) {
|
||||
out += strs.at(i);
|
||||
if (i < strs.size() - 1) {
|
||||
out += join_with;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int line_count(const std::string& str) {
|
||||
int result = 0;
|
||||
for (auto& c : str) {
|
||||
if (c == '\n') {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE - this won't work running within gk.exe!
|
||||
bool valid_regex(const std::string& regex) {
|
||||
try {
|
||||
std::regex re(regex);
|
||||
} catch (const std::regex_error& e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string diff(const std::string& lhs, const std::string& rhs) {
|
||||
return google_diff::diff_strings(lhs, rhs);
|
||||
}
|
||||
/// Default splits on \n characters
|
||||
std::vector<std::string> split(const ::std::string& str, char delimiter) {
|
||||
return google_diff::split_string(str, delimiter);
|
||||
}
|
||||
} // namespace str_util
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace str_util {
|
||||
bool contains(const std::string& s, const std::string& substr);
|
||||
bool starts_with(const std::string& s, const std::string& prefix);
|
||||
bool ends_with(const std::string& s, const std::string& prefix);
|
||||
std::string ltrim(const std::string& s);
|
||||
std::string rtrim(const std::string& s);
|
||||
std::string trim(const std::string& s);
|
||||
/// Given a string with new-lines, split and trim the leading whitespace from each line
|
||||
/// then return the string with the new-lines back in place.
|
||||
std::string trim_newline_indents(const std::string& s);
|
||||
int line_count(const std::string& str);
|
||||
bool valid_regex(const std::string& regex);
|
||||
std::string diff(const std::string& lhs, const std::string& rhs);
|
||||
/// Default splits on \n characters
|
||||
std::vector<std::string> split(const ::std::string& str, char delimiter = '\n');
|
||||
std::string join(const std::vector<std::string>& strs, const std::string& join_with);
|
||||
} // namespace str_util
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "term_util.h"
|
||||
|
||||
#if defined _WIN32
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__)
|
||||
#include <cstdlib>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
namespace term_util {
|
||||
void clear() {
|
||||
#if defined _WIN32
|
||||
system("cls");
|
||||
#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__)
|
||||
system("clear");
|
||||
#endif
|
||||
}
|
||||
|
||||
int row_count() {
|
||||
#if defined _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__)
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
return w.ws_row;
|
||||
#endif
|
||||
}
|
||||
|
||||
int col_count() {
|
||||
#if defined _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
#elif defined(__LINUX__) || defined(__gnu_linux__) || defined(__linux__) || defined(__APPLE__)
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
return w.ws_col;
|
||||
#endif
|
||||
}
|
||||
} // namespace term_util
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
namespace term_util {
|
||||
void clear();
|
||||
int row_count();
|
||||
int col_count();
|
||||
} // namespace term_util
|
||||
+6
-1
@@ -3,6 +3,7 @@
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
GameVersion game_name_to_version(const std::string& name) {
|
||||
if (name == "jak1") {
|
||||
@@ -25,6 +26,10 @@ std::string version_to_game_name(GameVersion v) {
|
||||
case GameVersion::Jak2:
|
||||
return "jak2";
|
||||
default:
|
||||
ASSERT_MSG(false, fmt::format("no game_name for version: {} found", v));
|
||||
ASSERT_MSG(false, fmt::format("no game_name for version: {} found", fmt::underlying(v)));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> valid_game_version_names() {
|
||||
return {game_version_names[GameVersion::Jak1], game_version_names[GameVersion::Jak2]};
|
||||
}
|
||||
|
||||
+3
-1
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -46,7 +47,7 @@ enum class GameVersion { Jak1 = 1, Jak2 = 2 };
|
||||
template <typename T>
|
||||
struct PerGameVersion {
|
||||
constexpr PerGameVersion(T jak1, T jak2) : data{jak1, jak2} {}
|
||||
constexpr T operator[](GameVersion v) const { return data[(int)v - 1]; }
|
||||
constexpr const T& operator[](GameVersion v) const { return data[(int)v - 1]; }
|
||||
T data[2];
|
||||
};
|
||||
|
||||
@@ -55,3 +56,4 @@ constexpr PerGameVersion<const char*> game_version_names = {"jak1", "jak2"};
|
||||
GameVersion game_name_to_version(const std::string& name);
|
||||
bool valid_game_version(const std::string& name);
|
||||
std::string version_to_game_name(GameVersion v);
|
||||
std::vector<std::string> valid_game_version_names();
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
// adds a vector tag 'movie-pos':
|
||||
// "movie-pos": ["vector", [4096000.0, -176128.0, 1353973.76, 1.0]]
|
||||
|
||||
// The base actor id for your custom level. If you have multiple levels this should be unique!
|
||||
"base_id": 100,
|
||||
|
||||
"actors" : [
|
||||
{
|
||||
"trans": [-21.6238, 20.0496, 17.1191], // translation
|
||||
|
||||
@@ -79,6 +79,7 @@ add_library(
|
||||
util/goal_data_reader.cpp
|
||||
util/sparticle_decompile.cpp
|
||||
util/TP_Type.cpp
|
||||
util/type_utils.cpp
|
||||
|
||||
VuDisasm/VuDisassembler.cpp
|
||||
VuDisasm/VuInstruction.cpp
|
||||
|
||||
@@ -30,6 +30,8 @@ std::string InstructionAtom::to_string(const std::vector<DecompilerLabel>& label
|
||||
return "Q";
|
||||
case IMM_SYM:
|
||||
return sym;
|
||||
case IMM_SYM_VAL_PTR:
|
||||
return sym;
|
||||
case VF_FIELD:
|
||||
ASSERT(imm >= 0 && imm < 4);
|
||||
return fmt::format(".{}", "xyzw"[imm]);
|
||||
@@ -79,9 +81,17 @@ void InstructionAtom::set_vu_q() {
|
||||
/*!
|
||||
* Make this atom a symbol.
|
||||
*/
|
||||
void InstructionAtom::set_sym(std::string _sym) {
|
||||
void InstructionAtom::set_sym(const std::string& _sym) {
|
||||
kind = IMM_SYM;
|
||||
sym = std::move(_sym);
|
||||
sym = _sym;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Make this atom a symbol value pointer.
|
||||
*/
|
||||
void InstructionAtom::set_sym_val_ptr(const std::string& _sym) {
|
||||
kind = IMM_SYM_VAL_PTR;
|
||||
sym = _sym;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -129,17 +139,10 @@ int InstructionAtom::get_label() const {
|
||||
* Get as symbol, or error if not a symbol.
|
||||
*/
|
||||
std::string InstructionAtom::get_sym() const {
|
||||
ASSERT(kind == IMM_SYM);
|
||||
ASSERT(kind == IMM_SYM || kind == IMM_SYM_VAL_PTR);
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*!
|
||||
* True if this atom is some sort of constant that doesn't involve linking.
|
||||
*/
|
||||
bool InstructionAtom::is_link_or_label() const {
|
||||
return kind == IMM_SYM || kind == LABEL;
|
||||
}
|
||||
|
||||
bool InstructionAtom::operator==(const InstructionAtom& other) const {
|
||||
if (kind != other.kind) {
|
||||
return false;
|
||||
|
||||
@@ -20,13 +20,14 @@ constexpr int MAX_INTRUCTION_DEST = 1;
|
||||
// An "atom", representing a single register, immediate, etc... for use in an Instruction.
|
||||
struct InstructionAtom {
|
||||
enum AtomKind {
|
||||
REGISTER, // An EE Register
|
||||
IMM, // An immediate value (stored as int32)
|
||||
IMM_SYM, // An immediate value (a symbolic link)
|
||||
LABEL, // A label in a LinkedObjectFile
|
||||
VU_ACC, // The VU0 Accumulator
|
||||
VU_Q, // The VU0 Q Register
|
||||
VF_FIELD, // Field specifier
|
||||
REGISTER, // An EE Register
|
||||
IMM, // An immediate value (stored as int32)
|
||||
IMM_SYM, // An immediate value (a symbolic link)
|
||||
IMM_SYM_VAL_PTR, // pointer to value of a symbol
|
||||
LABEL, // A label in a LinkedObjectFile
|
||||
VU_ACC, // The VU0 Accumulator
|
||||
VU_Q, // The VU0 Q Register
|
||||
VF_FIELD, // Field specifier
|
||||
INVALID
|
||||
} kind = INVALID;
|
||||
|
||||
@@ -35,7 +36,8 @@ struct InstructionAtom {
|
||||
void set_label(int id);
|
||||
void set_vu_q();
|
||||
void set_vu_acc();
|
||||
void set_sym(std::string _sym);
|
||||
void set_sym(const std::string& _sym);
|
||||
void set_sym_val_ptr(const std::string& _sym);
|
||||
void set_vf_field(uint32_t value);
|
||||
|
||||
Register get_reg() const;
|
||||
@@ -46,15 +48,14 @@ struct InstructionAtom {
|
||||
|
||||
std::string to_string(const std::vector<DecompilerLabel>& labels) const;
|
||||
|
||||
bool is_link_or_label() const;
|
||||
bool is_reg() const { return kind == REGISTER; }
|
||||
bool is_imm() const { return kind == IMM; }
|
||||
bool is_label() const { return kind == LABEL; }
|
||||
bool is_sym() const { return kind == IMM_SYM; }
|
||||
bool is_sym() const { return kind == IMM_SYM || kind == IMM_SYM_VAL_PTR; }
|
||||
|
||||
bool is_reg(Register r) const { return kind == REGISTER && reg == r; }
|
||||
bool is_imm(int32_t i) const { return kind == IMM && imm == i; }
|
||||
bool is_sym(const std::string& name) const { return kind == IMM_SYM && name == sym; }
|
||||
bool is_sym(const std::string& name) const { return is_sym() && name == sym; }
|
||||
|
||||
bool operator==(const InstructionAtom& other) const;
|
||||
bool operator!=(const InstructionAtom& other) const { return !((*this) == other); }
|
||||
|
||||
@@ -1193,6 +1193,17 @@ Instruction decode_instruction(LinkedWord& word, LinkedObjectFile& file, int seg
|
||||
ASSERT(fixed);
|
||||
}
|
||||
|
||||
if (word.kind() == LinkedWord::SYM_VAL_OFFSET) {
|
||||
bool fixed = false;
|
||||
for (int j = 0; j < i.n_src; j++) {
|
||||
if (i.src[j].kind == InstructionAtom::IMM) {
|
||||
fixed = true;
|
||||
i.src[j].set_sym_val_ptr(word.symbol_name());
|
||||
}
|
||||
}
|
||||
ASSERT(fixed);
|
||||
}
|
||||
|
||||
if (word.kind() == LinkedWord::HI_PTR) {
|
||||
ASSERT(i.kind == InstructionKind::LUI);
|
||||
bool fixed = false;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
namespace decompiler {
|
||||
namespace Reg {
|
||||
@@ -128,12 +128,14 @@ Register::Register(Reg::RegisterKind kind, uint32_t num) {
|
||||
case Reg::COP0:
|
||||
case Reg::VI:
|
||||
if (num > 32) {
|
||||
ASSERT_MSG(false, fmt::format("RegisterKind: {}, greater than 32: {}", kind, num));
|
||||
ASSERT_MSG(false, fmt::format("RegisterKind: {}, greater than 32: {}",
|
||||
fmt::underlying(kind), num));
|
||||
}
|
||||
break;
|
||||
case Reg::SPECIAL:
|
||||
if (num > 4) {
|
||||
ASSERT_MSG(false, fmt::format("Special RegisterKind: {}, greater than 4: {}", kind, num));
|
||||
ASSERT_MSG(false, fmt::format("Special RegisterKind: {}, greater than 4: {}",
|
||||
fmt::underlying(kind), num));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Function.h"
|
||||
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/symbols.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
@@ -496,12 +497,12 @@ bool ControlFlowGraph::is_while_loop(CfgVtx* b0, CfgVtx* b1, CfgVtx* b2) {
|
||||
bool debug = b0->to_string() == "Seq CONDNE104 ... Block 18100";
|
||||
|
||||
if (debug) {
|
||||
fmt::print("try while: {} | {} | {}\n", b0->to_string(), b1->to_string(), b2->to_string());
|
||||
lg::debug("try while: {} | {} | {}", b0->to_string(), b1->to_string(), b2->to_string());
|
||||
}
|
||||
|
||||
if (b0->end_branch.asm_branch || b1->end_branch.asm_branch) {
|
||||
if (debug)
|
||||
fmt::print("reject 1 {} {}\n", b0->end_branch.asm_branch, b1->end_branch.asm_branch);
|
||||
lg::debug("reject 1 {} {}", b0->end_branch.asm_branch, b1->end_branch.asm_branch);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -532,17 +533,17 @@ bool ControlFlowGraph::is_while_loop(CfgVtx* b0, CfgVtx* b1, CfgVtx* b2) {
|
||||
return false;
|
||||
ASSERT(!b1->end_branch.has_branch);
|
||||
if (!b2->has_pred(b0)) {
|
||||
printf("expect b2 (%s) to have pred b0 (%s)\n", b2->to_string().c_str(),
|
||||
b0->to_string().c_str());
|
||||
printf("but it doesn't! instead it has:\n");
|
||||
lg::debug("expect b2 ({}) to have pred b0 ({})", b2->to_string().c_str(),
|
||||
b0->to_string().c_str());
|
||||
lg::debug("but it doesn't! instead it has:");
|
||||
for (auto* x : b2->pred) {
|
||||
printf(" %s\n", x->to_string().c_str());
|
||||
lg::debug(" {}", x->to_string().c_str());
|
||||
}
|
||||
if (b0->succ_ft) {
|
||||
printf("b0's succ_ft: %s\n", b0->succ_ft->to_string().c_str());
|
||||
lg::debug("b0's succ_ft: {}", b0->succ_ft->to_string().c_str());
|
||||
}
|
||||
if (b0->succ_branch) {
|
||||
printf("b0's succ_branch: %s\n", b0->succ_branch->to_string().c_str());
|
||||
lg::debug("b0's succ_branch: {}", b0->succ_branch->to_string().c_str());
|
||||
}
|
||||
}
|
||||
ASSERT(b2->has_pred(b0));
|
||||
@@ -583,12 +584,9 @@ bool ControlFlowGraph::is_until_loop(CfgVtx* b1, CfgVtx* b2) {
|
||||
return false;
|
||||
ASSERT(!b1->end_branch.has_branch);
|
||||
|
||||
if (!b2->has_pred(b1)) {
|
||||
fmt::print("Graph error {} (s {}) should have pred {} (s {})\n", b2->to_string(),
|
||||
b2->get_first_block_id(), b1->to_string(), b1->get_first_block_id());
|
||||
}
|
||||
|
||||
ASSERT(b2->has_pred(b1));
|
||||
ASSERT_MSG(b2->has_pred(b1),
|
||||
fmt::format("Graph error {} (s {}) should have pred {} (s {})\n", b2->to_string(),
|
||||
b2->get_first_block_id(), b1->to_string(), b1->get_first_block_id()));
|
||||
if (b2->pred.size() != 1)
|
||||
return false;
|
||||
|
||||
@@ -938,8 +936,6 @@ bool ControlFlowGraph::find_infinite_continue() {
|
||||
int my_block = b0->get_first_block_id();
|
||||
int dest_block = b0->succ_branch->get_first_block_id();
|
||||
|
||||
// fmt::print("Considering {} as an infinite continue:\n", b0->to_string());
|
||||
|
||||
if (b0->end_branch.asm_branch) {
|
||||
return true;
|
||||
}
|
||||
@@ -1155,8 +1151,8 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
}
|
||||
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("Looks like asm likely branch: {} {} to {}\n", b0->to_string(), bds->to_string(),
|
||||
b1->to_string());
|
||||
lg::debug("Looks like asm likely branch: {} {} to {}", b0->to_string(), bds->to_string(),
|
||||
b1->to_string());
|
||||
}
|
||||
|
||||
auto* b0_seq = dynamic_cast<SequenceVtx*>(b0);
|
||||
@@ -1174,7 +1170,7 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
|
||||
for (auto* new_pred : b0->pred) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print(" pred {}\n", new_pred->to_string());
|
||||
lg::debug(" pred {}", new_pred->to_string());
|
||||
}
|
||||
new_pred->replace_succ_and_check(b0, new_seq);
|
||||
}
|
||||
@@ -1294,19 +1290,19 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
else {
|
||||
lg::error("unhandled sequences in clean_up_asm_branches likely seq: {} {}", !!b0_seq,
|
||||
!!b1_seq);
|
||||
lg::error("{} {}\n", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
lg::error("{} {}", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
}
|
||||
|
||||
} else {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("Looks like asm normal branch: {} to {}\n", b0->to_string(), b1->to_string());
|
||||
lg::debug("Looks like asm normal branch: {} to {}", b0->to_string(), b1->to_string());
|
||||
}
|
||||
auto* b0_seq = dynamic_cast<SequenceVtx*>(b0);
|
||||
auto* b1_seq = dynamic_cast<SequenceVtx*>(b1);
|
||||
|
||||
if (!b0_seq && !b1_seq) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("[combo nn] {} and {}\n", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
lg::debug("[combo nn] {} and {}", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
}
|
||||
// build new sequence
|
||||
replaced = true;
|
||||
@@ -1330,19 +1326,19 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
|
||||
for (auto* new_succ : b1->succs()) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("changing {}'s pred {} to seq. bc: {}", new_succ->to_string(),
|
||||
b1->to_string(), new_succ->pred.size());
|
||||
lg::debug("changing {}'s pred {} to seq. bc: {}", new_succ->to_string(),
|
||||
b1->to_string(), new_succ->pred.size());
|
||||
}
|
||||
new_succ->replace_pred_and_check(b1, new_seq);
|
||||
if (debug_asm_branch) {
|
||||
fmt::print(" ac: {}\n", new_succ->pred.size());
|
||||
lg::debug(" ac: {}", new_succ->pred.size());
|
||||
}
|
||||
}
|
||||
new_seq->succ_ft = b1->succ_ft;
|
||||
|
||||
if (b1->succ_branch && debug_asm_branch) {
|
||||
fmt::print("combining {} and {} into a sequence, succ {}\n", b0->get_first_block_id(),
|
||||
b1->get_first_block_id(), b1->succ_branch->get_first_block_id());
|
||||
lg::debug("combining {} and {} into a sequence, succ {}", b0->get_first_block_id(),
|
||||
b1->get_first_block_id(), b1->succ_branch->get_first_block_id());
|
||||
}
|
||||
|
||||
new_seq->succ_branch = b1->succ_branch;
|
||||
@@ -1367,13 +1363,13 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
return false;
|
||||
} else if (b0_seq && !b1_seq) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("[combo sn] {} and {}\n", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
fmt::print("expanding sequence: {} (s {}) to include {}\n", b0_seq->to_string(),
|
||||
b0_seq->get_first_block_id(), b1->get_first_block_id());
|
||||
lg::debug("[combo sn] {} and {}", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
lg::debug("expanding sequence: {} (s {}) to include {}", b0_seq->to_string(),
|
||||
b0_seq->get_first_block_id(), b1->get_first_block_id());
|
||||
}
|
||||
if (b1->succ_ft) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print(" b1 succ_ft is {}\n", b1->succ_ft->to_string());
|
||||
lg::debug(" b1 succ_ft is {}", b1->succ_ft->to_string());
|
||||
}
|
||||
ASSERT(b1->succ_ft->has_pred(b1));
|
||||
}
|
||||
@@ -1386,18 +1382,18 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
|
||||
if (b0->succ_branch) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("succ {} has {} preds parent: {}\n", b0->succ_branch->get_first_block_id(),
|
||||
b0->succ_branch->pred.size(), !!b0->succ_branch->parent);
|
||||
lg::debug("succ {} has {} preds parent: {}", b0->succ_branch->get_first_block_id(),
|
||||
b0->succ_branch->pred.size(), !!b0->succ_branch->parent);
|
||||
}
|
||||
b0->succ_branch->replace_preds_with_and_check({b0}, nullptr);
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("OKOK\n");
|
||||
lg::debug("OKOK");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* new_succ : b1->succs()) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("fixing up succ {}\n", new_succ->to_string());
|
||||
lg::debug("fixing up succ {}", new_succ->to_string());
|
||||
}
|
||||
new_succ->replace_pred_and_check(b1, b0);
|
||||
}
|
||||
@@ -1419,7 +1415,7 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
}
|
||||
|
||||
if (seq->succ_branch && debug_asm_branch) {
|
||||
fmt::print(" new sb: {}\n", seq->succ_branch->get_first_block_id());
|
||||
lg::debug(" new sb: {}", seq->succ_branch->get_first_block_id());
|
||||
}
|
||||
seq->next = b1->next;
|
||||
if (seq->next) {
|
||||
@@ -1434,8 +1430,8 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
return false;
|
||||
} else if (b0_seq && b1_seq) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("[combo ss] {} and {}\n", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
fmt::print(" {} and {}\n", b0->to_string(), b1->to_string());
|
||||
lg::debug("[combo ss] {} and {}", b0->get_first_block_id(), b1->get_first_block_id());
|
||||
lg::debug(" {} and {}", b0->to_string(), b1->to_string());
|
||||
}
|
||||
|
||||
// printf("make seq type 3 %s %s\n", b0->to_string().c_str(), b1->to_string().c_str());
|
||||
@@ -1449,8 +1445,8 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
|
||||
if (b0->succ_branch) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print(" sbp: {}\n", !!b0->succ_branch->parent);
|
||||
fmt::print(" sb: {}\n", b0->succ_branch->to_string());
|
||||
lg::debug(" sbp: {}", !!b0->succ_branch->parent);
|
||||
lg::debug(" sb: {}", b0->succ_branch->to_string());
|
||||
}
|
||||
b0->succ_branch->replace_preds_with_and_check({b0}, nullptr);
|
||||
}
|
||||
@@ -1467,7 +1463,7 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
seq->succ_branch = old_seq->succ_branch;
|
||||
seq->succ_branch = b1->succ_branch;
|
||||
if (seq->succ_branch && debug_asm_branch) {
|
||||
fmt::print(" DS new sb: {}\n", seq->succ_branch->get_first_block_id());
|
||||
lg::debug(" DS new sb: {}", seq->succ_branch->get_first_block_id());
|
||||
}
|
||||
seq->succ_ft = old_seq->succ_ft;
|
||||
seq->end_branch = old_seq->end_branch;
|
||||
@@ -1483,15 +1479,15 @@ bool ControlFlowGraph::clean_up_asm_branches() {
|
||||
} else if (!b0_seq && b1_seq) {
|
||||
replaced = true;
|
||||
if (!b0->succ_branch) {
|
||||
fmt::print("bad: {}\n", b0->to_string());
|
||||
lg::debug("bad: {}", b0->to_string());
|
||||
}
|
||||
m_blocks.at(b0->succ_branch->get_first_block_id())->needs_label = true;
|
||||
auto* old_seq = dynamic_cast<SequenceVtx*>(b1);
|
||||
ASSERT(old_seq);
|
||||
if (b0->succ_branch) {
|
||||
if (debug_asm_branch) {
|
||||
fmt::print(" sbp: {}\n", !!b0->succ_branch->parent);
|
||||
fmt::print(" sb: {}\n", b0->succ_branch->to_string());
|
||||
lg::debug(" sbp: {}", !!b0->succ_branch->parent);
|
||||
lg::debug(" sb: {}", b0->succ_branch->to_string());
|
||||
}
|
||||
b0->succ_branch->replace_preds_with_and_check({b0}, nullptr);
|
||||
}
|
||||
@@ -2318,18 +2314,18 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
ShortCircuit::Entry candidate = {vtx, vtx->next};
|
||||
CfgVtx* end = vtx->next->succ_branch;
|
||||
|
||||
// fmt::print("Starting loop\n");
|
||||
// lg::print("Starting loop\n");
|
||||
|
||||
while (true) {
|
||||
// check candidate:
|
||||
if (!candidate.condition || !candidate.likely_delay || !end) {
|
||||
// fmt::print("reject begin {} {} {}\n", !!candidate.condition,
|
||||
// lg::print("reject begin {} {} {}\n", !!candidate.condition,
|
||||
// !!candidate.likely_delay,
|
||||
// !!end);
|
||||
return true;
|
||||
}
|
||||
|
||||
// fmt::print(" try {} and {} end {}\n", candidate.condition->to_string(),
|
||||
// lg::print(" try {} and {} end {}\n", candidate.condition->to_string(),
|
||||
// candidate.likely_delay->to_string(), end->to_string());
|
||||
|
||||
if (candidate.condition->next == end) {
|
||||
@@ -2341,13 +2337,13 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
candidate.likely_delay = nullptr;
|
||||
entries.push_back(candidate);
|
||||
|
||||
// fmt::print("all done!");
|
||||
// lg::print("all done!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidate.condition->next || !candidate.condition->succ_branch) {
|
||||
// fmt::print(" fail 0 {}, {}\n", !!candidate.condition->next,
|
||||
// lg::print(" fail 0 {}, {}\n", !!candidate.condition->next,
|
||||
// !!candidate.condition->succ_branch);
|
||||
return true;
|
||||
}
|
||||
@@ -2357,12 +2353,12 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
candidate.condition->next != candidate.condition->succ_branch ||
|
||||
!candidate.condition->end_branch.branch_likely ||
|
||||
candidate.condition->end_branch.kind != CfgVtx::DelaySlotKind::NO_DELAY) {
|
||||
// fmt::print(" fail 1 {} {} {} {}\n", !candidate.condition->next,
|
||||
// lg::print(" fail 1 {} {} {} {}\n", !candidate.condition->next,
|
||||
// candidate.condition->next != candidate.condition->succ_branch,
|
||||
// !candidate.condition->end_branch.branch_likely,
|
||||
// candidate.condition->end_branch.kind !=
|
||||
// CfgVtx::DelaySlotKind::NO_DELAY);
|
||||
// fmt::print(" fail 1 condition->next {}, condition->succ_branch {}\n",
|
||||
// lg::print(" fail 1 condition->next {}, condition->succ_branch {}\n",
|
||||
// candidate.condition->next->to_string(),
|
||||
// candidate.condition->succ_branch->to_string());
|
||||
return true;
|
||||
@@ -2371,7 +2367,7 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
if (entries.empty() && candidate.likely_delay->next == end) {
|
||||
entries.push_back(candidate);
|
||||
|
||||
// fmt::print("all don2!");
|
||||
// lg::print("all don2!");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2380,7 +2376,7 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
!candidate.likely_delay->end_branch.branch_always ||
|
||||
candidate.likely_delay->end_branch.branch_likely ||
|
||||
candidate.likely_delay->end_branch.kind != CfgVtx::DelaySlotKind::NO_DELAY) {
|
||||
// fmt::print(" fail 2 {} {} {} {} {} {} {}\n", candidate.likely_delay->pred.size()
|
||||
// lg::print(" fail 2 {} {} {} {} {} {} {}\n", candidate.likely_delay->pred.size()
|
||||
// != 1,
|
||||
// !!candidate.likely_delay->succ_ft,
|
||||
// !candidate.likely_delay->succ_branch,
|
||||
@@ -2389,21 +2385,21 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
// !!candidate.likely_delay->end_branch.branch_likely,
|
||||
// candidate.likely_delay->end_branch.kind !=
|
||||
// CfgVtx::DelaySlotKind::NO_DELAY);
|
||||
// fmt::print("delay {} has ft {}\n", candidate.likely_delay->to_string(),
|
||||
// lg::print("delay {} has ft {}\n", candidate.likely_delay->to_string(),
|
||||
// candidate.likely_delay->succ_ft->to_string());
|
||||
return true;
|
||||
}
|
||||
|
||||
// slot -> end
|
||||
if (candidate.likely_delay->succ_branch != end) {
|
||||
// fmt::print(" fail 3\n");
|
||||
// lg::print(" fail 3\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// root -> next root
|
||||
if (!candidate.condition->next->next ||
|
||||
candidate.condition->next->next != candidate.condition->succ_ft) {
|
||||
// fmt::print(" fail 4\n");
|
||||
// lg::print(" fail 4\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2415,10 +2411,10 @@ bool ControlFlowGraph::find_short_circuits() {
|
||||
|
||||
// pre next root check
|
||||
if (next_root->pred.size() != 1) {
|
||||
// fmt::print(" fail 5\n");
|
||||
// lg::print(" fail 5\n");
|
||||
return true;
|
||||
}
|
||||
// fmt::print("on to next!\n");
|
||||
// lg::print("on to next!\n");
|
||||
}
|
||||
|
||||
auto new_sc = alloc<ShortCircuit>();
|
||||
@@ -2618,7 +2614,7 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file,
|
||||
const CondWithElseLengthHack& cond_with_else_hack,
|
||||
const std::unordered_set<int>& blocks_ending_in_asm_br,
|
||||
GameVersion version) {
|
||||
// fmt::print("START {}\n", func.guessed_name.to_string());
|
||||
// lg::print("START {}\n", func.guessed_name.to_string());
|
||||
auto cfg = std::make_shared<ControlFlowGraph>();
|
||||
|
||||
const auto& blocks = cfg->create_blocks(func.basic_blocks.size());
|
||||
@@ -2769,7 +2765,7 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file,
|
||||
if (blocks_ending_in_asm_br.find(i) != blocks_ending_in_asm_br.end()) {
|
||||
b->end_branch.asm_branch = true;
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("OVERRIDE asm branch at block {}\n", i);
|
||||
lg::debug("OVERRIDE asm branch at block {}", i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -2785,9 +2781,8 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file,
|
||||
if (branch_delay_asm(following, version)) {
|
||||
b->end_branch.asm_branch = true;
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("LIKELY ASM BRANCH: {} and {}\n",
|
||||
likely_branch_candidate.to_string(file.labels),
|
||||
following.to_string(file.labels));
|
||||
lg::debug("LIKELY ASM BRANCH: {} and {}", likely_branch_candidate.to_string(file.labels),
|
||||
following.to_string(file.labels));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2801,8 +2796,8 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file,
|
||||
if (branch_delay_asm(delay_slot_candidate, version)) {
|
||||
b->end_branch.asm_branch = true;
|
||||
if (debug_asm_branch) {
|
||||
fmt::print("NORMAL ASM BRANCH: {} and {}\n", branch_candidate.to_string(file.labels),
|
||||
delay_slot_candidate.to_string(file.labels));
|
||||
lg::debug("NORMAL ASM BRANCH: {} and {}", branch_candidate.to_string(file.labels),
|
||||
delay_slot_candidate.to_string(file.labels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,8 +504,7 @@ void Function::find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dt
|
||||
for (const auto& instr : instructions) {
|
||||
// look for lw t9, method-set!(s7)
|
||||
if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::T9) &&
|
||||
instr.get_src(0).kind == InstructionAtom::IMM_SYM &&
|
||||
instr.get_src(0).get_sym() == "method-set!" &&
|
||||
instr.get_src(0).is_sym() && instr.get_src(0).get_sym() == "method-set!" &&
|
||||
instr.get_src(1).get_reg() == make_gpr(Reg::S7)) {
|
||||
state = 1;
|
||||
continue;
|
||||
@@ -514,8 +513,7 @@ void Function::find_method_defs(LinkedObjectFile& file, DecompilerTypeSystem& dt
|
||||
if (state == 1) {
|
||||
// look for lw a0, type-name(s7)
|
||||
if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::A0) &&
|
||||
instr.get_src(0).kind == InstructionAtom::IMM_SYM &&
|
||||
instr.get_src(1).get_reg() == make_gpr(Reg::S7)) {
|
||||
instr.get_src(0).is_sym() && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) {
|
||||
type_name = instr.get_src(0).get_sym();
|
||||
state = 2;
|
||||
continue;
|
||||
@@ -587,7 +585,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts)
|
||||
|
||||
for (const auto& instr : instructions) {
|
||||
// look for lw xx, type(s7)
|
||||
if (instr.kind == InstructionKind::LW && instr.get_src(0).kind == InstructionAtom::IMM_SYM &&
|
||||
if (instr.kind == InstructionKind::LW && instr.get_src(0).is_sym() &&
|
||||
instr.get_src(0).get_sym() == "type" && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) {
|
||||
state = 1;
|
||||
temp_reg = instr.get_dst(0).get_reg();
|
||||
@@ -621,8 +619,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts)
|
||||
if (state == 3) {
|
||||
// look for lw a1, parent-type(s7)
|
||||
if (instr.kind == InstructionKind::LW && instr.get_dst(0).get_reg() == make_gpr(Reg::A1) &&
|
||||
instr.get_src(0).kind == InstructionAtom::IMM_SYM &&
|
||||
instr.get_src(1).get_reg() == make_gpr(Reg::S7)) {
|
||||
instr.get_src(0).is_sym() && instr.get_src(1).get_reg() == make_gpr(Reg::S7)) {
|
||||
state = 4;
|
||||
parent_type = instr.get_src(0).get_sym();
|
||||
continue;
|
||||
@@ -658,7 +655,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts)
|
||||
if (instr.kind == InstructionKind::SLL && instr.get_dst(0).get_reg() == make_gpr(Reg::V0) &&
|
||||
instr.get_src(0).get_reg() == make_gpr(Reg::RA) && instr.get_src(1).get_imm() == 0) {
|
||||
// done!
|
||||
// fmt::print("Got type {} parent {}\n", type_name, parent_type);
|
||||
// lg::print("Got type {} parent {}\n", type_name, parent_type);
|
||||
dts.add_type_parent(type_name, parent_type);
|
||||
DecompilerLabel flag_label = file.labels.at(label_idx);
|
||||
u64 word = file.read_data_word(flag_label);
|
||||
@@ -667,7 +664,7 @@ void Function::find_type_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts)
|
||||
word |= (word2 << 32);
|
||||
types_defined.push_back(type_name);
|
||||
dts.add_type_flags(type_name, word);
|
||||
// fmt::print("Flags are 0x{:x}\n", word);
|
||||
// lg::print("Flags are 0x{:x}\n", word);
|
||||
state = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user