This attempts to get into master whatever work was done in this PR /
it's earlier PR https://github.com/open-goal/jak-project/pull/3965
I don't want this work to be lost / floating around in massive PRs.
However the changes are:
- switch to ntsc_v1 instead of PAL as the development target, as we have
done for all other games
- remove most of the copied-from-jak2/3 changes as they need to be
confirmed during the decompilation process not just assumed
- avoids committing any changes to `game/kernel/common` as it was not
clear to me if these were changes made in jak x's kernel that were not
properly broken out into it's own functions. We don't want to
accidentally introduce bugs into jak1-3's kernel code.
- in other words, if the change in the kernel only happens in jak x...it
should likely be specific to jak x's kernel, not common.
---------
Co-authored-by: VodBox <dillon@vodbox.io>
Co-authored-by: yodah <greenboyyodah@gmail.com>
Resolves#3075
TODO before merge:
- [x] Properly draw non-korean strings while in korean mode (language
selection)
- [x] Check jak 3
- [x] Translation scaffolding (allow korean characters, add to Crowdin,
fix japanese locale, etc)
- [x] Check translation of text lines
- [x] Check translation of subtitle lines
- [x] Cleanup PR / some performance optimization (it's take a bit too
long to build the text and it shouldn't since the information is in a
giant lookup table)
- [x] Wait until release is cut
I confirmed the font textures are identical between Jak 2 and Jak 3, so
thank god for that.
Some examples of converting the korean encoding to utf-8. These show off
all scenarios, pure korean / korean with ascii and japanese / korean
with replacements (flags):
<img width="316" height="611" alt="Screenshot 2025-07-26 191511"
src="https://github.com/user-attachments/assets/614383ba-8049-4bf4-937e-24ad3e605d41"
/>
<img width="254" height="220" alt="Screenshot 2025-07-26 191529"
src="https://github.com/user-attachments/assets/1f6e5a6c-8527-4f98-a988-925ec66e437d"
/>
And it working in game. `Input Options` is a custom not-yet-translated
string. It now shows up properly instead of a disgusting block of
glyphs, and all the original strings are hopefully the same
semantically!:
<img width="550" height="493" alt="Screenshot 2025-07-26 202838"
src="https://github.com/user-attachments/assets/9ebdf6c0-f5a3-4a30-84a1-e5840809a1a2"
/>
Quite the challenge. The crux of the problem is -- Naughty Dog came up
with their own encoding for representing korean syllable blocks, and
that source information is lost so it has to be reverse engineered.
Instead of trying to figure out their encoding from the text -- I went
at it from the angle of just "how do i draw every single korean
character using their glyph set".
One might think this is way too time consuming but it's important to
remember:
- Korean letters are designed to be composable from a relatively small
number of glyphs (more on this later)
- Someone at naughty dog did basically this exact process
- There is no other way! While there are loose patterns, there isn't an
overarching rhyme or reason, they just picked the right glyph for the
writing context (more on this later). And there are even situations
where there IS NO good looking glyph, or the one ND chose looks awful
and unreadable (we could technically fix this by adjusting the
positioning of the glyphs but....no more)!
Information on their encoding that gets passed to `convert-korean-text`:
- It's a raw stream of bytes
- It can contain normal font letters
- Every syllable block begins with: `0x04 <num_glyphs> <...the glyph
bytes...>`
- DO NOT confuse `num_glyphs` with num jamo, because some glyphs can
have multiple jamo!
- Every section of normal text starts with `0x03`. For example a space
would be `0x03 0x20`
- There are a very select few number of jamo glyphs on a secondary
texture page, these glyph bytes are preceeded with a `0x05`. These jamo
are a variant of some of the final vowels, moving them as low down as
possible.
Crash course on korean writing:
- Nice resource as this is basically what we are doing -
https://glyphsapp.com/learn/creating-a-hangeul-font
- Korean syllable blocks have either 2 or 3 jamo. Jamo are basically
letters and are the individual pieces that make up the syllable blocks.
- The jamo are split up into "initial", "medial" and "final" categories.
Within the "medial" category there are obvious visual variants:
- Horizontal
- Vertical
- Combination (horizontal + a vertical)
- These jamo are laid out in 6 main pre-defined "orientations":
- initial + vertical medial
- initial + horizontal medial
- initial + combination
- initial + vertical medial + final
- initial + horizontal medial + final
- initial + combination + final
- Sometimes, for stylistic reasons, jamo will be written in different
ways (ie. if there is nothing below a vertical vowel will be extended).
- Annoying, and ND's glyph set supports this stylistic choice!
- There are some combination of jamo that are never used, and some that
are only used for a single word in the entire language!
With all that in mind, my basic process was:
- Scan the game's entire corpus of korean text, that includes subtitles.
It's very easy to look at the font texture's glyphs and assign them to
their respective jamo
- This let me construct a mapping and see which glyphs were used under
which context
- I then shoved this information into a 2-D matrix in excel, and created
an in-game tool to check every single jamo permutation to fill in the
gaps / change them if naughty dogs was bad. Most of the time, ND's
encoding was fine.
-
https://docs.google.com/spreadsheets/d/e/2PACX-1vTtyMeb5-mL5rXseS9YllVj32BGCISOGZFic6nkRV5Er5aLZ9CLq1Hj_rTY7pRCn-wrQDH1rvTqUHwB/pubhtml?gid=886895534&single=true
anything in red is an addition / modification on my part.
- This was the most lengthy part but not as long as you may think, you
can do a lot of pruning. For example if you are checking a 3-jamo
variant (the ones with the most permutations) and you've verified that
the medial jamo is as far up vertically as it can be, and you are using
the lowest final jamo that are available -- there is nothing to check or
improve -- for better or worse! So those end up being the permutations
between the initial and medial instead of a three-way permutation
nightmare.
- Also, while it is a 2d matrix, there's a lot of pruning even within
that. For example, for the first 3 orientations, you dont have to care
about final vowels at all.
- At the end, I'm left with a lookup table that I can use the encode the
best looking korean syllable blocks possible given the context of the
jamo combination.
Prior to SDL3, borderless windows were kinda a hack, they cleaned all of
that up. The side-effect of that is that the C++ code keeps track of the
current window size (it does this for framebuffer sizing, but also to
set the size of the window).
This is now an issue because SDL is properly firing events when you
change to borderless mode, so our window size gets updated to the size
of the entire monitor. When you switch to windowed mode from borderless,
it now seems like it didn't work (it did, its just still taking up the
entire screen).
This could be better cleaned up if more settings are extracted out of
GOAL and put into C++ so they can be managed and accessed where they
belong. But for now, this is a minimally invasive fix.
Fixes#3917
This is a simple multiplier to the gamepad axis input value received
from SDL events. Normally the values it provides cannot satisfy the
square range of the stick input. This is usually fine but it might play
differently with different controllers and compared to consoles,
especially considering the DualShock 1/2 have automatic calibration
which works in mysterious ways. The setting is there so any user can
adjust it for their controllers.
Saved to and loaded from the input-settings.json file.
133% matches PCSX2's default setting and is generally a good value to
map the square stick range within most modern(ish) controllers' circular
stick motion.
Progress menu option added to Jak 1 and 2. Setting can be changed from
50% all the way to 200%.
~~Renamed the analog deadzone options to stick deadzone since they don't
apply to the other analog buttons and only the (analog, yes) sticks.~~
This PR updates to SDL3, and with it, adds a handful of new features.
Everything seems to work but I'm going to look over the code once last
time before merging, some of the API changes are hard to spot.
Fixes#2773
### Pressure sensitivity support for DS3 Controllers
SDL3 adds pressure sensitivity support for DS3 controllers on windows. I
have not tested on linux. The option is disabled by default.
On windows you will need https://docs.nefarius.at/projects/DsHidMini/
and to be using SXS mode.
### DualSense and Xbox One Trigger Effects
If enabled, Jak 2 will have certain trigger effects. They are:
- xbox1:
- small vibrate when collecting dark eco
- big vibrate when changing to dark jak
- vibrate when shooting gun, proportional to gun type
- ps5:
- resistance when changing to dark jak
- different gun shooting effects
- red (resistance)
- yellow (weapon trigger)
- blue (vibrates)
- purple (less resistance)
> **Gun Shooting effects are only enabled if the new "Swap R1 and R2"
option is enabled**
There are more effects that could be used in `dualsense_effects.cpp`,
but I only exposed the ones I needed to OpenGOAL. If a modder wants to
use some of the others and wires them up end-to-end, please consider
contributing that upstream.
### New ImGUI Menu
Added new imgui options for selecting the active controller, for those
people that struggle to select the initial controller.

### Testing
The highlights of what I tested successfully:
- display
- [x] all mode switch permutations
- [x] launch with all modes saved
- [x] switch monitors / unplug monitor that was active, how does it
handle it
- [x] load with alternate monitor saved and all modes
- [x] allowing hidpi doesnt break macos
- controls
- [x] keyboard and mouse still work
- [x] pressure sensitivity on linux
Fixes https://github.com/open-goal/jak-project/issues/3563
These users have the following spamming in logs:
> OpenGL error 0x502 S8246 T824C: GL_INVALID_OPERATION error generated.
Source and destination dimensions must be identical with the current
filtering modes.
And the solution is to correctly set their game-size. The way this
change accomplishes that is by confirming whether or not the set
`game-size` is a valid resolution informed by SDL, if not, it defaults
to the monitor's currently set display mode's resolution.
This also moves the selected display id, and the display mode into the
C++ settings -- closer to where it's actually managed and used. I'm
tempted to do this eventually for the resolutions as well but that stuff
is much more burdensome. This hopefully simplifies debugging, reduces
startup flickering, and removes back-and-forth complexity. Hopefully
this makes debugging display related problems easier. It also adds a
bunch more logging to the related code.
It was narrowed down recently that a lot of people have issues with the
controller input because of Steam Input working as intended. Steam Input
can be configured to replicate controller inputs as keyboard inputs (for
example, pressing X on your controller presses Enter on the keyboard).
This results in the problem of "jumping pauses the game" and similar
issues. This is a consequence of the intended behaviour of the game
listening to all input sources at the same time.
Since the vast majority of players are using controllers over keyboards,
it makes sense to disable the keyboard input by default to solve this
problem. However that makes things awkward for users that want to use
the keyboard (how do they enable the setting). The solution is a new
imgui option in the settings menu:

**Known issue that I don't care about** -- in Jak 1's menu code, since
the flags are controlled by pointers to values instead of a lambda like
in jak 2, the menu won't update live with the imgui option. This has no
functional impact and I don't care enough to fix it.
I also made the pc-settings.gc file persist on first load if the file
wasn't found. Hopefully this helps diagnose the support issues related
to the black screen.
# Why not just ignore the keyboard inputs for a period of time?
This won't work, the keyboard is polled every frame. Therefore if you
hold down the X button on your controller, steam is continuously
signaling that `Enter` is held down on the keyboard.
Yes it would be possible to completely disable the keyboard while the
controller is being used, but this defeats the purpose of creating an
input system that allows multiple input sources at the same time.
With an explicit option, not only can the user decide the behaviour they
want (do they want the keyboard ignored or simultaneously listened to)
but we avoid breaking strange edge-cases in usage leading to never
ending complexity:
- ie. imagine steam input sends events to the mouse, well you can't
disable the mouse while using the keyboard because most times people are
using mouse and keyboard
- ie. a user that wants to hold a direction with the keyboard and press
buttons on the controller in tandem (something i frequently do while
TAS'ing, to move in a perfect straight line)
This sets up the C Kernel for Jak 3, and makes it possible to build and
load code built with `goalc --jak3`.
There's not too much interesting here, other than they switched to a
system where symbol IDs (unique numbers less than 2^14) are generated at
compile time, and those get included in the object file itself.
This is kind of annoying, since it means all tools that produce a GOAL
object file need to work together to assign unique symbol IDs. And since
the symbol IDs can't conflict, and are only a number between 0 and 2^14,
you can't just hash and hope for no collisions.
We work around this by ignoring the IDs and re-assigning our own. I
think this is very similar to what the C Kernel did on early builds of
Jak 3 which supported loading old format level files, which didn't have
the IDs included.
As far as I can tell, this shouldn't cause any problems. It defeats all
of their fancy tricks to save memory by not storing the symbol string,
but we don't care.
This is the more correct way of doing what that code is trying to do.
Fixes#3296
Also fixed some type inconsistencies with related code, probably wasn't
causing issues though.
May also fix the "black screen on startup" issues people keep having,
but that would simply be a nice bonus and isn't the aim of this PR.
Updates the decompiler for the new format and there's new macros. This
new format should be easier to read/parse.
Also rewrote `sp-init-fields!` (both jak 1 and 2) from assembly to GOAL.
Hopefully I did not miss any regressions in Jak 1/2 while updating the
files, it's a lot.
Adds the `pckernel` system to Jak 2, allowing you to do the PC-specific
things that Jak 1 lets you do like change game resolution, etc.
In other to reduce the amount of code duplication for something that
we're gonna be changing a lot over time, I split it into a few more code
files. In this new system, `pckernel-h.gc`, `pckernel-common.gc`
(previously `pckernel.gc`) and `pc-debug-common.gc` are the files that
should be shared across all games (I hacked the Jak 2 project to pull
these files from the Jak 1 folder), while `pckernel-impl.gc`,
`pckernel.gc` and `pc-debug-methods.gc` are their respective
game-specific counterparts that should be loaded after. I'm not fully
happy with this, I think it's slightly messy, but it cleanly separates
code that should be game-specific and not accidentally copied around and
code that should be the same for all games anyway.
This adds a new ImGUI menu to help filter out the clutter on screen.
https://user-images.githubusercontent.com/13153231/210192912-b1c28319-bacb-449c-ad7f-e7308fb75f50.mp4
This also:
- moves the imgui display bool into a game specific config file (you can
hide it in jak1, and not in jak2)
- the config file also persists the settings from this menu (except the
filters for now, future TODO)
- there is a new `ignore_imgui_hide_keybind` in this file to ignore
hiding it when you press Alt
Running reference tests/decompiler should now be possible on macos
(arm). Most of the changes were just cleaning up places where we were
sloppy with ifdefs, but there were two interesting ones:
- `Printer.cpp` was updated to not use a recursive function for printing
lists, to avoid stack overflow
- I replaced xxhash with another version of the same library that
supports arm (the one that comes in zstd). The interface is C instead of
C++ but it's not bad to use. I confirmed that the extractor succeeds on
jak 1 iso so it looks like this gives us the same results as the old
library.
* game: add auto-splitter structs
* game/jak1: wire up auto-splitter updating to game when in SR mode
* game/jak1: add info to split on picking up white eco
* game/jak1: add another two tasks for fish and finalboss
* game/jak1: autosplitting code entirely in GOAL
* redo cheat encodings
* fix error
* add no texture cheat
* tiny cleanup + add sidekick stats button
* crappy implementation of big/small head mode
* more correct bone scaling
* redo bone manip code a bit
* jp text fixes
* improved matrix math!
* add big fist cheat, minor type cleanup, add some debug toggles
* move all this mess to a new file
* slightly rework joint scaling function
* add big head npc cheat
* subtitles typo
* WIP mirror mode
* fix mirrored hud sprites
* fix mirror mode sound pan
* add cheats to menu!
* split some subtitles
* Add in-game option to switch fullscreen monitor
* mmm undefined memory :)
* Fix type consistency
* Optimize get_monitor and get_monitor_count since they're called often
* Address PR feedback
* Track fullscreen mode and minimized state to reduce GLFW calls per frame
* extractor: refactor and cleanup for multi-game support
* deps: switch to `ghc::filesystem` as it is utf-8 everywhere by default
* extractor: finally working with unicode
* unicode: fix unicode cli args on windows in all `main` functions