Compare commits

..
Author SHA1 Message Date
Tyler Wilding 1e307c8a9e i18n: add finnish to menus 2024-07-31 22:34:55 -04:00
Tyler Wilding 3a67f40098 i18n: remove meta line 2024-07-31 22:21:04 -04:00
Aloqas cbfa65c78a Update text-id-h.gc 2024-07-31 23:43:55 +03:00
Aloqas 681c419f17 Update subtitle_meta_fi-FI.json 2024-07-31 23:43:42 +03:00
Aloqas a4e108cca6 city-save-lurkers-intro 2024-07-31 23:41:08 +03:00
Aloqas f1ee25c640 Merge branch 'master' into test2 2024-07-31 18:55:20 +03:00
Tyler Wilding af6de539b5 jak1/jak2: Persist sound settings, play-hints, subtitles and vibration settings in pc-settings instead of the memory card file (#3612)
In the original game, they had no choice but to use the memory card file
as their method of persisting settings. We are not limited by such
things.

It's inconvenient to have to load your save-file when launching the game
to initialize these settings to your liking, it's also confusing
behaviour to even some players that have played the game heavily for
over a decade. We can do better by globally saving these settings to the
`pc-settings` file instead.

Originally I only migrated the volume settings, then i figured it would
be nice to also have play-hints and subtitles settings persisted. More
could debatably be moved (language is a big one...) but these were the
low hanging fruit.

I also reduced the default volumes as that is something else that has
come up a few times.
2024-07-31 00:01:18 -04:00
Tyler Wilding c72a086e49 jak2: support mirror mode (#3616)
Fixes #3210


![image](https://github.com/user-attachments/assets/86bb6a67-bc6a-4169-aa82-d6a46ecd43d7)

TIL that on the PS4/PS5, mirror mode breaks the upscaling
2024-07-30 23:30:53 -04:00
Hat Kid a66d832b2f jak3: add more prim buckets (#3618)
Used by `prebot`
2024-07-30 19:50:00 +02:00
Tyler Wilding d29ab77892 g/j1: fix menu labelling issue in jak1 (#3613)
Fixes #3393
2024-07-29 21:57:26 -04:00
Tyler Wilding e975eab15a decompiler: Apply docstring indentation fix to all game versions (#3614)
Previously was only applying to game versions above Jak 2, Fixes #3342
2024-07-29 21:57:07 -04:00
OpenGOAL BotandOpenGOALBot 441952186c CI: Periodic Controller Database Update (#3615)
Updating Controller Database

Co-authored-by: OpenGOALBot <[email protected]>
2024-07-29 19:14:02 -04:00
Tyler Wilding d819d6da05 game: cleanup some display settings related code, forbid invalid game-size resolutions (#3601)
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.
2024-07-27 22:29:14 -04:00
Hat Kid ce978638fe jak3: add darkjak highres texture anim (#3611)
Eyes don't work yet
2024-07-28 02:45:00 +02:00
water111 9d0009715e [jak3] Fix alpha for prims, entity-table (#3609)
Fix issue where light-trail is invisible and some actors not spawning
due to out of memory.
2024-07-27 12:46:33 -04:00
water111 3856ae505a [jak3] Some cleanup/fixes around curve and light-trail (#3608)
They still don't work yet, this is just naming/comments to help with
debug.

The vehicle tracks are now at least trying to draw, but like the others,
don't actually show up.
2024-07-27 11:17:39 -04:00
Tyler Wilding 92c4390f0a jak1: Adjust sprite positioning or hide them where appropriate when using non-standard aspect ratios (#3596)
This attempts to do a best-effort quick fix for the sprite alignment in
the menus and first person views on higher aspect ratios. This:
- Hides the binocular borders completely when using a non-standard ratio
![Screenshot 2024-07-20
021430](https://github.com/user-attachments/assets/c56d3a6c-13b0-43e1-b99b-83292993728c)
- Hides the borders in jak's first person view when using a non-standard
ratio
![Screenshot 2024-07-20
021310](https://github.com/user-attachments/assets/fefca993-960b-4741-87b7-6d7c17efe89d)
- Uses a combination of manual alignment and approximation to get the
pause menu closer.
![Screenshot 2024-07-20
151725](https://github.com/user-attachments/assets/2c8aa759-b33a-4fbe-abc6-b5861fc33208)
> 32:9 screenshot.

I accomplished the last one by manually aligning all of the core sprites
and text for the most popular aspect ratios. This means that from a
practical standpoint, things should align "perfectly". However, I then
used all of those values to derive a polynomial for each adjustment
based on the aspect ratio. This allows the game to do a half-decent
approximation/interpolation for every aspect ratio in-between the common
ones. It won't be perfect, but it will be better than this:

![image](https://github.com/user-attachments/assets/420b1e38-6f88-436a-8e8c-21df6b49428e)
2024-07-26 23:15:51 -04:00
zakfaulk a7c9151c6c Jak1: Add Autosplitting options for tasks completed in one level and turned in in another (#3595)
For the FJ Mirrors, Muse, Lightning Moles, and Gambler's race the tasks
are completed in one level, then turned in at the hub. This presents a
spot of confusion for new players with the autosplitter and requires
hacky workarounds for those who want to split on these conditions. This
pull request adds in the necessary code to the autosplit-h.gc and
autosplit.gc files so that the autosplitter can identify these events,
similar to the talk to fisherman and catch fish options in the current
autosplitter.
2024-07-26 23:12:30 -04:00
water111 82a23c747e [jak3] prim rendering for cloth (#3607) 2024-07-26 20:31:32 -04:00
water111 f0b46ff2e5 [jak3] A few bug fixes (#3606)
A few minor fixes:

- Fix crash in overlord3 during final boss
https://github.com/open-goal/jak-project/issues/3605
- Update goal_src for `scene-actor.gc`, which was not updated after a
bug fix for decompiling skelgroups, making some cutscene actors
invisible due to using the wrong joint for culling checks.
- Stop using `-1` as an invalid value for texture id's in Merc.cpp. This
could sometimes cause Merc2.cpp to accidentally skip updating the OpenGL
texture. This fixes the bug where skull gems sometimes didn't have the
animated textures.
2024-07-26 13:48:43 -04:00
water111 9d80ada016 [jak3] Fix eye slot assignment and textures (#3603)
I found two issues with Jak 3 eyes. The first was simple - we were
missing a `-pc` texture upload in `texture.gc` for `pris2` textures,
which has eye textures for a few characters, like torn or damas.

The second was a little more annoying. Unlike jak 2 and jak 1, jak 3 can
dynamically assign eye slots when merc models are loaded. This involves
modifying eye data to tell the eye renderer where to render, and
modifying the merc model's adgif shaders to point to the correct eye
texture. The modification to the merc adgif shader is problematic since
our PC port of merc assumes this slot is constant.

My solution here was to bypass this whole slot system entirely for jak
3. I modified the GOAL eye renderer to tell the c++ eye renderer the
name of the merc-ctrl containing the eye. Then, the PC C++ Merc renderer
can just look up the merc-ctrl by name. To make this fit nicely in the
existing memory layout, I used a 64-bit fnv hash of the name. (which
honestly is how we should have handled a lot of other texture/model
names stuff...)

Unrelated fix to Overlord2 so it handles the case where file size
changes after the game starts, I had this in jak2/jak1 and forgot it for
jak 3.
2024-07-26 11:42:52 -04:00
water111 e81431bd21 [wip] Jak 3 Overlord (#3567) 2024-07-26 09:42:28 -04:00
OpenGOAL BotandOpenGOALBot 57772c59a0 CI: Periodic Controller Database Update (#3600)
Updating Controller Database

Co-authored-by: OpenGOALBot <[email protected]>
2024-07-23 20:36:54 -04:00
Hat Kid edae60d58d decompiler: support merc model replacements and adding custom actor models to vanilla fr3s (#3597)
This adds support for replacing existing merc models in FR3 files with
custom GLB model files. The replacements go in
`custom_assets/<GAME>/merc_replacements`, similar to texture
replacements. When a `.glb` file with a file name that matches any model
present in an FR3 is detected (e.g. `eichar-lod0` for Jak), all merc
model data is replaced with the given model.

Additionally, models for custom actors can now also be added to vanilla
FR3s. The models for this go in
`custom_assets/<GAME>/models/<LEVEL_NAME>` (e.g.
`custom_assets/jak1/models/jungleb/test-actor-lod0.glb`) and will be
added to the FR3 that has a matching name (exception: to add things to
the common level file, the folder should be named `common` instead of
`GAME`).
For custom levels, these now go in
`custom_assets/<GAME>/models/custom_levels` (previously
`custom_assets/<GAME>/models`).

Another small change: When level ripping is enabled, the resulting model
files will now be stored in game name subfolders inside of `glb_out`.
2024-07-21 01:51:31 +02:00
Hat Kid 365fae4913 jak3: fix bbush rings (#3593)
Fixes #3592
2024-07-17 21:49:07 +02:00
OpenGOAL BotandOpenGOALBot 3d04ac958c CI: Periodic Controller Database Update (#3585)
Updating Controller Database

Co-authored-by: OpenGOALBot <[email protected]>
2024-07-16 19:45:50 -04:00
Tyler Wilding 849ba391d4 game: log more OpenGL info (#3588)
Logs the OpenGL vendor and renderer, in most cases this helps identify
what GPU the game is using, which is something that comes up from
time-to-time in support.


![image](https://github.com/user-attachments/assets/4d9ca6fa-f3ea-440e-bce2-7d22dab21ec6)
2024-07-16 19:40:50 -04:00
Hat Kid c8fbe26c7d jak3: fix texture anim alpha (#3587) 2024-07-15 21:39:44 +02:00
Hat Kid fbab87b0fe jak3: fix opengl error spam (#3586) 2024-07-15 20:21:52 +02:00
Hat Kid 1c170b741c jak3: fix robo-hover hand cannon nans (#3584)
Fixes #3583.

The callback function for the hand cannon joint mods was initialized
prior to calling `ja-post`, propagating NaNs.
2024-07-15 16:28:28 +02:00
Brent HickeyandHat Kid b8f1ee5289 [high fps] Increase input buffer for jak1 and jak3 (#3578)
Applying https://github.com/open-goal/jak-project/pull/3178 to jak1 and
jak3

This also fixes cloud speed in jak3

---------

Co-authored-by: Hat Kid <[email protected]>
2024-07-15 02:56:10 +02:00
Hat Kid bdded9ad8c jak3: add missing texture animations (#3577)
This refactors some of the texture animation code a bit to better
support multiple games and adds most of the missing texture animations
for Jak 3 with a couple of exceptions/bugs:

- `hanga-sprite`: Despite `move_to_pool` being set for this anim, it's
not showing up in-game, but it does display properly in the ImGUI debug
window.

![image](https://github.com/open-goal/jak-project/assets/6624576/29df8e17-8831-412b-b9b7-7704d6bd7813)
- `factoryc-alpha`: Some conveyors do not have the animation for some
reason.

![image](https://github.com/open-goal/jak-project/assets/6624576/209ef073-2a81-4e2c-b020-dc2ae0b01196)
- This spot in Spargus seems to use texture animations, but it looks
like it maps to the `fora-water-dest` texture/slot, which comes from
`foresta-water`. Because the texture is not initialized, the texture
shows up black on first load, but it does show up after loading
`foresta`.

![image](https://github.com/open-goal/jak-project/assets/6624576/1e54bab2-f97c-47d5-a92a-a98a52c30178)

![image](https://github.com/open-goal/jak-project/assets/6624576/240b0137-1e9e-4e65-8446-0f78df9802dd)
- `hfrag` texture anim is not handled yet. Probably needs some special
casing.
2024-07-15 02:25:18 +02:00
Hat Kid 08a23793c8 jak3: fix some nan bugs (#3581)
Fixes a couple of NaN bugs, making the Arena and Marauder Stronghold
missions, the leaper corralling mission and final boss playable:

- Fixes #3579:
- After catching a leaper, the `flut` that spawns would have a NaN
`world-sphere`
- Fixes #3580:
- `vf0` was being clobbered after a `suspend`, causing them to spawn at
the origin.
- The Terraformer's `world-sphere` would be NaN until an animation
started playing.
2024-07-15 01:54:52 +02:00
ZedB0T 291c89a851 Update volume in sound-group sog3 to sfx-volume (#3574)
Adds a way to override certain sounds that don't respect the settings
that they should...



https://github.com/open-goal/jak-project/assets/89345505/97d9518d-aafd-4227-b25f-82c5d32e811e
2024-07-09 01:24:03 +02:00
Tyler Wilding b4113dda67 decompiler: Cleanup duplication in extractor/decompiler and make it easier to enable streamed audio ripping from CLI (#3560)
This centralizes the code that both `extractor` and the decompiler
executes. In the past this code was partially-duplicated, meaning that
the `extractor` could only do _some_ operations and not others (ie.
could not extract the audio files).

I also simplified the process to enable audio streaming in the
configuration. This is to support a new feature in the launcher that
allows you to enable these options for the decompiler:


![image](https://github.com/open-goal/jak-project/assets/13153231/8e6c20a1-8b5b-46f0-bceb-7644f713989f)
2024-06-29 01:18:39 -04:00
OpenGOAL BotandOpenGOALBot a485c236d6 CI: Periodic Controller Database Update (#3564)
Updating Controller Database

Co-authored-by: OpenGOALBot <[email protected]>
2024-06-24 14:44:01 -04:00
Tyler Wilding 73ff53f01d debugging: Improve event profiler utility (#3561)
- Can make the event buffer larger or smaller
- UI shows the current event index / size, so you know how fast it's
filling up
- Can save compressed, 10x reduction in filesize and Windows 11 explorer
actually supports ZSTD natively now so this isn't inconvenient at all

![Screenshot 2024-06-22
000343](https://github.com/open-goal/jak-project/assets/13153231/2f7dfa41-d931-4170-a848-840cbed9be9f)
> An example of almost 1 million events.  Results in a 4mb file.
2024-06-22 22:01:33 -04:00
Aloqas 495a8bc26f rewording stuff 2024-06-20 18:26:15 +03:00
OpenGOAL BotandOpenGOALBot 90c11bde8f CI: Periodic Controller Database Update (#3557)
Updating Controller Database

Co-authored-by: OpenGOALBot <[email protected]>
2024-06-19 19:24:47 -04:00
Aloqas c2cccc48cd another change 2024-05-24 15:35:17 +03:00
Aloqas c5effb2395 small change 2024-05-24 12:14:29 +03:00
Aloqas 5f3671b8a3 small fixes 2024-05-24 11:02:02 +03:00
Aloqas 26bfac8555 Jak 2: Finnish subtitles and game text 2024-05-23 22:54:42 +03:00
606 changed files with 68700 additions and 9681 deletions

No files matched your search

+5
View File
@@ -58,6 +58,11 @@ custom_assets/jak1/texture_replacements/*
custom_assets/jak2/texture_replacements/*
custom_assets/jak3/texture_replacements/*
# merc replacements
custom_assets/jak1/merc_replacements/*
custom_assets/jak2/merc_replacements/*
custom_assets/jak3/merc_replacements/*
# generated cmake files
svnrev.h
common/versions/revision.h
-1
View File
@@ -145,7 +145,6 @@
"name": "Game - Jak 1 - Runtime (boot)",
"args": [
"-v",
"--portable",
"--game",
"jak1",
"--",
+2 -2
View File
@@ -4,7 +4,7 @@
#include "fmt/core.h"
std::string DmaTag::print() {
std::string DmaTag::print() const {
std::string result;
const char* mode_names[8] = {"refe", "cnt", "next", "ref", "refs", "call", "ret", "end"};
result += fmt::format("TAG: 0x{:08x} {:4s} qwc 0x{:04x}", addr, mode_names[(int)kind], qwc);
@@ -15,7 +15,7 @@ std::string DmaTag::print() {
return result;
}
std::string VifCode::print() {
std::string VifCode::print() const {
std::string result;
switch (kind) {
+2 -2
View File
@@ -52,7 +52,7 @@ struct DmaTag {
bool operator!=(const DmaTag& other) const { return !((*this) == other); }
std::string print();
std::string print() const;
};
inline void emulate_dma(const void* source_base, void* dest_base, u32 tadr, u32 dadr) {
@@ -148,7 +148,7 @@ struct VifCode {
u16 num;
u16 immediate;
std::string print();
std::string print() const;
};
struct VifCodeStcycl {
+1
View File
@@ -240,6 +240,7 @@ static FormFormattingConfig new_top_level_inline_form(bool elide_new_line) {
const std::unordered_map<std::string, FormFormattingConfig> opengoal_form_config = {
{"case", new_pair_rule(true)},
{"case-str", new_pair_rule(true)},
{"cond", new_pair_rule(false)},
{"#cond", new_pair_rule(false)},
{"in-package", new_top_level_inline_form(true)},
+28 -7
View File
@@ -26,6 +26,8 @@ u64 get_current_tid() {
}
#endif
#include "common/log/log.h"
#include "common/util/compress.h"
#include "common/util/string_util.h"
// clang-format on
u64 get_current_ts() {
@@ -34,12 +36,12 @@ u64 get_current_ts() {
GlobalProfiler::GlobalProfiler() {
m_t0 = get_current_ts();
set_max_events(65536);
m_nodes.resize(m_max_events);
}
void GlobalProfiler::set_max_events(size_t event_count) {
ASSERT(!m_enabled);
m_nodes.resize(event_count);
void GlobalProfiler::update_event_buffer_size(size_t new_size) {
m_max_events = new_size;
m_nodes.resize(m_max_events);
}
void GlobalProfiler::set_waiting_for_event(const std::string& event_name) {
@@ -72,6 +74,10 @@ void GlobalProfiler::root_event() {
instant_event("ROOT");
}
size_t GlobalProfiler::get_next_idx() {
return (m_next_idx % m_nodes.size());
}
void GlobalProfiler::begin_event(const char* name) {
event(name, ProfNode::BEGIN);
}
@@ -96,8 +102,10 @@ void GlobalProfiler::set_enable(bool en) {
m_enabled = en;
}
void GlobalProfiler::dump_to_json(const std::string& path) {
ASSERT(!m_enabled);
void GlobalProfiler::dump_to_json() {
if (m_enabled) {
set_enable(false);
}
nlohmann::json json;
auto& trace_events = json["traceEvents"];
@@ -185,7 +193,20 @@ void GlobalProfiler::dump_to_json(const std::string& path) {
t.second.highest_at_target);
}
file_util::write_text_file(path, json.dump());
if (m_enable_compression) {
const auto json_str = json.dump();
const auto compressed_data =
compression::compress_zstd_no_header(json_str.data(), json_str.size());
auto file_path = file_util::get_jak_project_dir() / "profile_data" /
fmt::format("prof-{}.json.zst", str_util::current_local_timestamp_no_colons());
file_util::create_dir_if_needed_for_file(file_path);
file_util::write_binary_file(file_path, compressed_data.data(), compressed_data.size());
} else {
auto file_path = file_util::get_jak_project_dir() / "profile_data" /
fmt::format("prof-{}.json", str_util::current_local_timestamp_no_colons());
file_util::create_dir_if_needed_for_file(file_path);
file_util::write_text_file(file_path, json.dump());
}
}
GlobalProfiler gprof;
+8 -2
View File
@@ -17,7 +17,8 @@ struct ProfNode {
class GlobalProfiler {
public:
GlobalProfiler();
void set_max_events(size_t event_count);
size_t get_max_events() { return m_max_events; }
void update_event_buffer_size(size_t new_size);
void set_waiting_for_event(const std::string& event_name);
void instant_event(const char* name);
void begin_event(const char* name);
@@ -25,11 +26,16 @@ class GlobalProfiler {
void end_event();
void clear();
void set_enable(bool en);
void dump_to_json(const std::string& path);
void dump_to_json();
void root_event();
bool is_enabled() { return m_enabled; }
size_t get_next_idx();
bool m_enable_compression = false;
private:
std::atomic_bool m_enabled = false;
size_t m_max_events = 65536;
u64 m_t0 = 0;
std::atomic_size_t m_next_idx = 0;
std::vector<ProfNode> m_nodes;
+1 -1
View File
@@ -140,7 +140,7 @@ void printstd(const char* format, va_list arg_list) {
internal::log_vprintf(format, arg_list);
}
// how many extra log files for a single program should be kept?
// how many extra log files for a single program should be kept
constexpr int LOG_ROTATE_MAX = 10;
void set_file(const std::string& filename,
+5 -2
View File
@@ -29,8 +29,11 @@ class Wrapper {
Wrapper(const std::string& _username,
const Config& config,
const StartupFile& startup,
bool nrepl_alive)
: username(_username), repl_config(config), startup_file(startup), nrepl_alive(nrepl_alive) {}
bool _nrepl_alive)
: username(_username),
repl_config(config),
startup_file(startup),
nrepl_alive(_nrepl_alive) {}
replxx::Replxx& get_repl() { return repl; }
void init_settings();
void reload_startup_file();
+145 -1
View File
@@ -43,7 +43,151 @@ std::vector<std::string> jak2_slots = {
};
std::vector<std::string> jak3_slots = {
"skull-gem-dest", "jakc-arm", "jakc-eyebrow", "jakc-face", "jakc-finger", "jakc-hair",
"skull-gem-dest",
"jakc-arm",
"jakc-eyebrow",
"jakc-face",
"jakc-finger",
"jakc-hair",
"jakchires-arm",
"jakchires-eye",
"jakchires-eyebrow",
"jakchires-eyelid",
"jakchires-facelft",
"jakchires-facert",
"jakchires-hair",
// default-water
"bomb-gradient",
"blue-beam-dest",
"lightjak-wings",
"mushroom-dest",
// default-warp
"shield-env-rim-dest",
// templea-water
"templea-waterfall-dest",
// templea-warp
"holograph-env-rim-dest",
// hanga-sprite
"glider-ring-dest2",
"glider-ring-dest",
// foresta-water
"fora-water-dest",
"fora-waterfall-01-dest",
"fora-water-wave-01-dest",
// forestb-water
"forb-water-dest",
"forb-waterfall-01-dest",
"forb-water-wave-01-dest",
// lforplnt-pris
"mh-gem-dest",
// lmhcitya-tfrag
"mhcitya-base-goo-01-dest",
// lmhcityb-tfrag
"mhcityb-base-goo-01-dest",
// mhcitya-pris
// "mhcity-de-door-skin-01-dest",
// templec-water
"tplc-water-dest",
// sewc-water
"sewer-water-01-c-dest",
"sewer-waterfall-01-c-dest",
"sewer-waterfall-02-c-dest",
"sewer-water-wave-01-c-dest",
"sewer-water-highlight-01-c-dest",
// sewd-water
"sewer-water-01-d-dest",
"sewer-waterfall-02-d-dest",
"sewer-water-highlight-01-d-dest",
"sewer-water-wave-01-d-dest",
"sewer-water-wave-02-d-dest",
"sewer-water-still-01-d-dest",
// sewe-water
"sewer-water-01-e-dest",
"sewer-waterfall-01-e-dest",
"sewer-waterfall-02-e-dest",
"sewer-water-highlight-01-e-dest",
// sewg-water
"sewer-water-01-g-dest",
"sewer-waterfall-02-g-dest",
"sewer-water-wave-01-g-dest",
// sewh-water
"sewer-water-01-h-dest",
"sewer-waterfall-02-h-dest",
"sewer-water-wave-02-h-dest",
"sewer-watefall-froth-01-h-dest",
// sewi-water
"sewer-water-still-01-i-dest",
"sewer-waterfall-02-i-dest",
"sewer-water-wave-01-i-dest",
// sewj-water
"sewer-waterfall-02-j-dest",
"sewer-watefall-froth-01-j-dest",
// sewl-water
"sewer-waterfall-02-l-dest",
"sewer-watefall-froth-01-l-dest",
// sewm-water
"sewer-water-01-m-dest",
"sewer-waterfall-01-m-dest",
"sewer-waterfall-02-m-dest",
"sewer-water-highlight-01-m-dest",
"sewer-water-wave-01-m-dest",
"sewer-water-still-01-m-dest",
"sewer-watefall-froth-01-m-dest",
// sewn-water
"sewer-waterfall-01-n-dest",
"sewer-waterfall-02-n-dest",
// "sewer-water-highlight-01-n-dest",
"sewer-water-wave-01-n-dest",
"sewer-water-still-01-n-dest",
// hanga-water
// "des-thermal-01-dest",
// desresc-warp
"sat-shield-dest",
// security
"security-env-dest",
"security-dot-dest",
// lgunnorm-water
"kg-target-c-forcefield-01-dest",
// templex-water
"temple-waterfall-dest",
// desertd-water
"des-waterfall-dest",
// towerb-water
"tow-energy-bridge-dest",
// factoryb-water
"hemi-gradient-dest",
"hemi-gradient-flames-dest",
// factoryc-alpha
"facc-convey-dest",
"facc-convey-02-dest",
// waspal-water
"waspala-water-dest",
"waspala-waterfall-dest",
// rubblea-water
"rub-water-dest",
// rubblea2-water
"rub-water-desta2",
// rubbleb-water
"rub-water-destb",
// rubblec-water
"rub-water-destc",
// nstb-quicksand
"nstb-quicksand-dest",
// ctyslumb-water
"ctyslumb-water-dest",
"ctyslumb-fountain-fall-dest",
// ctyslumc-water
"ctyslumc-water-dest",
"ctyslumc-fountain-fall-dest",
// mined-tfrag
"mined-pillar-side-dest",
"mined-pillar-top2side-dest",
"mined-pillar-top-dest",
// volcanoa-alpha
"vola-lava-01-dest",
"vola-lava-fall-dest",
// wasstada-alpha
"wstd-lava-base-dest",
};
} // namespace
+13
View File
@@ -46,4 +46,17 @@ std::vector<u8> decompress_zstd(const void* data, size_t size) {
ASSERT(decomp_size == decompressed_size);
return result;
}
std::vector<u8> compress_zstd_no_header(const void* data, size_t size) {
size_t max_compressed = ZSTD_compressBound(size);
std::vector<u8> result(max_compressed);
size_t compressed_size = ZSTD_compress(result.data(), max_compressed, data, size, 1);
if (ZSTD_isError(compressed_size)) {
ASSERT_MSG(false, fmt::format("ZSTD error: {}", ZSTD_getErrorName(compressed_size)));
}
result.resize(compressed_size);
return result;
}
} // namespace compression
+2 -1
View File
@@ -8,4 +8,5 @@ namespace compression {
// compress and decompress data with zstd
std::vector<u8> compress_zstd(const void* data, size_t size);
std::vector<u8> decompress_zstd(const void* data, size_t size);
} // namespace compression
std::vector<u8> compress_zstd_no_header(const void* data, size_t size);
} // namespace compression
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <string>
#include "common/common_types.h"
inline u64 fnv64(const void* data, u64 len) {
u64 ret = 0xcbf29ce484222325;
const auto* ptr = (const u8*)data;
for (u64 i = 0; i < len; i++) {
ret = 1099511628211 * (((u64)*ptr) ^ ret);
ptr++;
}
return ret;
}
inline u64 fnv64(const std::string& str) {
return fnv64(str.data(), str.length());
}
+3 -3
View File
@@ -27,11 +27,11 @@ Range<int> parse_json_optional_integer_range(const nlohmann::json& json);
}
template <typename T>
void json_get_optional(const nlohmann::json& json,
void json_get_optional(const nlohmann::json& j,
const std::string& key,
std::optional<T>& optionalValue) {
if (json.contains(key) && !json[key].is_null()) {
optionalValue = json[key].get<T>();
if (j.contains(key) && !j[key].is_null()) {
optionalValue = j[key].get<T>();
}
}
@@ -62,7 +62,7 @@
// Note: You will still have to add them to your level's .gd file.
// "art_groups": ["plat-ag"],
// If you have any custom models in the "custom_assets/jak1/models" folder that you want to use in your level, add them to this list.
// If you have any custom models in the "custom_assets/jak1/models/custom_levels" folder that you want to use in your level, add them to this list.
// Note: Like with art groups, these should also be added to your level's .gd file.
"custom_models": ["test-actor"],
+1
View File
@@ -68,6 +68,7 @@ add_library(
level_extractor/fr3_to_gltf.cpp
level_extractor/MercData.cpp
level_extractor/tfrag_tie_fixup.cpp
level_extractor/merc_replacement.cpp
ObjectFile/LinkedObjectFile.cpp
ObjectFile/LinkedObjectFileCreation.cpp
+1 -1
View File
@@ -25,7 +25,7 @@ namespace decompiler {
*/
class LinkedObjectFile {
public:
LinkedObjectFile(GameVersion version) : version(version){};
LinkedObjectFile(GameVersion _version) : version(_version) {}
void set_segment_count(int n_segs);
void push_back_word_to_segment(uint32_t word, int segment);
int get_label_id_for(int seg, int offset);
+2 -6
View File
@@ -54,12 +54,8 @@ void append_body_to_function_definition(goos::Object* top_form,
body_elements.insert(body_elements.end(), inline_body.begin(), inline_body.end());
// If the first element in the body is a docstring, add it first
if (body_elements.size() > 0 && body_elements.at(0).is_string()) {
if (version > GameVersion::Jak2) {
initial_top_level_forms.push_back(
goos::StringObject::make_new(fix_docstring_indent(inline_body.at(0).as_string()->data)));
} else {
initial_top_level_forms.push_back(inline_body.at(0));
}
initial_top_level_forms.push_back(
goos::StringObject::make_new(fix_docstring_indent(inline_body.at(0).as_string()->data)));
body_elements.erase(body_elements.begin());
}
+1 -1
View File
@@ -21102,7 +21102,7 @@
:size-assert #x94
:flag-assert #xf00300094
(:methods
(dumb-15 (_type_) none))
(spawn-particles! (_type_) none))
(:states
hud-normal
hud-coming-in
+14
View File
@@ -2515,6 +2515,7 @@
(iop-mem 48)
(cancel-dgo 49)
(set-stereo-mode 50) ;; sound-rpc-set-stereo-mode
(set-mirror 201)
)
(defenum sound-group
@@ -2829,6 +2830,17 @@
:flag-assert #x900000004
)
; added
(defenum sound-mirror-mode
:type uint8
(normal)
(mirrored)
)
; added for mirror mode
(deftype sound-rpc-set-mirror-mode (sound-rpc-cmd)
((mirror sound-mirror-mode)))
(deftype sound-rpc-union (structure)
((data uint32 20 :offset-assert 0)
(load-bank sound-rpc-load-bank :offset 0)
@@ -2852,6 +2864,7 @@
(shutdown sound-rpc-shutdown :offset 0)
(list-sounds sound-rpc-list-sounds :offset 0)
(unload-music sound-rpc-unload-music :offset 0)
(mirror-mode sound-rpc-set-mirror-mode :overlay-at (-> data 0))
)
:method-count-assert 9
:size-assert #x50
@@ -8774,6 +8787,7 @@ Unlike [[engine]], users can use [[engine-pers]] as a parent class."
(progress-graphics-ps2-lod-dist #x1337)
(progress-graphics-msaa-off #x1338)
(progress-graphics-display #x1339)
(language-name-finnish #x133a)
)
;; ---text-id-h:text-id
+177 -108
View File
@@ -15147,8 +15147,8 @@
:size-assert #x4
:flag-assert #xb00000004
(:methods
(init! (_type_) int) ;; 9
(cloth-base-method-10 (_type_ cloth-params handle) int) ;; 10
(update! (_type_) int) ;; 9
(setup-from-params! (_type_ cloth-params handle) int) ;; 10
)
)
@@ -25908,8 +25908,33 @@
:flag-assert #xe00000010
)
;; doesn't exist, but referenced by type ref in the entity table.
(deftype sail-boat-a (process-drawable-reserved)
()
)
(deftype cty-window-a (process-drawable-reserved)
()
)
(deftype city-window-a (process-drawable-reserved)
()
)
(deftype cty-guard-turret (process-drawable-reserved)
()
)
(deftype cty-fruit-stand (process-drawable-reserved)
()
)
(deftype torn (process-drawable-reserved)
()
)
(deftype entity-info (basic)
((ptype type :offset-assert 4) ;; guessed by decompiler
((ptype object :offset-assert 4) ;; guessed by decompiler
(pool symbol :offset-assert 8) ;; guessed by decompiler
(heap-size int32 :offset-assert 12)
)
@@ -33950,7 +33975,7 @@
)
(deftype particle-array (inline-array-class)
((data verlet-particle :dynamic :inline :offset-assert 16)
((data verlet-particle :dynamic :inline :offset-assert 16 :score 1)
)
:method-count-assert 14
:size-assert #x10
@@ -34017,12 +34042,12 @@
:size-assert #x40
:flag-assert #x1000000040
(:methods
(calculate-wind!
(accumulate-external-forces!
"If this cloth system has the wind flag, calculate the wind force."
(_type_) none) ;; 11
(verlet-particle-system-method-12 (_type_ float) none) ;; 12
(verlet-particle-system-method-13 (_type_) none) ;; 13
(verlet-particle-system-method-14 (_type_) none) ;; 14
(compute-verlet-step (_type_ float) none) ;; 12
(run-one-iteration "Run one iteration of the system." (_type_) none) ;; 13
(reset! "Reset to stationary/default state." (_type_) none) ;; 14
(debug-draw (_type_) none) ;; 15
)
)
@@ -34082,9 +34107,9 @@
"Set up this cloth system with the given [[cloth-params]]."
(_type_ cloth-params) none) ;; 16
(debug-draw-spheres (_type_) none) ;; 17
(cloth-system-method-18 (_type_) int) ;; 18
(cloth-system-method-19 (_type_) none) ;; 19
(cloth-system-method-20 (_type_) none) ;; 20
(post-physics-update (_type_) int) ;; 18
(enforce-constraints-1 (_type_) none) ;; 19
(enforce-constraints-2 (_type_) none) ;; 20
(cloth-system-method-21 (_type_) none) ;; 21
(cloth-system-method-22 (_type_) none) ;; 22
(cloth-system-method-23 (_type_) none) ;; 23
@@ -34098,9 +34123,9 @@
(cloth-system-method-31 (_type_ current-position-info) none) ;; 31
(cloth-system-method-32 (_type_ vector int int current-position-info) none) ;; 32
(cloth-system-method-33 (_type_ vu-lights) none) ;; 33
(cloth-system-method-34 (_type_) none) ;; 34
(cloth-system-method-35 (_type_) none) ;; 35
(cloth-system-method-36 (_type_) none) ;; 36
(hide! (_type_) none) ;; 34
(reset-locations (_type_) none) ;; 35
(pre-physics-update "Callback to update prior to running verlet integration. Can handle movement of the entire system in the world, for example" (_type_) none) ;; 36
(cloth-system-cmd-handler (_type_ pair) none) ;; 37
)
)
@@ -34122,8 +34147,8 @@
;; cloth ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-extern get-neighboring-faces (function vector int int int int int))
(define-extern *normal-array* (pointer vector))
(define-extern get-neighboring-faces (function vector4w int int int int int))
(define-extern *normal-array* (inline-array vector))
(define-extern light-vertex (function current-position-info vector rgba))
(define-extern *once* symbol)
(define-extern *cloth-fade-alpha* gs-alpha)
@@ -34196,7 +34221,17 @@
;; curves ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defenum loop-behavior
:type uint64
:bitfield #f
(wrap)
(clamp)
(b2)
(use-default)
)
(deftype float-pair (structure)
"Two floats. Specifies one point on a piecewise linear curve."
((first float :offset-assert 0)
(second float :offset-assert 4)
(x float :offset 0)
@@ -34208,6 +34243,7 @@
)
(deftype float-pair-array (inline-array-class)
"Array of points used to make a piecewise linear curve."
((data float-pair :dynamic :inline :offset-assert 16)
)
:method-count-assert 14
@@ -34216,39 +34252,50 @@
)
(deftype curve2d (basic)
"Interface for evaluating a 2d curve.
The input is a float (x-position) and the output is a float (y-position).
The curve is over (0, 1). Values outside of the range are either clamped
or wrapped depending on the loop-behavior flag."
()
:method-count-assert 10
:size-assert #x4
:flag-assert #xa00000004
(:methods
(curve2d-method-9 (_type_ float int) float) ;; 9
(evaluate "Compute value of curve at the given position." (_type_ float loop-behavior) float) ;; 9
)
)
(deftype curve-color (basic)
"Interface for evaluating a color curve. The input is a float, representing
progress through the curve, and the result is a floating point rgba color."
()
:method-count-assert 10
:size-assert #x4
:flag-assert #xa00000004
(:methods
(curve-color-method-9 (_type_ float rgbaf int) rgbaf) ;; 9
(evaluate "Compute value of curve at the given position." (_type_ float rgbaf loop-behavior) rgbaf) ;; 9
)
)
(deftype curve2d-piecewise (curve2d)
"Implementation of 2d-curve for a piecewise linear curve.
Not particularly efficient - each evaluation needs to check each point."
((pts float-pair-array :offset-assert 4)
(default-loop-behavior uint64 :offset-assert 8)
(default-loop-behavior loop-behavior :offset-assert 8)
)
:method-count-assert 12
:size-assert #x10
:flag-assert #xc00000010
(:methods
(curve2d-piecewise-method-10 (_type_ int symbol int) none) ;; 10
(allocate! "Allocate memory for points." (_type_ int symbol symbol) none) ;; 10
(curve2d-piecewise-method-11 (_type_) none) ;; 11
)
)
(deftype curve2d-fast (curve2d)
"Implementation of 2d piecewise linear curve which tries to be faster.
While it is faster, it places the huge restriction that you can only have 4 points.
Note that the xs should be negative here."
((xs vector :inline :offset-assert 16)
(ys vector :inline :offset-assert 32)
(one-over-x-deltas vector :inline :offset-assert 48)
@@ -34259,6 +34306,9 @@
)
(deftype curve-color-fast (curve-color)
"Implementation of color curve which tries to be faster.
While it is faster, it again has the restriction that you only
get 4 piecewise sections."
((xs vector :inline :offset-assert 16)
(ys vector 4 :inline :offset-assert 32)
(one-over-x-deltas vector :inline :offset-assert 96)
@@ -34269,6 +34319,8 @@
)
(deftype color-pair (structure)
"Single section of a piecewise linear color curve.
Unlike the fast version, this stores x values exactly like you'd expect."
((first float :offset-assert 0)
(second rgbaf :inline :offset-assert 16)
(x float :offset 0)
@@ -34280,6 +34332,7 @@
)
(deftype color-pair-array (inline-array-class)
"Array of points for piecewise linear color curve."
((data color-pair :dynamic :inline :offset-assert 16)
)
:method-count-assert 14
@@ -34288,21 +34341,22 @@
)
(deftype curve-color-piecewise (curve-color)
"Implementation of curve-color."
((pts color-pair-array :offset-assert 4)
(default-loop-behavior uint64 :offset-assert 8)
(default-loop-behavior loop-behavior :offset-assert 8)
)
:method-count-assert 11
:size-assert #x10
:flag-assert #xb00000010
(:methods
(curve-color-piecewise-method-10 (_type_ int symbol uint) none) ;; 10
(allocate! "Allocate memory for points." (_type_ int symbol symbol) none) ;; 10
)
)
(define-extern rgbaf-lerp! (function rgbaf rgbaf rgbaf float rgbaf))
(define-extern evaluate-curve-fast (function curve2d-fast rgbaf rgbaf float))
(define-extern evaluate-color-curve-fast (function curve-color-fast rgbaf rgbaf rgbaf))
(define-extern rgba<-rgbaf (function rgba rgbaf int))
(define-extern rgbaf-lerp! "Lerp all four components of rgba." (function rgbaf rgbaf rgbaf float rgbaf))
(define-extern evaluate-curve-fast "Evaluate a curve2d-fast at the given value." (function curve2d-fast float float))
(define-extern evaluate-color-curve-fast "Evaluate a color-curve-fast at the given value." (function curve-color-fast float rgbaf rgbaf))
(define-extern rgba<-rgbaf "Convert rgbaf to rgba. Seems like the input rgba's value is not used in any way." (function rgba rgbaf rgba))
(define-extern *curve-unity* curve2d-fast)
(define-extern *curve-linear-up* curve2d-fast)
(define-extern *curve-linear-down* curve2d-fast)
@@ -34679,6 +34733,14 @@
:flag-assert #xe00000010
)
(defenum lie-mode
:type uint64
(appearance0)
(appearance1)
(appearance2)
(use-two-strips 3)
)
(deftype light-trail-composition (structure)
((color-mode uint64 :offset-assert 0)
(color-curve curve-color-piecewise :offset-assert 8)
@@ -34696,8 +34758,8 @@
(uv-mode uint64 :offset-assert 72)
(uv-repeat-dist float :offset-assert 80)
(max-age time-frame :offset-assert 88)
(tex-id uint32 :offset-assert 96)
(lie-mode uint64 :offset-assert 104)
(tex-id texture-id :offset-assert 96)
(lie-mode lie-mode :offset-assert 104)
(lie-vector vector :inline :offset-assert 112)
(zbuffer? symbol :offset-assert 128)
(use-tape-mode? symbol :offset-assert 132)
@@ -34711,7 +34773,7 @@
(deftype light-trail-breadcrumb (structure)
((pos vector :inline :offset-assert 0)
(birth-time uint32 :offset 12 :decomp-as time-frame)
(birth-time uint32 :offset 12 :decomp-as time-frame :score 1)
)
:method-count-assert 9
:size-assert #x10
@@ -34726,15 +34788,22 @@
:flag-assert #xe00000010
)
(defenum light-trail-decision
:type uint64
(added 0)
(not-added 1)
(reset 2)
)
(deftype light-trail (basic)
((crumb-array (array light-trail-breadcrumb) :offset-assert 4)
((crumb-array (array uint8) :offset-assert 4)
(crumb-size uint8 :offset-assert 8)
(crumb-count int16 :offset-assert 10)
(max-crumb-count int16 :offset-assert 12)
(appearance light-trail-composition :offset-assert 16)
(start-marker uint64 :offset-assert 24)
(end-marker uint64 :offset-assert 32)
(decision uint64 :offset-assert 40)
(decision light-trail-decision :offset-assert 40)
(total-distance-traveled float :offset-assert 48)
(strip prim-strip :offset-assert 52)
(strip2 prim-strip :offset-assert 56)
@@ -34744,19 +34813,19 @@
:size-assert #x80
:flag-assert #x1600000080
(:methods
(light-trail-method-9 (_type_ light-trail-composition int) none) ;; 9
(light-trail-method-10 (_type_) none) ;; 10
(light-trail-method-11 (_type_ vector time-frame) int) ;; 11
(light-trail-method-12 (_type_) none) ;; 12
(light-trail-method-13 (_type_) int) ;; 13
(light-trail-method-14 (_type_) none) ;; 14
(setup! "Initialize, including allocation of crumbs and strips on the process heap." (_type_ light-trail-composition int) none) ;; 9
(reset! "Clear all tracked crumbs." (_type_) none) ;; 10
(add-crumb! "Try adding a crumb, kicking out the oldest if there's not enough room. This may reject if it's too close to the previous." (_type_ vector time-frame) int) ;; 11
(build-prim-strip! "Build the mesh for this light trail." (_type_) none) ;; 12
(common-trans! "Call this on each frame to handle max-age." (_type_) int) ;; 13
(expire-old-points! "Internal function to kill off points that are too old." (_type_) none) ;; 14
(light-trail-method-15 (_type_) none) ;; 15
(add-vert! (_type_ prim-strip vector float float float) none) ;; 16
(light-trail-method-17 (_type_ vector float float vector float) symbol) ;; 17
(light-trail-method-18 (_type_ light-trail-breadcrumb int vector vector) none) ;; 18
(light-trail-method-19 (_type_ float int) none) ;; 19
(add-vert-to-prim-strip! "Add a single vertex to the prim." (_type_ prim-strip vector rgba float float) none) ;; 16
(add-tri-pair-to-prim! "Add two vertices to the prim strip to add two triangles" (_type_ vector rgba float vector float) symbol) ;; 17
(calc-vertex-pos! (_type_ light-trail-breadcrumb int vector vector) none) ;; 18
(crumb-age-out-callback "To be implemented by the user to smoothly interpolate out non-default entries in their crumb type." (_type_ float int) none) ;; 19
(reset-crumbs! (_type_) none) ;; 20
(light-trail-method-21 (_type_ vector) none) ;; 21
(replace-last-crumb! "Similar to add-crumb, but will modify the last crumb instead of advancing." (_type_ vector) none) ;; 21
)
)
@@ -34774,7 +34843,7 @@
:size-assert #x80
:flag-assert #x1800000080
(:methods
(weapon-trail-method-22 (_type_ vector vector) light-trail-breadcrumb) ;; 22
(add-crumb-with-offset "Given two points, add the first and offset to a crumb." (_type_ vector vector) light-trail-breadcrumb) ;; 22
(weapon-trail-method-23 (_type_ vector vector) none) ;; 23
)
)
@@ -34793,8 +34862,8 @@
:size-assert #x80
:flag-assert #x1800000080
(:methods
(tread-trail-method-22 (_type_ vector vector) light-trail-breadcrumb) ;; 22
(tread-trail-method-23 (_type_ vector vector) light-trail-breadcrumb) ;; 23
(add-crumb-with-offset (_type_ vector vector) none) ;; 22
(tread-trail-method-23 (_type_ vector vector) none) ;; 23
)
)
@@ -34834,11 +34903,11 @@
die ;; 15
)
(:methods
(light-trail-tracker-method-16 (_type_ process-focusable vector) vector) ;; 16
(light-trail-tracker-method-17 (_type_ process-focusable) symbol) ;; 17
(light-trail-tracker-method-18 (_type_ process-focusable) symbol) ;; 18
(light-trail-tracker-method-19 (_type_) symbol) ;; 19
(light-trail-tracker-method-20 (_type_ vector) none) ;; 20
(get-tracked-object-pos (_type_ process-focusable vector) vector) ;; 16
(should-track? (_type_ process-focusable) symbol) ;; 17
(should-end? (_type_ process-focusable) symbol) ;; 18
(should-draw? (_type_) symbol) ;; 19
(add-crumb! (_type_ vector) none) ;; 20
)
)
@@ -42779,7 +42848,7 @@
:size-assert #xfc
:flag-assert #x22008000fc
(:methods
(task-manager-desert-bbush-ring-method-32 (_type_) none) ;; 32
(get-mgr-name (_type_) string) ;; 32
(task-manager-desert-bbush-ring-method-33 (_type_) none) ;; 33
)
)
@@ -44496,12 +44565,12 @@
;; templea-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *templea-water-texture-anim-array* texture-anim-array)
;; (define-extern *templea-warp-texture-anim-array* texture-anim-array)
;; (define-extern templea-warp-texture-anim-func function)
;; (define-extern *templeb-warp-texture-anim-array* texture-anim-array)
;; (define-extern templeb-warp-texture-anim-func function)
;; (define-extern *templec-water-texture-anim-array* texture-anim-array)
(define-extern *templea-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *templea-warp-texture-anim-array* (texture-anim-array texture-anim))
(define-extern templea-warp-texture-anim-func (function none))
(define-extern *templeb-warp-texture-anim-array* (texture-anim-array texture-anim))
(define-extern templeb-warp-texture-anim-func (function none))
(define-extern *templec-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; hover-training ;;
@@ -46284,16 +46353,16 @@
;; sewer-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *sewc-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewd-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewe-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewg-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewh-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewi-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewj-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewl-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewm-water-texture-anim-array* texture-anim-array)
;; (define-extern *sewn-water-texture-anim-array* texture-anim-array)
(define-extern *sewc-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewd-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewe-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewg-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewh-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewi-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewj-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewl-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewm-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *sewn-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; kg-hopper ;;
@@ -50287,7 +50356,7 @@
;; desresc-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *desresc-warp-texture-anim-array* texture-anim-array)
(define-extern *desresc-warp-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; desert-rescue ;;
@@ -50984,7 +51053,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-extern check-drop-level-ctyslumb-drop-userdata (function sparticle-system sparticle-cpuinfo vector none))
(define-extern *ctyslumb-water-texture-anim-array* texture-anim-array)
(define-extern *ctyslumb-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; mech-part ;;
@@ -51090,7 +51159,7 @@
;; nstb-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *nstb-quicksand-anim-array* texture-anim-array)
(define-extern *nstb-quicksand-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; nst-mood ;;
@@ -51733,7 +51802,7 @@
)
)
(define-extern *ctyslumc-water-texture-anim-array* texture-anim-array)
(define-extern *ctyslumc-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern ctyslumc-flag-init-by-other (function vector entity-actor object :behavior ctyslumc-flag))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -51908,8 +51977,8 @@
;; factoryc-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *factoryc-alpha-texture-anim-array* texture-anim-array)
;; (define-extern set-facc-conveyor-speed! function)
(define-extern *factoryc-alpha-texture-anim-array* (texture-anim-array texture-anim))
(define-extern set-facc-conveyor-speed! (function float float))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; conveyor ;;
@@ -52558,7 +52627,7 @@
(define-extern wang-texture-anim-init (function texture-anim none))
(define-extern real-wang-texture-anim-func (function texture-anim texture-anim none))
(define-extern wang-texture-anim-func (function none))
(define-extern wang-texture-anim-func (function texture-anim texture-anim none))
(define-extern *hfrag-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -54182,7 +54251,7 @@
;; glider-ring-part ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-extern *hanga-sprite-texture-anim-array* texture-anim-array)
(define-extern *hanga-sprite-texture-anim-array* (texture-anim-array texture-anim))
(define-extern cloud-shadow-find-ground (function vector matrix vector vector float))
(define-extern birth-func-fader (function sparticle-system sparticle-cpuinfo sparticle-launchinfo none))
(define-extern sparticle-fader (function sparticle-system sparticle-cpuinfo sparticle-launchinfo none))
@@ -54385,7 +54454,7 @@
(define-extern hanga-login (function level none))
(define-extern hanga-activate (function level none))
(define-extern hanga-deactivate (function level none))
(define-extern *hanga-water-texture-anim-array* texture-anim-array)
(define-extern *hanga-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; elec-gate ;;
@@ -57037,7 +57106,7 @@
;; desertd-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *desertd-water-texture-anim-array* texture-anim-array)
(define-extern *desertd-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ctyfarm-obs ;;
@@ -57861,7 +57930,7 @@
(deftype tire-trail-crumb (light-trail-breadcrumb)
((offset vector :inline :offset-assert 16)
(uu float :offset 28)
(uu float :offset 28 :score 1)
)
:method-count-assert 9
:size-assert #x20
@@ -57874,13 +57943,13 @@
:size-assert #x80
:flag-assert #x1800000080
(:methods
(tire-trail-method-22 (_type_) none) ;; 22
(tire-trail-method-23 (_type_) none) ;; 23
(add-crumb-with-offset-and-uu (_type_ vector vector float) none) ;; 22
(tire-trail-method-23 (_type_ vector vector float) none) ;; 23
)
)
(deftype tire-trail-tracker (light-trail-tracker)
()
((trail tire-trail :override))
:method-count-assert 21
:size-assert #xac
:flag-assert #x15003000ac
@@ -60281,7 +60350,7 @@
;; mhcityb-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *lmhcityb-tfrag-texture-anim-array* texture-anim-array)
(define-extern *lmhcityb-tfrag-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; rublcst-scenes ;;
@@ -61291,7 +61360,7 @@
(define-extern init-mood-tower (function mood-context float))
(define-extern update-tower-lights (function mood-context none))
(define-extern update-mood-tower (function mood-context float int none :behavior time-of-day-proc))
(define-extern *towerb-water-texture-anim-array* texture-anim-array)
(define-extern *towerb-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; tower-scenes ;;
@@ -63331,7 +63400,7 @@
;; wasstada-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *wasstada-alpha-texture-anim-array* texture-anim-array)
(define-extern *wasstada-alpha-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; wasstada-mood ;;
@@ -63355,7 +63424,7 @@
;; factorya-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *factoryb-water-texture-anim-array* texture-anim-array)
(define-extern *factoryb-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; factoryb-init ;;
@@ -63367,9 +63436,9 @@
;; mhcitya-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *lmhcitya-tfrag-texture-anim-array* texture-anim-array)
;; (define-extern *mhcitya-pris-texture-anim-array* texture-anim-array)
;; (define-extern set-mhcity-de-door-skin! function)
(define-extern *lmhcitya-tfrag-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *mhcitya-pris-texture-anim-array* (texture-anim-array texture-anim))
(define-extern set-mhcity-de-door-skin! (function float float))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ctyinda-part ;;
@@ -63677,10 +63746,10 @@
;; rubble-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *rubblea-water-texture-anim-array* texture-anim-array)
;; (define-extern *rubblea2-water-texture-anim-array* texture-anim-array)
;; (define-extern *rubbleb-water-texture-anim-array* texture-anim-array)
;; (define-extern *rubblec-water-texture-anim-array* texture-anim-array)
(define-extern *rubblea-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *rubblea2-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *rubbleb-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *rubblec-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; stadiuma-part ;;
@@ -65151,9 +65220,9 @@
;; forest-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *foresta-water-texture-anim-array* texture-anim-array)
;; (define-extern *forestb-water-texture-anim-array* texture-anim-array)
;; (define-extern *lforplnt-pris-texture-anim-array* texture-anim-array)
(define-extern *foresta-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *forestb-water-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *lforplnt-pris-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; forest-ring-chase ;;
@@ -65410,8 +65479,8 @@
;; ltnfxhip-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *ltnfxhip-texture-anim-array* texture-anim-array)
;; (define-extern ltnfxhip-texture-anim-func function)
(define-extern *ltnfxhip-texture-anim-array* (texture-anim-array texture-anim))
(define-extern ltnfxhip-texture-anim-func (function none))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ctygenb-part ;;
@@ -67812,7 +67881,7 @@
;; gungame-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *lgunnorm-water-texture-anim-array* texture-anim-array)
(define-extern *lgunnorm-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; gungame-obs ;;
@@ -68092,8 +68161,8 @@
;; ljkdxvin-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *ljkdxvin-texture-anim-array* texture-anim-array)
;; (define-extern ljkdxvin-texture-anim-func function)
(define-extern *ljkdxvin-texture-anim-array* (texture-anim-array texture-anim))
(define-extern ljkdxvin-texture-anim-func (function none))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; target-tube ;;
@@ -71489,7 +71558,7 @@
;; waspal-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *waspal-water-texture-anim-array* texture-anim-array)
(define-extern *waspal-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; throne-scenes ;;
@@ -72665,7 +72734,7 @@
;; mined-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-extern *mined-tfrag-texture-anim-array* texture-anim-array)
(define-extern *mined-tfrag-texture-anim-array* (texture-anim-array texture-anim))
(define-extern set-mined-pillar-texture! (function float none))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -73051,8 +73120,8 @@
;; volcanox-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *volcanox-warp-texture-anim-array* texture-anim-array)
;; (define-extern volcanox-warp-texture-anim-func function)
(define-extern *volcanox-warp-texture-anim-array* (texture-anim-array texture-anim))
(define-extern volcanox-warp-texture-anim-func (function none))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; volcanox-mood ;;
@@ -73252,7 +73321,7 @@
;; templex-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *templex-water-texture-anim-array* texture-anim-array)
(define-extern *templex-water-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; templex-scenes ;;
@@ -73635,7 +73704,7 @@
;; volcano-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *volcanoa-alpha-texture-anim-array* texture-anim-array)
(define-extern *volcanoa-alpha-texture-anim-array* (texture-anim-array texture-anim))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; spiky-frog ;;
@@ -74348,8 +74417,8 @@
;; deshover-texture ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (define-extern *deshover-texture-anim-array* texture-anim-array)
;; (define-extern deshover-texture-anim-func function)
(define-extern *deshover-texture-anim-array* (texture-anim-array texture-anim))
(define-extern deshover-texture-anim-func (function none))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; terraformer-head ;;
@@ -138,7 +138,7 @@
[7, "(function none)"],
[3, "(function symbol :behavior process)"]
],
"scene": [[4, "(function none :behavior scene-player)"]],
"scene": [[4, "(function symbol :behavior scene-player)"]],
"pov-camera": [
[
7,
+305 -1
View File
@@ -361,11 +361,315 @@
"jakc-finger-dark",
"jakc-hair-dark",
// darkjak-highres
"jakchires-arm",
"jakchires-arm-norm",
"jakchires-arm-dark",
"jakchires-eye",
"jakchires-eye-norm",
"jakchires-eye-dark",
"jakchires-eyebrow",
"jakchires-eyebrow-norm",
"jakchires-eyebrow-dark",
"jakchires-eyelid",
"jakchires-eyelid-norm",
"jakchires-eyelid-dark",
"jakchires-facelft",
"jakchires-facelft-norm",
"jakchires-facelft-dark",
"jakchires-facert",
"jakchires-facert-norm",
"jakchires-facert-dark",
"jakchires-hair",
"jakchires-hair-norm",
"jakchires-hair-dark",
// Skull Gem
"skull-gem-dest",
"skull-gem-alpha-00",
"skull-gem-alpha-01",
"skull-gem-alpha-02"
"skull-gem-alpha-02",
// default-water
"bomb-gradient",
"bomb-gradient-rim",
"bomb-gradient-flames",
"lightjak-wings",
"lighjak-wings-u-src",
"lightjak-wings-v-src",
"mushroom-dest",
"mushroom-src",
// default-warp
"shield-env-rim-dest",
"common-white",
"shield-env-uscroll",
"shield-env-rim-src",
// hanga-sprite
"glider-ring-dest2",
"glider-ring-dest",
"splash-foam",
"racegate",
// templea-water
"templea-waterfall-dest",
"templea-waterfall",
// templea-warp
"holograph-env-rim-dest",
"holograph-env-noise",
"holograph-env-scan",
"holograph-env-rim",
"environment-phong-rim",
// foresta-water
"fora-water-dest",
"fora-water",
"fora-waterfall-01-dest",
"fora-waterfall-01",
"fora-water-wave-01-dest",
"fora-water-wave-01",
// forestb-water
"forb-water-dest",
"forb-water",
"forb-waterfall-01-dest",
"forb-waterfall-01",
"forb-water-wave-01-dest",
"forb-water-wave-01",
// lforplnt-pris
"mh-gem-dest",
"mh-gem",
"mh-gem-alpha-01",
"mh-gem-alpha-02",
// lmhcitya-tfrag
"mhcitya-base-goo-01-dest",
"mhcitya-base-goo-01",
// lmhcityb-tfrag
"mhcityb-base-goo-01-dest",
"mhcityb-base-goo-01",
// mhcitya-pris
// "mhcity-de-door-skin-01-dest",
// "mhcity-de-door-skin-01",
// "mhcity-de-door-skin-02"
// templec-water
"tplc-water-dest",
"tplc-water",
// sewc-water
"sewer-water-01-c-dest",
"sewer-water-01-c",
"sewer-waterfall-01-c-dest",
"sewer-waterfall-01-c",
"sewer-waterfall-02-c-dest",
"sewer-waterfall-02-c",
"sewer-water-wave-01-c-dest",
"sewer-water-wave-01-c",
"sewer-water-highlight-01-c-dest",
"sewer-water-highlight-01-c",
// sewd-water
"sewer-water-01-d-dest",
"sewer-water-01-d",
"sewer-waterfall-02-d-dest",
"sewer-waterfall-02-d",
"sewer-water-highlight-01-d-dest",
"sewer-water-highlight-01-d",
"sewer-water-wave-01-d-dest",
"sewer-water-wave-01-d",
"sewer-water-wave-02-d-dest",
"sewer-water-wave-02-d",
"sewer-water-still-01-d-dest",
"sewer-water-still-01-d",
// sewe-water
"sewer-water-01-e-dest",
"sewer-water-01-e",
"sewer-waterfall-01-e-dest",
"sewer-waterfall-01-e",
"sewer-waterfall-02-e-dest",
"sewer-waterfall-02-e",
"sewer-water-highlight-01-e-dest",
"sewer-water-highlight-01-e",
// sewg-water
"sewer-water-01-g-dest",
"sewer-water-01-g",
"sewer-waterfall-02-g-dest",
"sewer-waterfall-02-g",
"sewer-water-wave-01-g-dest",
"sewer-water-wave-01-g",
// sewh-water
"sewer-water-01-h-dest",
"sewer-water-01-h",
"sewer-waterfall-02-h-dest",
"sewer-waterfall-02-h",
"sewer-water-wave-02-h-dest",
"sewer-water-wave-02-h",
"sewer-watefall-froth-01-h-dest",
"sewer-watefall-froth-01-h",
// sewi-water
"sewer-water-still-01-i-dest",
"sewer-water-still-01-i",
"sewer-waterfall-02-i-dest",
"sewer-waterfall-02-i",
"sewer-water-wave-01-i-dest",
"sewer-water-wave-01-i",
// sewj-water
"sewer-waterfall-02-j-dest",
"sewer-waterfall-02-j",
"sewer-watefall-froth-01-j-dest",
"sewer-watefall-froth-01-j",
// sewl-water
"sewer-waterfall-02-l-dest",
"sewer-waterfall-02-l",
"sewer-watefall-froth-01-l-dest",
"sewer-watefall-froth-01-l",
// sewm-water
"sewer-water-01-m-dest",
"sewer-water-01-m",
"sewer-waterfall-01-m-dest",
"sewer-waterfall-01-m",
"sewer-waterfall-02-m-dest",
"sewer-waterfall-02-m",
"sewer-water-highlight-01-m-dest",
"sewer-water-highlight-01-m",
"sewer-water-wave-01-m-dest",
"sewer-water-wave-01-m",
"sewer-water-still-01-m-dest",
"sewer-water-still-01-m",
"sewer-watefall-froth-01-m-dest",
"sewer-watefall-froth-01-m",
// sewn-water
"sewer-waterfall-01-n-dest",
"sewer-waterfall-01-n",
"sewer-waterfall-02-n-dest",
"sewer-waterfall-02-n",
// "sewer-water-highlight-01-n-dest",
// "sewer-water-highlight-01-n",
"sewer-water-wave-01-n-dest",
"sewer-water-wave-01-n",
"sewer-water-still-01-n-dest",
"sewer-water-still-01-n",
// hanga-water
// "des-thermal-01-dest",
// "des-thermal-01"
// desresc-warp
"sat-shield-dest",
"sat-shield-env-uvscroll",
"sat-shield",
// security
"security-env-dest",
"security-env-uscroll",
"security-dot-dest",
"security-dot-src",
// lgunnorm-water
"kg-target-c-forcefield-01-dest",
"kg-target-c-forcefield-01",
// templex-water
"temple-waterfall-dest",
"temple-waterfall",
// desertd-water
"des-waterfall-dest",
"des-waterfall",
// towerb-water
"tow-energy-bridge-dest",
"tow-energy-bridge",
// factoryb-water
"hemi-gradient-dest",
"hemi-gradient-rim",
"hemi-gradient-flames-dest",
"hemi-gradient-flames",
// factoryc-alpha
"facc-convey-dest",
"facc-convey",
"facc-convey-02-dest",
"facc-convey-02",
// waspal-water
"waspala-water-dest",
"waspala-water",
"waspala-waterfall-dest",
"waspala-waterfall",
// rubblea-water
"rub-water-dest",
"rub-water",
// rubblea2-water
"rub-water-desta2",
"rub-watera2",
// rubbleb-water
"rub-water-destb",
"rub-waterb",
// rubblec-water
"rub-water-destc",
"rub-waterc",
// nstb-quicksand
"nstb-quicksand-dest",
"nstb-quicksand-scroll",
// ctyslumb-water
"ctyslumb-water-dest",
"ctyslumb-water",
"ctyslumb-fountain-fall-dest",
"ctyslumb-fountain-fall",
// ctyslumc-water
"ctyslumc-water-dest",
"ctyslumc-water",
"ctyslumc-fountain-fall-dest",
"ctyslumc-fountain-fall",
// mined-tfrag
"mined-pillar-side-dest",
"mined-pillar-side-cold",
"mined-pillar-side-cooling",
"mined-pillar-side-hot",
"mined-pillar-molten",
"mined-pillar-top2side-dest",
"mined-pillar-top2side-cold",
"mined-pillar-top2side-cooling",
"mined-pillar-top2side-hot",
"mined-pillar-top-dest",
"mined-pillar-top-cold",
"mined-pillar-top-cooling",
"mined-pillar-top-hot",
"mined-pillar-molten-top",
// volcanoa-alpha
"vola-lava-01-dest",
"vola-lava-01",
"vola-lava-fall-dest",
"vola-lava-fall",
// wasstada-alpha
"wstd-lava-base-dest",
"wstd-lava-base"
],
"levels_to_extract": [
+22 -16
View File
@@ -1850,6 +1850,8 @@
["_stack_", 148, "float"],
["_stack_", 152, "float"],
["_stack_", 156, "float"],
[25, "a1", "light-trail-breadcrumb"],
[178, "s4", "light-trail-breadcrumb"],
[556, "a0", "vector"]
],
"compute-trail-scaled-t": [[17, "v1", "float"]],
@@ -2683,8 +2685,13 @@
[[21, 25], "a0", "prim-strip"]
],
"(event tracking light-trail-tracker)": [[55, "v1", "float"]],
"(method 21 light-trail)": [[50, "v1", "light-trail-breadcrumb"]],
"(method 14 light-trail)": [[47, "a0", "uint"]],
"(method 21 light-trail)": [[[18, 64], "gp", "light-trail-breadcrumb"]],
"(method 14 light-trail)": [
[47, "a0", "uint"],
[[10, 32], "a1", "light-trail-breadcrumb"],
[[34, 60], "s3", "light-trail-breadcrumb"],
[[34, 73], "s2", "light-trail-breadcrumb"]
],
"debug-menu-item-var-update-display-str": [
[48, "v1", "int"],
[63, "v1", "int"],
@@ -2735,16 +2742,12 @@
[27, "gp", "process-drawable"],
[30, "gp", "process-drawable"]
],
"(method 23 weapon-trail)": [
[62, "v1", "light-trail-breadcrumb"],
[65, "v1", "light-trail-breadcrumb"]
],
"(method 23 weapon-trail)": [[[0, 100], "gp", "weapon-trail-crumb"]],
"(method 22 weapon-trail)": [[32, "v0", "light-trail-breadcrumb"]],
"(method 22 tread-trail)": [[19, "v0", "light-trail-breadcrumb"]],
"(method 23 tread-trail)": [
[51, "v1", "light-trail-breadcrumb"],
[67, "v0", "light-trail-breadcrumb"]
],
"(method 23 tread-trail)": [[[0, 100], "s5", "tread-trail-crumb"]],
"(method 23 tire-trail)": [[[0, 74], "s5", "tire-trail-crumb"]],
"(method 22 tire-trail)": [[[18, 24], "v1", "tire-trail-crumb"]],
"(trans idle fma-sphere)": [[39, "a2", "process-drawable"]],
"part-water-splash-callback": [[3, "v1", "float"]],
"(method 15 water-control)": [[48, "v1", "float"]],
@@ -6428,11 +6431,11 @@
],
"cshape-reaction-scorp-shot": [[15, "v1", "v-scorp-shot"]],
"(method 78 wvehicle)": [
[262, "s1", "process-focusable"],
[271, "s1", "process-focusable"],
[287, "s1", "process-focusable"],
[309, "s1", "process-focusable"],
[322, "s1", "process-focusable"],
[262, "s1", "tire-trail-tracker"],
[271, "s1", "tire-trail-tracker"],
[287, "s1", "tire-trail-tracker"],
[309, "s1", "tire-trail-tracker"],
[322, "s1", "tire-trail-tracker"],
[358, "v1", "collide-shape-prim-group"]
],
"(method 97 wvehicle)": [
@@ -6551,7 +6554,10 @@
[15, "v1", "process-drawable"],
[22, "v1", "process-drawable"]
],
"(method 19 tire-trail)": [],
"(method 19 tire-trail)": [
[[5, 28], "s5", "tire-trail-crumb"],
[[5, 28], "s4", "tire-trail-crumb"]
],
"vehicle-spawn": [[93, "gp", "vehicle"]],
"vehicle-spawn-hack": [[41, "gp", "vehicle"]],
"(method 11 w-parking-spot)": [[42, "v0", "vector"]],
+41 -3
View File
@@ -515,8 +515,44 @@ TPageResultStats process_tpage(ObjectFileData& data,
// currently not needed.
break;
case int(PSM::PSMT8):
ASSERT(tex.clutpsm == int(CPSM::PSMCT32));
{
if (tex.clutpsm == (int)CPSM::PSMCT16) {
// will store output pixels, rgba (8888)
std::vector<u8> index_out;
// width is like the TEX0 register, in 64 texel units.
// not sure what the other widths are yet.
int read_width = 64 * tex.width[0];
// loop over pixels in output texture image
for (int y = 0; y < tex.h; y++) {
for (int x = 0; x < tex.w; x++) {
// read as the PSMT8 type. The dest field tells us a block offset.
auto addr8 = psmt8_addr(x, y, read_width) + tex.dest[0] * 256;
u8 value = *(u8*)(vram.data() + addr8);
index_out.push_back(value);
}
}
std::array<math::Vector4<u8>, 256> unscrambled_clut{};
for (int i = 0; i < 256; i++) {
u32 clut_chunk = i / 16;
u32 off_in_chunk = i % 16;
u8 clx = 0, cly = 0;
if (clut_chunk & 1) {
clx = 8;
}
cly = (clut_chunk >> 1) * 2;
if (off_in_chunk >= 8) {
off_in_chunk -= 8;
cly++;
}
clx += off_in_chunk;
u32 clut_addr = psmct16_addr(clx, cly, 64) + tex.clutdest * 256;
memcpy(&unscrambled_clut[i], vram.data() + clut_addr, 4);
}
texture_db.add_index_texture(texture_page.id, tex_id, index_out, unscrambled_clut,
tex.w, tex.h, tex.name, texture_page.name, level_names);
stats.successful_textures++;
} else if (tex.clutpsm == (int)PSM::PSMCT32) {
// will store output pixels, index (u8)
std::vector<u8> index_out;
@@ -555,10 +591,12 @@ TPageResultStats process_tpage(ObjectFileData& data,
texture_db.add_index_texture(texture_page.id, tex_id, index_out, unscrambled_clut,
tex.w, tex.h, tex.name, texture_page.name, level_names);
stats.successful_textures++;
} else {
ASSERT_NOT_REACHED();
}
break;
default:
lg::die("Animated texture {} format {}\n", tex.name, tex.psm);
lg::die("Animated texture {} format {} clut {}\n", tex.name, tex.psm, tex.clutpsm);
}
}
+7 -4
View File
@@ -322,7 +322,8 @@ void extract_common(const ObjectFileDB& db,
compressed.data(), compressed.size());
if (config.rip_levels) {
auto file_path = file_util::get_jak_project_dir() / "glb_out" / "common.glb";
auto file_path = file_util::get_jak_project_dir() / "glb_out" /
game_version_names[config.game_version] / "common.glb";
file_util::create_dir_if_needed_for_file(file_path);
save_level_foreground_as_gltf(tfrag_level, art_group_data, file_path);
}
@@ -360,15 +361,17 @@ void extract_from_level(const ObjectFileDB& db,
if (config.rip_levels) {
auto back_file_path = file_util::get_jak_project_dir() / "glb_out" /
fmt::format("{}_background.glb", level_data.level_name);
game_version_names[config.game_version] /
fmt::format("{}-background.glb", level_data.level_name);
file_util::create_dir_if_needed_for_file(back_file_path);
save_level_background_as_gltf(level_data, back_file_path);
auto fore_file_path = file_util::get_jak_project_dir() / "glb_out" /
fmt::format("{}_foreground.glb", level_data.level_name);
game_version_names[config.game_version] /
fmt::format("{}-foreground.glb", level_data.level_name);
file_util::create_dir_if_needed_for_file(fore_file_path);
save_level_foreground_as_gltf(level_data, art_group_data, fore_file_path);
}
file_util::write_text_file(entities_folder / fmt::format("{}_actors.json", level_data.level_name),
file_util::write_text_file(entities_folder / fmt::format("{}-actors.json", level_data.level_name),
extract_actors_to_json(bsp_header.actors));
}
@@ -1,5 +1,7 @@
#include "extract_merc.h"
#include "merc_replacement.h"
#include "common/log/log.h"
#include "common/util/BitUtils.h"
#include "common/util/FileUtil.h"
@@ -1597,6 +1599,60 @@ void create_modifiable_vertex_data(
}
}
void replace_model(tfrag3::Level& lvl, tfrag3::MercModel& model, const fs::path& mdl_path) {
if (model.max_bones < 100) {
auto lvl_name = lvl.level_name == "" ? "common" : lvl.level_name;
lg::info("Replacing {} for {}: {} effects, {} max bones, {} max draws\n", model.name, lvl_name,
model.effects.size(), model.max_bones, model.max_draws);
std::vector<tfrag3::MercVertex> old_verts;
for (auto& e : model.effects) {
for (auto& d : e.all_draws) {
for (size_t i = 0; i < d.index_count; i++) {
auto idx = lvl.merc_data.indices.at(i + d.first_index);
if (idx != UINT32_MAX) {
old_verts.push_back(lvl.merc_data.vertices[idx]);
}
}
}
}
auto swap_info = load_replacement_merc_model(model.name, lvl.merc_data.indices.size(),
lvl.merc_data.vertices.size(), lvl.textures.size(),
mdl_path.string(), old_verts, false);
model = swap_info.new_model;
size_t old_start = lvl.merc_data.vertices.size();
for (auto& ind : swap_info.new_indices) {
ASSERT(ind >= old_start);
}
lvl.merc_data.indices.insert(lvl.merc_data.indices.end(), swap_info.new_indices.begin(),
swap_info.new_indices.end());
lvl.merc_data.vertices.insert(lvl.merc_data.vertices.end(), swap_info.new_vertices.begin(),
swap_info.new_vertices.end());
lvl.textures.insert(lvl.textures.end(), swap_info.new_textures.begin(),
swap_info.new_textures.end());
}
}
void add_custom_model_to_level(tfrag3::Level& lvl,
const std::string& name,
const fs::path& mdl_path) {
auto lvl_name = lvl.level_name == "" ? "common" : lvl.level_name;
lg::info("Adding custom model {} to {}", name, lvl_name);
auto merc_data =
load_replacement_merc_model(name, lvl.merc_data.indices.size(), lvl.merc_data.vertices.size(),
lvl.textures.size(), mdl_path.string(), {}, true);
for (auto& idx : merc_data.new_indices) {
lvl.merc_data.indices.push_back(idx);
}
for (auto& vert : merc_data.new_vertices) {
lvl.merc_data.vertices.push_back(vert);
}
lvl.merc_data.models.push_back(merc_data.new_model);
lvl.textures.insert(lvl.textures.end(), merc_data.new_textures.begin(),
merc_data.new_textures.end());
}
/*!
* Top-level merc extraction
*/
@@ -1731,5 +1787,33 @@ void extract_merc(const ObjectFileData& ag_data,
}
}
}
// do model replacements if present
auto merc_replacement_folder = file_util::get_jak_project_dir() / "custom_assets" /
game_version_names[version] / "merc_replacements";
if (file_util::file_exists(merc_replacement_folder.string())) {
auto merc_replacements =
file_util::find_files_in_dir(merc_replacement_folder, std::regex(".*\\.glb"));
for (auto& path : merc_replacements) {
auto name = path.stem().string();
auto it = std::find_if(out.merc_data.models.begin(), out.merc_data.models.end(),
[&](const auto& m) { return m.name == name; });
if (it != out.merc_data.models.end()) {
auto& model = *it;
replace_model(out, model, path);
}
}
}
// add custom models if present
auto lvl_name = out.level_name == "" ? "common" : out.level_name;
auto models_folder = file_util::get_jak_project_dir() / "custom_assets" /
game_version_names[version] / "models" / lvl_name;
if (file_util::file_exists(models_folder.string())) {
auto custom_models = file_util::find_files_in_dir(models_folder, std::regex(".*\\.glb"));
for (auto& mdl : custom_models) {
add_custom_model_to_level(out, mdl.stem().string(), mdl);
}
}
}
} // namespace decompiler
@@ -0,0 +1,211 @@
#include "merc_replacement.h"
using namespace gltf_util;
namespace decompiler {
void extract(const std::string& name,
MercExtractData& out,
const tinygltf::Model& model,
const std::vector<NodeWithTransform>& all_nodes,
u32 index_offset,
u32 vertex_offset,
u32 tex_offset) {
ASSERT(out.new_vertices.empty());
std::map<int, tfrag3::MercDraw> draw_by_material;
int mesh_count = 0;
int prim_count = 0;
for (const auto& n : all_nodes) {
const auto& node = model.nodes[n.node_idx];
if (node.extras.Has("set_invisible") && node.extras.Get("set_invisible").Get<int>()) {
continue;
}
if (node.mesh >= 0) {
const auto& mesh = model.meshes[node.mesh];
mesh_count++;
for (const auto& prim : mesh.primitives) {
prim_count++;
// extract index buffer
std::vector<u32> prim_indices = gltf_util::gltf_index_buffer(
model, prim.indices, out.new_vertices.size() + vertex_offset);
ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, "Unsupported triangle mode");
// extract vertices
auto verts =
gltf_util::gltf_vertices(model, prim.attributes, n.w_T_node, true, true, mesh.name);
out.new_vertices.insert(out.new_vertices.end(), verts.vtx.begin(), verts.vtx.end());
out.new_colors.insert(out.new_colors.end(), verts.vtx_colors.begin(),
verts.vtx_colors.end());
out.normals.insert(out.normals.end(), verts.normals.begin(), verts.normals.end());
ASSERT(out.new_colors.size() == out.new_vertices.size());
// TODO: just putting it all in one material
auto& draw = draw_by_material[prim.material];
draw.mode = gltf_util::make_default_draw_mode(); // todo rm
draw.tree_tex_id = 0; // todo rm
draw.num_triangles += prim_indices.size() / 3;
draw.no_strip = true;
draw.index_count = prim_indices.size();
draw.first_index = index_offset + out.new_indices.size();
out.new_indices.insert(out.new_indices.end(), prim_indices.begin(), prim_indices.end());
}
}
}
tfrag3::MercEffect e;
out.new_model.name = name;
out.new_model.max_bones = 120;
out.new_model.max_draws = 200;
for (const auto& [mat_idx, d_] : draw_by_material) {
e.all_draws.push_back(d_);
auto& draw = e.all_draws.back();
draw.mode = make_default_draw_mode();
if (mat_idx == -1) {
lg::warn("Draw had a material index of -1, using default texture.");
draw.tree_tex_id = 0;
continue;
}
const auto& mat = model.materials[mat_idx];
int tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index;
if (tex_idx == -1) {
lg::warn("Material {} has no texture, using default texture.", mat.name);
draw.tree_tex_id = 0;
continue;
}
const auto& tex = model.textures[tex_idx];
ASSERT(tex.sampler >= 0);
ASSERT(tex.source >= 0);
draw.mode = draw_mode_from_sampler(model.samplers.at(tex.sampler));
const auto& img = model.images[tex.source];
draw.tree_tex_id = tex_offset + texture_pool_add_texture(&out.tex_pool, img);
}
lg::info("total of {} unique materials", e.all_draws.size());
e.has_mod_draw = false;
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count,
out.new_vertices.size());
}
const tfrag3::MercVertex& find_closest(const std::vector<tfrag3::MercVertex>& old,
float x,
float y,
float z) {
float best_dist = 1e10;
int best_idx = 0;
for (int i = 0; i < old.size(); i++) {
auto& v = old[i];
float dx = v.pos[0] - x;
float dy = v.pos[1] - y;
float dz = v.pos[2] - z;
float dist = (dx * dx) + (dy * dy) + (dz * dz);
if (dist < best_dist) {
best_dist = dist;
best_idx = i;
}
}
return old[best_idx];
}
void merc_convert_replacement(MercSwapData& out,
const MercExtractData& in,
const std::vector<tfrag3::MercVertex>& old_verts) {
out.new_model = in.new_model;
out.new_indices = in.new_indices;
out.new_textures = in.tex_pool.textures_by_idx;
// convert vertices
for (size_t i = 0; i < in.new_vertices.size(); i++) {
const auto& y = in.new_vertices[i];
const auto& copy_from = find_closest(old_verts, y.x, y.y, y.z);
auto& x = out.new_vertices.emplace_back();
x.pos[0] = y.x;
x.pos[1] = y.y;
x.pos[2] = y.z;
x.normal[0] = copy_from.normal[0];
x.normal[1] = copy_from.normal[1];
x.normal[2] = copy_from.normal[2];
x.weights[0] = copy_from.weights[0];
x.weights[1] = copy_from.weights[1];
x.weights[2] = copy_from.weights[2];
x.st[0] = y.s;
x.st[1] = y.t;
x.rgba[0] = in.new_colors[i][0];
x.rgba[1] = in.new_colors[i][1];
x.rgba[2] = in.new_colors[i][2];
x.rgba[3] = in.new_colors[i][3];
x.mats[0] = copy_from.mats[0];
x.mats[1] = copy_from.mats[1];
x.mats[2] = copy_from.mats[2];
}
}
void merc_convert_custom(MercSwapData& out, const MercExtractData& in) {
out.new_model = in.new_model;
out.new_indices = in.new_indices;
out.new_textures = in.tex_pool.textures_by_idx;
// convert vertices
for (size_t i = 0; i < in.new_vertices.size(); i++) {
const auto& y = in.new_vertices[i];
auto& x = out.new_vertices.emplace_back();
x.pos[0] = y.x;
x.pos[1] = y.y;
x.pos[2] = y.z;
x.normal[0] = in.normals.at(i).x();
x.normal[1] = in.normals.at(i).y();
x.normal[2] = in.normals.at(i).z();
x.weights[0] = 1.0f;
x.weights[1] = 0.0f;
x.weights[2] = 0.0f;
x.st[0] = y.s;
x.st[1] = y.t;
x.rgba[0] = in.new_colors[i][0];
x.rgba[1] = in.new_colors[i][1];
x.rgba[2] = in.new_colors[i][2];
x.rgba[3] = in.new_colors[i][3];
x.mats[0] = 3;
x.mats[1] = 0;
x.mats[2] = 0;
}
}
MercSwapData load_replacement_merc_model(const std::string& name,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::vector<tfrag3::MercVertex>& old_verts,
bool custom_mdl) {
MercSwapData result;
lg::info("Reading gltf mesh: {}", path);
tinygltf::TinyGLTF loader;
tinygltf::Model model;
std::string err, warn;
bool res = loader.LoadBinaryFromFile(&model, &err, &warn, path);
ASSERT_MSG(warn.empty(), warn.c_str());
ASSERT_MSG(err.empty(), err.c_str());
ASSERT_MSG(res, "Failed to load GLTF file!");
auto all_nodes = flatten_nodes_from_all_scenes(model);
MercExtractData extract_data;
extract(name, extract_data, model, all_nodes, current_idx_count, current_vtx_count,
current_tex_count);
if (custom_mdl) {
merc_convert_custom(result, extract_data);
} else {
merc_convert_replacement(result, extract_data, old_verts);
}
return result;
}
} // namespace decompiler
@@ -0,0 +1,32 @@
#pragma once
#include "common/log/log.h"
#include "common/util/gltf_util.h"
namespace decompiler {
struct MercExtractData {
gltf_util::TexturePool tex_pool;
std::vector<u32> new_indices;
std::vector<tfrag3::PreloadedVertex> new_vertices;
std::vector<math::Vector<u8, 4>> new_colors;
std::vector<math::Vector3f> normals;
tfrag3::MercModel new_model;
};
// Data produced by loading a replacement model
struct MercSwapData {
std::vector<u32> new_indices;
std::vector<tfrag3::MercVertex> new_vertices;
std::vector<tfrag3::Texture> new_textures;
tfrag3::MercModel new_model;
};
MercSwapData load_replacement_merc_model(const std::string& name,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::vector<tfrag3::MercVertex>& old_verts,
bool custom_mdl);
} // namespace decompiler
-1
View File
@@ -2576,7 +2576,6 @@ void CallOp::propagate_types2(types2::Instruction& instr,
if (can_use_call_parent) {
out_types[Register(Reg::GPR, Reg::V0)]->type = TP_Type::make_from_ts(call_parent_result_type);
lg::print("used special {}\n", call_parent_result_type.print());
use_normal_last_arg = false;
}
}
+24
View File
@@ -201,6 +201,7 @@ set(RUNTIME_SOURCE
mips2c/jak3_functions/sky.cpp
mips2c/jak3_functions/texture.cpp
mips2c/jak3_functions/particle_curves.cpp
mips2c/jak3_functions/cloth.cpp
mips2c/jak3_functions/collide_cache.cpp
mips2c/jak3_functions/collide_hash.cpp
mips2c/jak3_functions/collide_edge_grab.cpp
@@ -254,6 +255,29 @@ set(RUNTIME_SOURCE
overlord/jak2/streamlfo.cpp
overlord/jak2/streamlist.cpp
overlord/jak2/vag.cpp
overlord/jak3/overlord.cpp
overlord/jak3/pagemanager.cpp
overlord/jak3/iso_cd.cpp
overlord/jak3/dma.cpp
overlord/jak3/iso.cpp
overlord/jak3/iso_queue.cpp
overlord/jak3/srpc.cpp
overlord/jak3/vag.cpp
overlord/jak3/ssound.cpp
overlord/jak3/iso_api.cpp
overlord/jak3/spustreams.cpp
overlord/jak3/list.cpp
overlord/jak3/vblank_handler.cpp
overlord/jak3/dvd_driver.cpp
overlord/jak3/basefile.cpp
overlord/jak3/basefilesystem.cpp
overlord/jak3/ramdisk.cpp
overlord/jak3/isocommon.cpp
overlord/jak3/init.cpp
overlord/jak3/stream.cpp
overlord/jak3/sbank.cpp
overlord/jak3/soundcommon.cpp
overlord/jak3/streamlist.cpp
runtime.cpp
sce/deci2.cpp
sce/iop.cpp
+7
View File
@@ -54,4 +54,11 @@
:lines "game/assets/jak2/subtitle/subtitle_lines_en-GB.json"
:meta "game/assets/jak2/subtitle/subtitle_meta_en-GB.json"
:lines-base "game/assets/jak2/subtitle/subtitle_lines_en-US.json"
:meta-base "game/assets/jak2/subtitle/subtitle_meta_en-US.json")
(file-json
:language-id 9
:text-version "jak2"
:lines "game/assets/jak2/subtitle/subtitle_lines_fi-FI.json"
:meta "game/assets/jak2/subtitle/subtitle_meta_fi-FI.json"
:lines-base "game/assets/jak2/subtitle/subtitle_lines_en-US.json"
:meta-base "game/assets/jak2/subtitle/subtitle_meta_en-US.json"))
+2 -1
View File
@@ -21,7 +21,8 @@
(file-json 7 jak2 "common" '("game/assets/jak2/text/game_custom_text_en-GB.json"))
;; custom languages
(file-json 8 jak2 "common" '("game/assets/jak2/text/game_custom_text_pt-PT.json"))
(file-json 9 jak2 "common" '("game/assets/jak2/text/game_custom_text_fi-FI.json"))
(file-json 9 jak2 "common" '("game/assets/jak2/text/game_custom_text_fi-FI.json"
"game/assets/jak2/text/game_base_text_fi-FI.json"))
(file-json 10 jak2 "common" '("game/assets/jak2/text/game_custom_text_sv-SE.json"))
(file-json 11 jak2 "common" '("game/assets/jak2/text/game_custom_text_da-DK.json"))
(file-json 12 jak2 "common" '("game/assets/jak2/text/game_custom_text_no-NO.json"))
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,7 @@
{
"1a3": "138",
"1b5": "161",
"133a": "1113",
"1200": "1031",
"1201": "1032",
"1202": "1034",
@@ -0,0 +1,531 @@
{
"0100": "Lopeta",
"0101": "Tauko~",
"0102": "Tekstityksen kieli",
"0103": "Ääniformaatti",
"0104": "Mono",
"0105": "Stereo",
"0106": "Surround",
"0107": "Äänitehosteet",
"0108": "Musiikki",
"0109": "Puhe",
"010a": "Kieli",
"010b": "Värinätoiminto",
"010c": "Vinkit",
"010d": "Keskitä ruutu",
"010e": "Päällä",
"010f": "Pois",
"0110": "Käytä suuntanäppäimiä",
"0111": "<FLAG_USA> English",
"0112": "<FLAG_FRANCE> Fran,cais",
"0113": "<FLAG_GERMAN> Deutsch",
"0114": "<FLAG_SPAIN> Español",
"0115": "<FLAG_ITALIAN> Italiano",
"0116": "<FLAG_KOREA> ~Y~Z<K32e>~Z<K31a>~Z<K3c8>~Y~Z<K35e>~Z<K36b>~Z<K3de>~Y~Z<K30f>~Z<K31d>",
"0117": "<FLAG_JAPAN> にほんご",
"0118": "Kuvasuhde",
"0119": "Progressiivinen skannaus",
"011a": "Videotila",
"011b": "Peliasetukset",
"011c": "Grafiikan asetukset",
"011d": "Ääniasetukset",
"011e": "4x3",
"011f": "16x9",
"0120": "60Hz",
"0121": "50Hz",
"0122": "Jak II",
"0123": "Poistu demosta",
"0124": "Kyllä",
"0125": "Ei",
"0126": "Takaisin",
"0127": "OK",
"0128": "Seuraava",
"0129": "Edellinen",
"012a": "Jatka tallentamatta",
"012b": "Valitse tallennuspaikka",
"012c": "Valitse ladattava tallennus",
"012d": "Lataa peli",
"012e": "Tallenna peli",
"012f": "Tyhjä",
"0130": "Asetukset",
"0131": "Uusi peli",
"0132": "Paina START-näppäintä",
"0133": "Lopeta peli",
"0134": "Kartta",
"0135": "Valitse aloitus",
"0136": "Ennätykset",
"0137": "1. sija",
"0138": "2. sija",
"0139": "3. sija",
"013a": "4. sija",
"013b": "5. sija",
"013c": "6. sija",
"013d": "7. sija",
"013e": "8. sija",
"013f": "NYFE-leijuri",
"0140": "Luokka 1",
"0141": "Luokka 2",
"0142": "Luokka 3",
"0143": "Harjoitusrata",
"0144": "Haulikko",
"0145": "Läjäytin",
"0146": "Vulkanus-piippu",
"0147": "Rauhantekijä",
"0148": "Oninin haaste",
"0149": "Metallipää-mäiskintä",
"014a": "JET-lauta-haaste",
"014b": "Oninin teltta",
"014c": "Sikahevon saluuna",
"014d": "Stadion",
"014e": "Luokka 1 - käänteinen",
"014f": "Luokka 2 - käänteinen",
"0150": "Luokka 3 - käänteinen",
"0151": "Sataman kisa",
"0152": "Kaupungin kisa",
"0153": "Salaisuudet",
"0154": "Avattu",
"0155": "Poista Jakin pukinparta",
"0156": "Käänteinen maailma",
"0157": "Loputtomat ammukset",
"0158": "Haavoittumattomuus",
"0159": "Sankaritila",
"015a": "Välinäytösten 1. näytös",
"015b": "Välinäytösten 2. näytös",
"015c": "Välinäytösten 3. näytös",
"015d": "Leikekirja",
"015e": "Mega-leikekirja",
"015f": "Leikekirja 3",
"0160": "Tehtävän valinta",
"0161": "Loputon Raivo-Jak",
"0162": "Vulkanus-tykin harjoitusrata",
"0163": "Rauhantekijän harjoitusrata",
"0164": "Käänteiset kisat",
"0165": "Isopäinen Jak",
"0166": "Pienipäinen Jak",
"0167": "Saatavilla",
"0168": "Kerätty",
"0169": "Tehtävät",
"016a": "Valitse esialoitus",
"016b": "Valitse kioski-aloitus",
"016c": "Valitse kohtaus",
"016d": "Tekstitys",
"016e": "Aloita uudelleen",
"016f": "<ICON_MISSION_TODO>",
"0170": "<ICON_MISSION_COMPLETE>",
"0171": "Ei mitään",
"0172": "Peli",
"0173": "Aloita/lopeta",
"0174": "Osta",
"0175": "Voitto",
"0176": "Häviö",
"0177": "Kokonaisaika",
"0178": "Kierros ",
"0179": "Palauta painamalla <PAD_SQUARE>",
"017a": "Kiireinen",
"017b": "Jatka painamalla <PAD_X>",
"017c": "____~%Suoritetut tehtävät",
"017d": "Suorittamattomat tehtävät",
"017e": "Yritä uudelleen?",
"017f": "Salaisuus saatavilla päävalikosta",
"0180": "<PAD_X> = Kyllä, <PAD_TRIANGLE> = Ei",
"0181": "Paina <PAD_TRIANGLE> puhuaksesi.",
"0182": "Paina <PAD_TRIANGLE> käyttääksesi.",
"0183": "Paina <PAD_TRIANGLE> matkustaaksesi.",
"0184": "Valitse taso suuntanäppäimellä",
"0185": "Paina <PAD_TRIANGLE> poistuaksesi.",
"0186": "Paina <PAD_TRIANGLE> pelataksesi.",
"0187": "Halutako pelata uudelleen?",
"0188": "Tehtävä suoritettu",
"0189": "Tehtävä epäonnistui",
"018a": "Yritä uudelleen?",
"018b": "Ruutu ottaa nyt käyttöön 60Hz-tilan",
"018c": "Ruutu ottaa nyt käyttöön progressiivisen skannauksen",
"018d": "Jos kuva näkyy virheellisenä, televisiosi ei tue tätä tilaa",
"018e": "Jos niin käy, odota 10 sekuntia, niin ruutu palautuu takaisin",
"018f": "Ruutu on nyt 60Hz-tilassa",
"0190": "Ruutu on nyt Progresiivinen skannaus -tilassa",
"0191": "Haluatko pitää tämän tilan käytössä?",
"0192": "Levy poistettiin",
"0193": "Syötä Jak II -levy sisään, jotta voit jatkaa pelaamista",
"0194": "Virhe tietojen lukemisessa",
"0195": "Tarkista Jak II -levysi ja yritä uudelleen",
"0196": "Haluatko varmasti lopettaa?",
"0197": "Haluatko aloittaa pelin?",
"0198": "Ei tarpeeksi tilaa muistikortilla Memory__Card__(PS2) paikassa Memory__Card__Slot__~D",
"0199": "Ei Memory__Card__(PS2) liitettynä paikassa Memory__Card__slot__~D",
"019a": "Memory__Card__(PS2) paikassa Memory__Card__slot__~D on alustamaton",
"019b": "Jak II vaatii ~D Kt vapaata tilaa",
"019c": "Syötä Jak II -pelitietoja sisältävä Memory__Card__(PS2) paikkaan Memory__Card__slot__~D",
"019d": "Syötä Memory__Card__(PS2) jossa on tarpeeksi tilaa, muuten pelitietojasi ei voida tallentaa",
"019e": "Ilman alustamista pelitietoja ei voida tallentaa",
"019f": "Tallennetaan tietoja",
"01a0": "Ladataan tietoja",
"01a1": "Alustetaan",
"01a2": "Luodaan tallennustiedostoa",
"01a3": "Älä poista Memory__Card__(PS2) paikassa Memory__Card__slot__~D tai sammuta laitettasi",
"01a4": "Jak II -tallennus on jo olemassa valitsemassasi tiedostossa",
"01a5": "Haluatko korvata tämän?",
"01a6": "Alusta?",
"01a7": "Jatka?",
"01a8": "Takaisin?",
"01a9": "Virhe ladattaessa",
"01aa": "virhe tallentaessa",
"01ab": "Virhe alustaessa",
"01ac": "Virhe luodessa tallennustiedostoa",
"01ad": "Tarkista Memory__Card__(PS2) paikassa Memory__Card__slot__~D",
"01ae": "Tarkista Memory__Card__(PS2) paikassa Memory__Card__slot__~D ja yritä uudelleen",
"01af": "Memory__Card__(PS2) paikasta Memory__Card__slot__~D poistettiin",
"01b0": "Automaattinen tallennus on poistettu käytöstä",
"01b1": "Tallenna pelisi manuaalisesti, niin automaattinen tallennus palautetaan käyttöön",
"01b2": "Jak II -tallennustietoja ei löytynyt kortilta Memory__Card__(PS2) paikassa Memory__Card__slot__~D",
"01b3": "Haluatko luoda uuden Jak II -tallennuksen?",
"01b4": "Kun oheinen kuvake ilmestyy, edistykesi tallennetaan",
"01b5": "Kun tämä kuvake on ruudulla, älä irrota Memory__Card__(PS2) tai sammuta laitettasi",
"01b6": "Pakene <COLOR_WHITE>linnoituksesta",
"01b7": "Suojele Koria ja lasta",
"01b8": "Nouda lippu <COLOR_WHITE>aavekaupungista",
"01b9": "Etsi <COLOR_WHITE>pumppaamon<COLOR_PROGRESS> venttiili",
"01ba": "Räjäytä ammukset <COLOR_WHITE>linnoituksessa",
"01bb": "Kuljeta toimitus <COLOR_WHITE>Sikahevon saluunaan",
"01bc": "Läpäise haulikon <COLOR_WHITE>harjoitusrata",
"01bd": "Suojele Sigiä <COLOR_WHITE>pumppaamolla",
"01be": "Tuhoa tykit <COLOR_WHITE>viemärissä",
"01bf": "Etsi <COLOR_WHITE>läjäytin<COLOR_PROGRESS>-modi",
"01c0": "Pelasta Vin <COLOR_WHITE>avolouhoksella",
"01c1": "Voita aika <COLOR_WHITE>kisatalliin",
"01c2": "Läpäise läjäyttimen <COLOR_WHITE>harjoitusrata",
"01c3": "Tuhoa munat <COLOR_WHITE>poralautalla",
"01c4": "Etsi <COLOR_WHITE>pumppaamon<COLOR_PROGRESS> partio",
"01c5": "Nouda rahat Krewille",
"01c6": "Voita JET-lauta-haaste <COLOR_WHITE>stadionilla",
"01c7": "Etsi Linssi <COLOR_WHITE>vuoristotemppelissä",
"01c8": "Etsi Siru <COLOR_WHITE>vuoristotemppelissä",
"01c9": "Etsi Ratas <COLOR_WHITE>vuoristotemppelissä",
"01ca": "Käynnistä 5 virtakytkintä",
"01cb": "Nouse hissillä <COLOR_WHITE>palatsiin",
"01cc": "Päihitä paroni <COLOR_WHITE>palatsissa",
"01cd": "Kuljeta Vastarinnan jäsenet",
"01ce": "Suojele paikkaa <COLOR_WHITE>aavekaupungissa",
"01cf": "Nappaa tarkkailijat <COLOR_WHITE>Havenin metsässä",
"01d0": "Saata lapsi <COLOR_WHITE>voimalaan",
"01d1": "Pelasta vaanijoita Brutterille",
"01d2": "Tuhoa lasti <COLOR_WHITE>satamalla",
"01d3": "Pysäytä tankkeri",
"01d4": "Tuhoa välineistö <COLOR_WHITE>kaivannolla",
"01d5": "Räjäytä ecokaivot <COLOR_WHITE>avolouhoksella",
"01d6": "Tyhjennä <COLOR_WHITE>viemäri<COLOR_PROGRESS> löytääksesi patsaan",
"01d7": "Tuhoa alus <COLOR_WHITE>poralautalla",
"01d8": "Pelasta vaanijat <COLOR_WHITE>kaivannolla",
"01d9": "Tapa Metallipäät <COLOR_WHITE>Havenin metsässä",
"01da": "Voita luokan 3 kisa <COLOR_WHITE>stadionilla",
"01db": "Nouda sinetin pala <COLOR_WHITE>kaivannolla",
"01dc": "Nouda sinetin pala <COLOR_WHITE>vesislummeilla",
"01dd": "Tuhoa 5 Villikissa-leijuria",
"01de": "Voita Oninin haaste",
"01df": "Käytä esineitä <COLOR_WHITE>Ei-kenenkään-kanjonissa",
"01e0": "Kohtaa testit <COLOR_WHITE>Marin haudassa",
"01e1": "Avaa holvin ovi <COLOR_WHITE>Marin haudassa",
"01e2": "Läpäise ensimmäinen <COLOR_WHITE>miehuuden testi",
"01e3": "Läpäise toinen <COLOR_WHITE>miehuuden testi",
"01e4": "Päihitä paroni <COLOR_WHITE>Marin haudassa",
"01e5": "Pelasta ystävät <COLOR_WHITE>linnoituksesta",
"01e6": "Saata miehet <COLOR_WHITE>viemärien<COLOR_PROGRESS> läpi",
"01e7": "Etsi <COLOR_WHITE>Rauhantekijä",
"01e8": "Voita luokan 2 kisa <COLOR_WHITE>stadionilla",
"01e9": "Suojele piilopaikkaa pommiboteilta",
"01ea": "Tutki <COLOR_WHITE>rakennustyömaa",
"01eb": "Kiipeä <COLOR_WHITE>rakennustyömaata",
"01ec": "Voita Erol kisahaasteessa",
"01ed": "Tuhoa munat <COLOR_WHITE>avolouhoksella",
"01ee": "Nouda Elämän siemen <COLOR_WHITE>aavekaupungissa",
"01ef": "Vie Elämän siemen <COLOR_WHITE>Havenin metsään",
"01f0": "Suojele Samosia <COLOR_WHITE>Havenin metsässä",
"01f1": "Tuhoa <COLOR_WHITE>poralautan<COLOR_PROGRESS> torni",
"01f2": "Voita luokan 1 kisa <COLOR_WHITE>stadionilla",
"01f3": "Tutki <COLOR_WHITE>palatsia",
"01f4": "Hanki Marin sydän <COLOR_WHITE>asetehtaalla",
"01f5": "Päihitä Krew <COLOR_WHITE>asetehtaalla",
"01f6": "Voita peli <COLOR_WHITE>Sikahevon saluunassa",
"01f7": "Etsi Sig <COLOR_WHITE>satamanpohjalla",
"01f8": "Saata Sig <COLOR_WHITE>satamanpohjan<COLOR_PROGRESS> läpi",
"01f9": "Puolusta <COLOR_WHITE>stadionia",
"01fa": "Puolusta vaanijapalloa",
"01fb": "Tuhoa tunnelit <COLOR_WHITE>satamanpohjalla",
"01fc": "Taistele tiesi <COLOR_WHITE>pesän muurille",
"01fd": "Löydä Paroni <COLOR_WHITE>rakennustyömaalla",
"01fe": "Tuhoa <COLOR_WHITE>pesän muuri",
"01ff": "Tuhoa Metalli-Kor <COLOR_WHITE>pesällä",
"0200": "Puhu <COLOR_WHITE>Korille",
"0201": "Puhu <COLOR_WHITE>Tornille",
"0202": "Puhu <COLOR_WHITE>Krewille",
"0203": "Puhu <COLOR_WHITE>Oninille",
"0204": "Puhu <COLOR_WHITE>Vinille",
"0205": "Puhu <COLOR_WHITE>Varjolle",
"0206": "Puhu <COLOR_WHITE>Sigille",
"0207": "Puhu <COLOR_WHITE>Mekaanikolle",
"0208": "Puhu <COLOR_WHITE>Keiralle",
"0209": "Puhu <COLOR_WHITE>Brutterille",
"020a": "Tarkista <COLOR_WHITE>basaari",
"020b": "Tarkista <COLOR_WHITE>vesislummit",
"020c": "Mene <COLOR_WHITE>Ei-kenenkään-kanjonille",
"020d": "Mene <COLOR_WHITE>Marin hautaan",
"020e": "Voita <COLOR_WHITE>Metallipää-mäiskintä",
"020f": "Puolusta <COLOR_WHITE>sataman muuria",
"0210": "Tarkista <COLOR_WHITE>rakennustyömaa",
"0211": "Hyökkää <COLOR_WHITE>Metallipäiden pesään",
"0212": "Käy <COLOR_WHITE>Oraakkelin<COLOR_PROGRESS> luona",
"0213": "Haven City",
"0214": "Linnoitus",
"0215": "Laskeutumisalusta",
"0216": "Palatsin katto",
"0217": "Palatsi",
"0218": "Asetehdas",
"0219": "Aavekaupunki",
"021a": "Pumppaamo",
"021b": "Viemäri",
"021c": "Avolouhos",
"021d": "Vuoristotemppeli",
"021e": "Havenin metsä",
"021f": "Poralautta",
"0220": "Marin hauta",
"0221": "Kaivanto",
"0222": "Satamanpohja",
"0223": "Pesä",
"0224": "Hyppää painamalla <PAD_X>",
"0225": "Kieri painamalla <PAD_L1> liikkeessä",
"0226": "Paina <PAD_X> ja sitten <PAD_X> uudelleen ilmassa",
"0227": "Hyökkää painamalla <PAD_SQUARE> tai <PAD_CIRCLE>",
"0228": "Kieri ja paina sitten <PAD_X> syöksyhyppyyn",
"0229": "Hyppää ja paina <PAD_SQUARE> ilmassa",
"022a": "Paina <PAD_L1> ja sitten <PAD_X> korkeaan hyppyyn",
"022b": "Ammu painamalla <PAD_R1>",
"022c": "<PAD_DPAD_UP> laittaa aseen pois",
"022d": "<PAD_DPAD_UP> tai <PAD_R1> tuo aseen esiin",
"022e": "Pelaa haulikon harjoitusrata painamalla <PAD_X>",
"022f": "<PAD_X> = Haulikon harjoitus~%<PAD_SQUARE> = Vulkanus-tykin harjoitus",
"0230": "Vaihda asetta painamalla <PAD_DPAD_DOWN>",
"0231": "Pelaa läjäyttimen harjoitusrata painamalla <PAD_X>",
"0232": "<PAD_X> = Läjäyttimen harjoitus~%<PAD_SQUARE> = Rauhantekijän harjoitus",
"0233": "Potkaise kohdetta ja paina <PAD_R1> tehdäksesi pikatulisarjan",
"0234": "Ammu laatikoita",
"0235": "Tavoite",
"0236": "Kulta",
"0237": "Hopea",
"0238": "Pronssi",
"0239": "Käytä lautaa painamalla <PAD_R2>",
"023a": "Hyppy: Paina <PAD_X>",
"023b": "Kyykkyhyppy: Paina <PAD_L1> ja sitten <PAD_X>",
"023c": "Voimahyppy: Paina <PAD_X> hieman laskeuduttuasi hypystä",
"023d": "Kaideliuku: Paina <PAD_SQUARE> ja <PAD_ANALOG_ANY>",
"023e": "Pyörähdys: Paina <PAD_X> ja sitten <PAD_R1> ja <PAD_ANALOG_LEFT_RIGHT>",
"023f": "Voltti: Paina <PAD_X> ja sitten <PAD_R1> ja <PAD_ANALOG_UP_DOWN>",
"0240": "Temppu: Paina <PAD_X> ja sitten <PAD_L1> ja <PAD_ANALOG_ANY>",
"0241": "Saat paljon pisteitä, jos liu'ut ja teet tempun liukuaksesi uudestaan",
"0242": "Aloita haaste aktivoimalla tuomari",
"0243": "Yritä uudelleen!",
"0244": "Palaa talliin",
"0245": "Tehtävä suoritettu: Palaa Krewin luo",
"0246": "Vaihda leijuvyöhykettä painamalla <PAD_R2>",
"0247": "Sukella painamalla <PAD_CIRCLE> pohjassa uidessasi",
"0248": "<PAD_CIRCLE> = Turbo~%<PAD_R1> tai <PAD_L1> = Hyppy",
"0249": "Iske Titaaniasulla painamalla <PAD_SQUARE>",
"024a": "Tartu Titaaniasulla lohkareisiin painamalla <PAD_R1>",
"024b": "Heitä nostetut esineet painamalla <PAD_SQUARE>",
"024c": "Paina <PAD_X> rampin yläpäässä",
"024d": "Liu'u kaidetta JET-laudalla painamalla <PAD_SQUARE> tai <PAD_CIRCLE>",
"024e": "Käytä <PAD_DPAD_ANY> ja <PAD_X> <PAD_CIRCLE> <PAD_SQUARE> <PAD_TRIANGLE>",
"024f": "Pumppaamo",
"0250": "Avolouhos",
"0251": "Katso traileri",
"0252": "<PAD_R2> mennäksesi laudan päälle tai pois",
"0253": "Käytä pimeää voimaa painamalla <PAD_L2>",
"0254": "Sony Computer Entertainment America~%presents",
"0255": "Sony Computer Entertainment Europe~%presents",
"0256": "Sony Computer Entertainment Korea~%presents",
"0257": "Sony Computer Entertainment Inc.~%presents",
"0258": "A game by",
"0259": "Jak II",
"025a": "Kaksi vuotta myöhemmin...",
"025b": "Viikko myöhemmin...",
"025c": "Asepäivitys hankittu",
"025d": "Haulikko hankittu",
"025e": "Läjäytin-modi mankittu",
"025f": "Vulkanus-piippu hankittu",
"0260": "Rauhantekijä hankittu",
"0261": "Haulikon tulinopeus päivitetty",
"0262": "Aseiden ammuskapasiteetti päivitetty",
"0263": "Aseiden vahinkoa päivitetty",
"0264": "Tehtävä suoritettu",
"0265": "Punainen turvapassi hankittu",
"0266": "Vihreä turvapassi hankittu",
"0267": "Keltainen turvapassi hankittu",
"0268": "Palatsin turvapassi hankittu",
"0269": "Musta turvapassi hankittu",
"026a": "Linssi hankittu",
"026b": "Ratas hankittu",
"026c": "Siru hankittu",
"026d": "Tuo 25 metallipäiden kallohelmeä",
"026e": "Tuo 200 metallipäiden kallohelmeä",
"026f": "Tuo 25 metallipäiden kallohelmeä",
"0270": "Tuo 200 metallipäiden kallohelmeä",
"0271": "Tuo 200 metallipäiden kallohelmeä",
"0272": "Tuo 100 metallipäiden kallohelmeä",
"0273": "Raivopommi: Paina <PAD_X> ja sitten <PAD_SQUARE>",
"0274": "Raivopurkaus: Paina <PAD_SQUARE> ja sitten <PAD_X>",
"0275": "Raivo-Jak on nyt haavoittumaton!",
"0276": "Raivojätti: Paina <PAD_L2> raivotilassa",
"0277": "Ohi",
"0278": "Valitse tapahtuma",
"0279": "Skeittipuisto",
"027a": "Luokan 3 kisa",
"027b": "Luokan 2 kisa",
"027c": "Luokan 1 kisa",
"027d": "Käänteinen luokan 3 kisa",
"027e": "Käänteinen luokan 2 kisa",
"027f": "Käänteinen luokan 1 kisa",
"0280": "Daxter saa suupalan",
"0281": "Tavataan Torn",
"0282": "Noutamassa lippua",
"0283": "Suojele pyhää paikkaa",
"0284": "Meidän maailmamme!",
"0285": "Vanhoja muistoja",
"0286": "Vin tarvitsee apua",
"0287": "Tavataan Vin",
"0288": "Hyviä ja huonoja uutisia",
"0289": "Suuri murskaus",
"028a": "Meillä on yhä ongelmia",
"028b": "Kaivot posahtavat!",
"028c": "Tavataan Kor ja lapsi",
"028d": "Vastarinta",
"028e": "Villikissa-tehtävä",
"028f": "Tornin toimitus",
"0290": "Tavataan Krew ja Sig",
"0291": "Yllätyshyökkäys!",
"0292": "Ashelin on huolissaan",
"0293": "Vin, kamu. Tarvitsemme palveluksen.",
"0294": "Kuka on verhojen takana?",
"0295": "Tiedonjyvä",
"0296": "Iloinen jälleennäkeminen",
"0297": "Kissatappelu",
"0298": "Jak poistuu paikalta",
"0299": "Daxterin suuri voitto",
"029a": "Keira paljastaa suunnitelmansa",
"029b": "Suuri kilpailu",
"029c": "Voittajan piiri",
"029d": "Tavataan Oraakkeli",
"029e": "Jak saa pimeän voiman",
"029f": "Jak saa toisen pimeän voiman",
"02a0": "Jak saa kolmannen pimeän voiman",
"02a1": "Jak saa neljännen pimeän voiman",
"02a2": "Valitus, naiset, ja laulu",
"02a3": "Rahankerääjät",
"02a4": "Mikä tämän lapsen tarina on?",
"02a5": "Krewilla on uusi keikka",
"02a6": "Tavataan Brutter",
"02a7": "Krewin kilpailusopimus",
"02a8": "Mitä te kaksi oikein teitte?",
"02a9": "Oninin haaste",
"02aa": "Marin sinetti on valmis",
"02ab": "Pelasta lisää vaanijoita",
"02ac": "Suojele piilopaikkaa",
"02ad": "Krew uhkailee",
"02ae": "Pelaaja astelee esiin",
"02af": "Saadaan aikakartta",
"02b0": "Brutter pelastaa päivän",
"02b1": "Leijutaan pois",
"02b2": "Saadaan keltainen ase",
"02b3": "Saadaan JET-lauta",
"02b4": "Jak laittaa JET-laudan pois",
"02b5": "Ihka oma Rauhantekijämme!",
"02b6": "Ulosmeno",
"02b7": "Jak matkaa pesään",
"02b8": "Paluu",
"02b9": "Ashelin kuljettaa Jakin",
"02ba": "Pala sinettiä",
"02bb": "Erol inhoaa häviötä",
"02bc": "Torn ja ammusvarasto",
"02bd": "Metallipäitä kaupungissa?",
"02be": "Nyt räjähtää!",
"02bf": "Torn myöntää petoksensa",
"02c0": "Aikakaksoset",
"02c1": "Krewin viemärikeikka",
"02c2": "Mikä on tuo löyhkä?",
"02c3": "Taiteenkeräilijä",
"02c4": "Että kaipaankin housuja!",
"02c5": "Sig paljastaa sielunsa",
"02c6": "Patsaanposauttelua",
"02c7": "Se on ansa!",
"02c8": "Daxter saa suupalan",
"02c9": "Daxter matkustaa putkea",
"02ca": "Työtarjous",
"02cb": "Torn tarjoaa tehtävän",
"02cc": "Tavataan Ashelin",
"02cd": "Daxter tekee siirtonsa",
"02ce": "Tästä tulee hauskaa!",
"02cf": "Boom, beibi!",
"02d0": "Peukut alas",
"02d1": "Heihei",
"02d2": "Temppu",
"02d3": "Hyvää yötä",
"02d4": "Toimit hienosti, tulokas",
"02d5": "Vin tahtoo munakokkelia",
"02d6": "Kor on huolissaan",
"02d7": "Poika on kadoksissa",
"02d8": "Ohjaustorni räjähtää",
"02d9": "Tavataan Onin ja Pecker",
"02da": "Vekotin romahtaa",
"02db": "Hyvät fiilikset",
"02dc": "Daxter kärventyy",
"02dd": "Salatapaaminen",
"02de": "Paroni kohtaa Jakin",
"02df": "Minä tuhoan sinut!",
"02e0": "Takaisin mistä aloitimme",
"02e1": "Salaisuus pommin rakentamiseen",
"02e2": "Krewin kaunotar",
"02e3": "Oi voi...",
"02e4": "Lentoon",
"02e5": "Laskeutumisalusta",
"02e6": "Jak löytää Marin haudan!",
"02e7": "tomb-open-door-res",
"02e8": "tomb-open-door-2-res",
"02e9": "Haudan ovet",
"02ea": "Ensimmäinen säde",
"02eb": "Toinen säde",
"02ec": "Ovet avautuvat",
"02ed": "Edeltäjäkivi!",
"02ee": "Paroni varastaa show'n",
"02ef": "Daxter ojentaa auttavan kätensä",
"02f0": "Daxterin karvainen takaa-ajo",
"02f1": "Tavataan Varjo",
"02f2": "Daxterin pöhkötarina",
"02f3": "Pecker haastaa riitaa",
"02f4": "Samos ja siemen",
"02f5": "Näky",
"02f6": "Kor ja lapsi",
"02f7": "Pelasta lisää vaanijoita",
"02f8": "Marin haudan legenda",
"02f9": "Se on kirous, eikö vain?",
"02fa": "Pora romahtaa",
"02fb": "Valotorni nousee",
"02fc": "Sinetti avaa portin",
"02fd": "Sig on ongelmissa",
"02fe": "Juokse!",
"02ff": "Sigin vahva käsi",
"0300": "Sieltä se taas tulee!",
"0301": "Sigin viimeinen hetki",
"0302": "Kivi... ase... pesä!",
"0303": "Jak matkaa pesään",
"0304": "Poistutaan pesästä",
"0305": "Kor hyppää esiin",
"0306": "Etkö tunnista häntä?",
"0307": "Outo uusi maailma",
"0308": "Juhlinta",
"0309": "<ICON_MISSION_COMPLETE>",
"030a": "<ICON_MISSION_TODO>",
"030b": "<FLAG_UK> English",
"030c": "Ota tekstitys käyttöön painamalla <PAD_SQUARE>",
"030d": "Tekstitys käytössä",
"030e": "Tekstitys ei käytössä",
"030f": "JET-lauta",
"0310": "Jatka"
}
@@ -232,5 +232,6 @@
"1336": "Fast Progress Menu",
"1337": "PS2 LOD Distance",
"1338": "Off",
"1339": "Display"
"1339": "Display",
"133a": "Suomi"
}
@@ -5,7 +5,7 @@
"1101": "Pelataan bonustehtävää",
"1102": "Haulikon harjoitusrata",
"1103": "Läjäyttimen harjoitusrata",
"1104": "Vulkanusraivon harjoitusrata",
"1104": "Vulkanus-tykin harjoitusrata",
"1105": "Rauhantekijän harjoitusrata",
"1106": "Oninin haaste",
"1107": "Metallipää-mäiskintä",
@@ -18,8 +18,8 @@
"1206": "4x3 (PS2)",
"1207": "16x9 (PS2)",
"1208": "Valitse leveys- ja korkeussuhde",
"1209": "Paina <PAD_X> vahvistaaksesi ja poistuaksesi.",
"120a": "Paina <PAD_CIRCLE> vahvistaaksesi ja poistuaksesi.",
"1209": "Vahvista ja poistu painamalla <PAD_X>.",
"120a": "Vahvista ja poistu painamalla <PAD_CIRCLE>.",
"120b": "Kuvasuhde ei saa olla pienempi kuin 4x3!",
"120c": "Musiikkisoitin",
"120d": "Pystytahdistus",
@@ -34,7 +34,7 @@
"1216": "Muuntoase",
"1217": "JET-lauta",
"1218": "Titaaniasu",
"1219": "Pimeä Jak",
"1219": "Raivo-Jak",
"121a": "Ajoneuvo",
"121b": "Vaara",
"121c": "Ei mikään",
@@ -85,10 +85,10 @@
"124a": "Ohjaimen LED-asetukset",
"124b": "Näytä pelaajan tila",
"124c": "Näytä terveyden määrä",
"124d": "Lopeta auto-tallennus",
"124e": "Auto-tallennus pois päältä",
"124d": "Lopeta autom. tallennus",
"124e": "Autom. tallennus pois",
"124f": "Haluatko varmasti lopettaa automaattisen tallentamisen?",
"1250": "Turbo JET-lauta Haven Cityssä",
"1250": "Turbo-JET-lauta Haven Cityssä",
"1251": "Vihollisten terveyspalkit",
"1252": "Ajoneuvojen terveyspalkit",
"1253": "Ajoneuvojen haavoittumattomuus",
@@ -96,13 +96,13 @@
"1255": "Kerää kerättävät automaattisesti",
"1256": "Musiikkisoitin",
"1257": "Tekstuuriton tila",
"1258": "Nopeat välianimaatiot",
"1259": "Hitaat välianimaatiot",
"1258": "Nopeat välinäytökset",
"1259": "Hitaat välinäytökset",
"125a": "Nopea pelattavuus",
"125b": "Hidas pelattavuus",
"125c": "Pikamatkustus Haven Cityssä",
"125d": "Edeltäjähelmien seuranta",
"125e": "Todellinen vuorokauden aika",
"125e": "Todellinen vuorokaudenaika",
"125f": "Tyyni Haven City",
"1260": "Näytä JET-laudan temput",
"1261": "Huono sää",
@@ -112,14 +112,14 @@
"1265": "Käännä kaikki 3 venttiiliä ja löydä Marin patsas tehtävässä \"~S\" alle ~D sekunnissa",
"1266": "Vähennä Haven Cityn asukaslukua ~D kappaleella",
"1267": "Vähennä Haven Cityn ajoneuvojen määrää ~D kappaleella",
"1268": "Metsästä jokaista Metallipäälajia",
"1268": "Metsästä jokaista metallipää-lajia",
"1269": "Käytä JET-lautaa rikkoaksesi säiliöt auki",
"126a": "Siirrä repeämäkuljetin jättäen Samos ja nuori Samos vaurioitta",
"1280": "Kuvakulma-asetukset",
"1281": "Vasen/oikea (ensimmäinen persoona)",
"1282": "Ylös/alas (ensimmäinen persoona)",
"1283": "Vasen/oikea (kolmas persoona)",
"1284": "Ylös/alas (kolmas persoona)",
"1281": "Vasen/oikea (1. persoona)",
"1282": "Ylös/alas (1. persoona)",
"1283": "Vasen/oikea (3. persoona)",
"1284": "Ylös/alas (3. persoona)",
"1285": "Palauta oletukset",
"1286": "Normaali",
"1287": "Käänteinen",
@@ -137,15 +137,15 @@
"1293": "Ohjainasetukset",
"1294": "Valitse ohjain",
"1295": "Analogisauvan katvealue",
"1296": "Älä huomioi, kun ikkuna ei aktiivinen",
"1296": "Älä huomioi työpöydällä",
"1297": "Ohjaimen LED terveydelle",
"1298": "Ohjaimen LED muihin tiloihin",
"1299": "Salli näppäimistö",
"129a": "Salli hiiri",
"129b": "Hiiriasetukset",
"129c": "Kameran ohjaus hiirellä",
"129d": "Vaakasuuntainen herkkyys",
"129e": "Pystysuuntainen herkkyys",
"129d": "Herkkyys (vaakasuunta)",
"129e": "Herkkyys (pystysuunta)",
"129f": "Pelaajan ohjaus hiirellä",
"12a0": "Objektien varjot",
"12a1": "Normaali",
@@ -156,25 +156,25 @@
"12a6": "Muut tilastot",
"12a7": "Tappoja yhteensä",
"12a8": "Jakin tappamia",
"12a9": "Pimeän Jakin tappamia",
"12a9": "Raivo-Jakin tappamia",
"12aa": "Daxterin tappamia",
"12ab": "Muiden tappamia",
"12ac": "Läjäytin",
"12ad": "Haulikko",
"12ae": "Vulkanusraivo",
"12ae": "Vulkanus-tykki",
"12af": "Rauhantekijä",
"12b0": "JET-lauta",
"12b1": "JET-lautatemppu",
"12b1": "JET-lauta-temppu",
"12b2": "Titaaniasun isku",
"12b3": "Titaaniasun runko",
"12b4": "Tykki",
"12b5": "Isku",
"12b6": "Lyyhistyi",
"12b6": "Maaisku",
"12b7": "Kohokoukku",
"12b8": "Pyörähdys",
"12b9": "Kierintä",
"12bf": "Pimeä pommi",
"12c0": "Pimeä purkaus",
"12bf": "Raivopommi",
"12c0": "Raivopurkaus",
"12c3": "Karmiinivartija",
"12c4": "Sig",
"12c5": "Ashelin",
@@ -208,24 +208,24 @@
"1315": "Tämä asetus on erittäin kokeellinen. Kun peliä ei pelata 60 Hz:n kuvataajuudella, visuaalisia sekä pelattavuuden ongelmia voi ilmaantua sen seurauksena.",
"1316": "Paikallinen tulos",
"1320": "Ei mitään",
"1321": "Spin",
"1322": "Buusti",
"1321": "Pyörähdys",
"1322": "Nopeuslisä",
"1323": "Voltti",
"1324": "Kaideliuku",
"1325": "Kaidehyppy",
"1326": "Nosegrab",
"1327": "Method",
"1328": "Board Spin",
"1329": "Board Flip",
"1328": "Lautapyörähdys",
"1329": "Lautavoltti",
"132a": "Noseflip",
"132b": "Kickflip",
"132c": "Hyppy",
"132d": "Kyykkyhyppy",
"132e": "Pikahyppy",
"132f": "Pimeä Jak",
"1330": "Pimeä pommi",
"1331": "Pimeä purkaus",
"1332": "Pimeä jätti",
"132f": "Raivo-Jak",
"1330": "Raivopommi",
"1331": "Raivopurkaus",
"1332": "Raivojätti",
"1333": "PS2-asetukset",
"1334": "Objektien vähennys",
"1335": "Pakota ympäristökartoitus",
+9 -4
View File
@@ -484,7 +484,7 @@
03000000f0250000c183000000000000,PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d9040000160f000000000000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000d620000011a7000000000000,PowerA Core Plus GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -873,6 +873,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:MacOSX
03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,
030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
@@ -1080,7 +1081,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,
030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b10,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,
030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -1090,6 +1091,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -1388,6 +1390,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b000001050000,Microsoft Xbox Series X Controller,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Linux,
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
@@ -1465,7 +1468,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d620000011a7000011010000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1687,6 +1690,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050082795e040000e002000003090000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1694,7 +1698,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-16
View File
@@ -31,19 +31,3 @@ struct RPC_Dgo_Cmd {
// a buffer.
uint8_t pad[32];
};
namespace jak3 {
struct RPC_Dgo_Cmd {
uint16_t rsvd;
uint16_t result;
uint32_t buffer1;
uint32_t buffer2;
uint32_t buffer_heap_top;
char name[16];
uint16_t cgo_id;
uint8_t pad[30];
};
static_assert(sizeof(RPC_Dgo_Cmd) == 0x40);
} // namespace jak3
static_assert(sizeof(RPC_Dgo_Cmd) == sizeof(jak3::RPC_Dgo_Cmd));
@@ -3,6 +3,10 @@
#include "fmt/core.h"
#include "third-party/imgui/imgui.h"
std::string BucketRenderer::name() const {
return m_name;
}
std::string BucketRenderer::name_and_id() const {
return fmt::format("[{:2d}] {}", (int)m_my_id, m_name);
}
@@ -26,8 +26,8 @@ class EyeRenderer;
struct SharedRenderState {
explicit SharedRenderState(std::shared_ptr<TexturePool> _texture_pool,
std::shared_ptr<Loader> _loader,
GameVersion version)
: shaders(version), texture_pool(_texture_pool), loader(_loader) {}
GameVersion _version)
: shaders(_version), texture_pool(_texture_pool), loader(_loader), version(_version) {}
ShaderLibrary shaders;
std::shared_ptr<TexturePool> texture_pool;
std::shared_ptr<Loader> loader;
@@ -98,6 +98,7 @@ class BucketRenderer {
virtual void render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) = 0;
std::string name() const;
std::string name_and_id() const;
virtual ~BucketRenderer() = default;
bool& enabled() { return m_enabled; }
@@ -158,4 +159,4 @@ class PrintRenderer : public BucketRenderer {
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
bool empty() const override { return true; }
void draw_debug_window() override {}
};
};
@@ -579,6 +579,7 @@ u32 get_direct_qwc_or_nop(const VifCode& code) {
return code.immediate;
}
default:
printf("expected direct, got %s\n", code.print().c_str());
ASSERT(false);
}
}
+27 -7
View File
@@ -186,7 +186,7 @@ EyeRenderer::SpriteInfo decode_sprite(const DmaTransfer& dma) {
memcpy(&result.a, dma.data + 16 + 12, 1); // a
// uv0
memcpy(&result.uv0[0], &dma.data[32], 8);
memcpy(&result.uv0, &dma.data[32], 8);
// xyz0
memcpy(&result.xyz0[0], &dma.data[48], 12);
@@ -236,8 +236,11 @@ std::vector<EyeRenderer::SingleEyeDraws> EyeRenderer::get_draws(DmaFollower& dma
bool using_64 = false;
{
auto draw0 = read_eye_draw(dma);
ASSERT(draw0.sprite.uv0[0] == 0);
ASSERT(draw0.sprite.uv0[1] == 0);
// ASSERT(draw0.sprite.uv0[0] == 0);
// ASSERT(draw0.sprite.uv0[1] == 0);
// printf("hashed name is 0x%x 0x%x\n", draw0.sprite.uv0[0], draw0.sprite.uv0[1]);
l_draw.fnv_name_hash = draw0.sprite.uv0;
r_draw.fnv_name_hash = draw0.sprite.uv0;
ASSERT(draw0.sprite.uv1[0] == 0);
ASSERT(draw0.sprite.uv1[1] == 0);
if (draw0.scissor.y1 - draw0.scissor.y0 == 63) {
@@ -509,7 +512,9 @@ void EyeRenderer::run_gpu(const std::vector<SingleEyeDraws>& draws,
buffer_idx = 0;
for (size_t draw_idx = 0; draw_idx < draws.size(); draw_idx++) {
const auto& draw = draws[draw_idx];
const auto& out_tex = m_gpu_eye_textures[draw.tex_slot()];
auto& out_tex = m_gpu_eye_textures[draw.tex_slot()];
out_tex.fnv_name_hash = draw.fnv_name_hash;
out_tex.lr = draw.lr;
// first, the clear
float clear[4] = {0, 0, 0, 0};
@@ -574,15 +579,30 @@ std::optional<u64> EyeRenderer::lookup_eye_texture(u8 eye_id) {
}
}
std::optional<u64> EyeRenderer::lookup_eye_texture_hash(u64 hash, bool lr) {
for (auto& slot : m_gpu_eye_textures) {
if (slot.fnv_name_hash == hash && slot.lr == lr) {
auto* gpu_tex = slot.gpu_tex;
if (gpu_tex) {
return gpu_tex->gpu_textures.at(0).gl;
} else {
fmt::print("lookup eye failed for {} (1)\n", hash);
return {};
}
}
}
fmt::print("lookup eye failed for {} (2)\n", hash);
return {};
}
//////////////////////
// DMA Decode
//////////////////////
std::string EyeRenderer::SpriteInfo::print() const {
std::string result;
result +=
fmt::format("a: {:x} uv: ({}, {}), ({}, {}) xyz: ({}, {}, {}), ({}, {}, {})", a, uv0[0],
uv0[1], uv1[0], uv1[1], xyz0[0], xyz0[1], xyz0[2], xyz1[0], xyz1[1], xyz1[2]);
result += fmt::format("a: {:x} uv: ({}), ({}, {}) xyz: ({}, {}, {}), ({}, {}, {})", a, uv0,
uv1[0], uv1[1], xyz0[0], xyz0[1], xyz0[2], xyz1[0], xyz1[1], xyz1[2]);
return result;
}
+5 -1
View File
@@ -22,10 +22,11 @@ class EyeRenderer : public BucketRenderer {
void handle_eye_dma2(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
std::optional<u64> lookup_eye_texture(u8 eye_id);
std::optional<u64> lookup_eye_texture_hash(u64 hash, bool lr);
struct SpriteInfo {
u8 a;
u32 uv0[2];
u64 uv0; // stores hashed name of merc-ctrl that reads this eye.
u32 uv1[2];
u32 xyz0[3];
u32 xyz1[3];
@@ -53,6 +54,8 @@ class EyeRenderer : public BucketRenderer {
GpuTexture* gpu_tex = nullptr;
u32 tbp;
FramebufferTexturePair fb;
u64 fnv_name_hash = 0;
bool lr = false;
// note: eye texture increased to 128x128 (originally 32x32) here.
GpuEyeTex() : fb(128, 128, GL_UNSIGNED_INT_8_8_8_8_REV) {}
@@ -65,6 +68,7 @@ class EyeRenderer : public BucketRenderer {
GLuint m_gl_vertex_buffer;
struct SingleEyeDraws {
u64 fnv_name_hash = 0;
int lr;
int pair;
bool using_64 = false;
@@ -28,6 +28,7 @@
#include "game/graphics/pipelines/opengl.h"
#include "third-party/imgui/imgui.h"
#include "third-party/imgui/imgui_stdlib.h"
// for the vif callback
#include "game/kernel/common/kmachine.h"
@@ -36,6 +37,7 @@
#ifdef _WIN32
#include <Windows.h>
#endif
#include "common/util/string_util.h"
namespace {
std::string g_current_renderer;
@@ -84,7 +86,11 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool,
&gl_error_ignores_api_other[0], GL_FALSE);
#endif
lg::debug("OpenGL context information: {}", (const char*)glGetString(GL_VERSION));
lg::info("OpenGL context version: {}", (const char*)glGetString(GL_VERSION));
lg::info("OpenGL context renderer: {}", (const char*)glGetString(GL_RENDERER));
lg::info("OpenGL context vendor: {}", (const char*)glGetString(GL_VENDOR));
lg::info("OpenGL context shading language version: {}",
(const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
const tfrag3::Level* common_level = nullptr;
{
@@ -263,6 +269,11 @@ void OpenGLRenderer::init_bucket_renderers_jak3() {
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_PRIS, BucketId::GMERC_L1_PRIS, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc2-l{}-pris", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC2_L0_PRIS, BucketId::GMERC2_L1_PRIS, i), m_generic2,
Generic2::Mode::PRIM);
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-pris2", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS2, BucketId::MERC_L1_PRIS2, i), m_merc2);
@@ -270,6 +281,10 @@ void OpenGLRenderer::init_bucket_renderers_jak3() {
fmt::format("gmerc-l{}-pris2", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_PRIS2, BucketId::GMERC_L1_PRIS2, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc2-l{}-pris2", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC2_L0_PRIS2, BucketId::GMERC2_L1_PRIS2, i),
m_generic2, Generic2::Mode::PRIM);
}
// 401
@@ -281,6 +296,10 @@ void OpenGLRenderer::init_bucket_renderers_jak3() {
BucketId::GMERC_LCOM_PRIS, m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<Generic2BucketRenderer>("gmerc2-lcom-pris", BucketCategory::GENERIC,
BucketId::GMERC2_LCOM_PRIS, m_generic2,
Generic2::Mode::PRIM);
// 461
init_bucket_renderer<TextureUploadHandler>("tex-lcom-sky-post", BucketCategory::TEX,
BucketId::TEX_LCOM_SKY_POST, m_texture_animator);
@@ -306,6 +325,10 @@ void OpenGLRenderer::init_bucket_renderers_jak3() {
fmt::format("tfrag-l{}-water", i), BucketCategory::TFRAG,
GET_BUCKET_ID_FOR_LIST(BucketId::TFRAG_L0_WATER, BucketId::TFRAG_L1_WATER, i),
std::vector{tfrag3::TFragmentTreeKind::WATER}, false, i, anim_slot_array());
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc2-l{}-water", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC2_L0_WATER, BucketId::GMERC2_L1_WATER, i),
m_generic2, Generic2::Mode::PRIM);
}
// 563
@@ -313,11 +336,20 @@ void OpenGLRenderer::init_bucket_renderers_jak3() {
BucketId::TEX_LCOM_WATER, m_texture_animator);
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-water", BucketCategory::MERC,
BucketId::MERC_LCOM_WATER, m_merc2);
init_bucket_renderer<Generic2BucketRenderer>("generic2-lcom-water", BucketCategory::GENERIC,
BucketId::GMERC2_LCOM_WATER, m_generic2,
Generic2::Mode::PRIM);
// 568
init_bucket_renderer<TextureUploadHandler>("tex-sprite", BucketCategory::TEX,
BucketId::TEX_SPRITE, m_texture_animator);
init_bucket_renderer<Generic2BucketRenderer>("generic-sprite-1", BucketCategory::GENERIC,
BucketId::GENERIC_SPRITE_1, m_generic2,
Generic2::Mode::PRIM);
init_bucket_renderer<Sprite3>("particles", BucketCategory::SPRITE, BucketId::PARTICLES);
init_bucket_renderer<Generic2BucketRenderer>("generic-sprite-2", BucketCategory::GENERIC,
BucketId::GENERIC_SPRITE_2, m_generic2,
Generic2::Mode::PRIM);
init_bucket_renderer<Generic2BucketRenderer>("generic-sprite-3", BucketCategory::OTHER,
BucketId::GENERIC_SPRITE_3, m_generic2,
Generic2::Mode::LIGHTNING);
@@ -1078,8 +1110,13 @@ void OpenGLRenderer::draw_renderer_selection_window() {
ImGui::TreePop();
}
ImGui::InputText("Renderer Filter", &m_renderer_filter);
for (size_t i = 0; i < m_bucket_renderers.size(); i++) {
auto renderer = m_bucket_renderers[i].get();
if (!m_renderer_filter.empty() && !str_util::contains(renderer->name(), m_renderer_filter)) {
continue;
}
if (renderer && !renderer->empty()) {
ImGui::PushID(i);
if (ImGui::TreeNode(renderer->name_and_id().c_str())) {
@@ -123,6 +123,7 @@ class OpenGLRenderer {
float m_last_pmode_alp = 1.;
bool m_enable_fast_blackout_loads = true;
std::string m_renderer_filter = "";
struct FboState {
struct {
+612 -126
View File
@@ -60,23 +60,48 @@
* A simple list of preallocated textures by size. If a texture needs to be resized, it's faster
* to swap to a different OpenGL texture from this pool than glTexImage2D with a different size.
*/
OpenGLTexturePool::OpenGLTexturePool() {
OpenGLTexturePool::OpenGLTexturePool(GameVersion version) {
struct Alloc {
u64 w, h, n;
};
// list of sizes to preallocate: {width, height, count}.
for (const auto& a : std::vector<Alloc>{{4, 4, 2},
{4, 64, 2},
{16, 16, 5},
{32, 16, 1},
{32, 32, 10},
{32, 64, 1},
{64, 32, 6},
{64, 64, 30},
{64, 128, 4},
{128, 128, 10},
{256, 1, 2},
{256, 256, 7}}) {
PerGameVersion<std::vector<Alloc>> tex_allocs{{{4, 4, 2},
{4, 64, 2},
{16, 16, 5},
{32, 16, 1},
{32, 32, 10},
{32, 64, 1},
{64, 32, 6},
{64, 64, 30},
{64, 128, 4},
{128, 128, 10},
{256, 1, 2},
{256, 256, 7}},
{{4, 4, 2},
{4, 64, 2},
{16, 16, 5},
{32, 16, 1},
{32, 32, 10},
{32, 64, 1},
{64, 32, 6},
{64, 64, 30},
{64, 128, 4},
{128, 128, 10},
{256, 1, 2},
{256, 256, 7}},
{{4, 4, 3},
{4, 64, 6},
{16, 16, 5},
{32, 16, 1},
{32, 32, 20},
{32, 64, 1},
{64, 32, 15},
{64, 64, 85},
{64, 128, 4},
{128, 128, 185},
{256, 1, 2},
{256, 256, 7}}};
for (const auto& a : tex_allocs[version]) {
auto& l = textures[(a.w << 32) | a.h];
l.resize(a.n);
glGenTextures(a.n, l.data());
@@ -370,6 +395,7 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders,
const tfrag3::Level* common_level,
GameVersion version)
: m_common_level(common_level),
m_opengl_texture_pool(version),
m_psm32_to_psm8_8_8(8, 8, 8, 64),
m_psm32_to_psm8_16_16(16, 16, 16, 64),
m_psm32_to_psm8_32_32(32, 32, 16, 64),
@@ -420,6 +446,7 @@ TextureAnimator::TextureAnimator(ShaderLibrary& shaders,
m_shader_id = shader.id();
m_uniforms.rgba = glGetUniformLocation(shader.id(), "rgba");
m_uniforms.enable_tex = glGetUniformLocation(shader.id(), "enable_tex");
m_uniforms.set_alpha = glGetUniformLocation(shader.id(), "set_alpha");
m_uniforms.positions = glGetUniformLocation(shader.id(), "positions");
m_uniforms.uvs = glGetUniformLocation(shader.id(), "uvs");
m_uniforms.channel_scramble = glGetUniformLocation(shader.id(), "channel_scramble");
@@ -642,7 +669,7 @@ GLuint TextureAnimator::get_by_slot(int idx) {
}
// IDs sent from GOAL telling us what texture operation to perform.
enum PcTextureAnimCodes {
enum class PcTextureAnimCodesJak2 : u16 {
FINISH_ARRAY = 13,
ERASE_DEST_TEXTURE = 14,
UPLOAD_CLUT_16_16 = 15,
@@ -673,6 +700,361 @@ enum PcTextureAnimCodes {
CLOUDS_HIRES = 43,
};
struct FixedAnimInfoJak2 {
PcTextureAnimCodesJak2 code = (PcTextureAnimCodesJak2)0;
std::string name;
int anim_array_idx = -1;
};
FixedAnimInfoJak2 anim_code_to_info(PcTextureAnimCodesJak2 code, const TextureAnimator& animator) {
ASSERT(code > (PcTextureAnimCodesJak2)0);
FixedAnimInfoJak2 anim;
anim.code = code;
switch (code) {
case PcTextureAnimCodesJak2::SKULL_GEM: {
anim.name = "skull-gem";
anim.anim_array_idx = animator.m_skull_gem_fixed_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::BOMB: {
anim.name = "bomb";
anim.anim_array_idx = animator.m_bomb_fixed_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::CAS_CONVEYOR: {
anim.name = "cas-conveyor";
anim.anim_array_idx = animator.m_cas_conveyor_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::SECURITY: {
anim.name = "security";
anim.anim_array_idx = animator.m_security_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::WATERFALL: {
anim.name = "waterfall";
anim.anim_array_idx = animator.m_waterfall_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::WATERFALL_B: {
anim.name = "waterfall-b";
anim.anim_array_idx = animator.m_waterfall_b_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::LAVA: {
anim.name = "lava";
anim.anim_array_idx = animator.m_lava_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::LAVA_B: {
anim.name = "lava-b";
anim.anim_array_idx = animator.m_lava_b_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::STADIUMB: {
anim.name = "stadiumb";
anim.anim_array_idx = animator.m_stadiumb_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::FORTRESS_PRIS: {
anim.name = "fort-pris";
anim.anim_array_idx = animator.m_fortress_pris_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::FORTRESS_WARP: {
anim.name = "fort-warp";
anim.anim_array_idx = animator.m_fortress_warp_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::METKOR: {
anim.name = "metkor";
anim.anim_array_idx = animator.m_metkor_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::SHIELD: {
anim.name = "shield";
anim.anim_array_idx = animator.m_shield_anim_array_idx;
} break;
case PcTextureAnimCodesJak2::KREW_HOLO: {
anim.name = "krew-holo";
anim.anim_array_idx = animator.m_krew_holo_anim_array_idx;
} break;
default:
anim.name = "unknown";
lg::error("Unknown texture anim code {}", (int)code);
}
return anim;
}
enum class PcTextureAnimCodesJak3 : u16 {
FINISH_ARRAY = 13,
ERASE_DEST_TEXTURE = 14,
UPLOAD_CLUT_16_16 = 15,
GENERIC_UPLOAD = 16,
SET_SHADER = 17,
DRAW = 18,
DARKJAK = 22,
DARKJAK_HIGHRES = 23,
SKULL_GEM = 27,
DEFAULT_WATER = 28,
DEFAULT_WARP = 29,
TEMPLEA_WATER = 30,
TEMPLEA_WARP = 31,
TEMPLEB_WARP = 32,
TEMPLEC_WATER = 33,
SEWC_WATER = 34,
SEWD_WATER = 35,
SEWE_WATER = 36,
SEWG_WATER = 37,
SEWH_WATER = 38,
SEWI_WATER = 39,
SEWJ_WATER = 40,
SEWL_WATER = 41,
SEWM_WATER = 42,
SEWN_WATER = 43,
DESRESC_WARP = 44,
CTYSLUMB_WATER = 45,
NSTB_QUICKSAND = 46,
CTYSLUMC_WATER = 47,
FACTORYC_ALPHA = 48,
HFRAG = 49,
HANGA_SPRITE = 50,
HANGA_WATER = 51,
DESERTD_WATER = 52,
LMHCITYB_TFRAG = 53,
TOWERB_WATER = 54,
COMB_FIELD = 55,
WASSTADA_ALPHA = 56,
FACTORYB_WATER = 57,
LMHCITYA_TFRAG = 58,
MHCITYA_PRIS = 59,
RUBBLEA_WATER = 60,
RUBBLEA2_WATER = 61,
RUBBLEB_WATER = 62,
RUBBLEC_WATER = 63,
FORESTA_WATER = 64,
FORESTB_WATER = 65,
LFORPLNT_PRIS = 66,
LTNFXHIP = 67,
LGUNNORM_WATER = 68,
LJKDXVIN = 69,
SECURITY = 70,
WASPAL_WATER = 71,
MINED_TFRAG = 72,
VOLCANOX_WARP = 73,
TEMPLEX_WATER = 74,
VOLCANOA_WATER = 75,
DESHOVER = 76,
CLOUDS_AND_FOG = 77,
CLOUDS_HIRES = 78,
};
struct FixedAnimInfoJak3 {
PcTextureAnimCodesJak3 code = (PcTextureAnimCodesJak3)0;
std::string name;
int anim_array_idx = -1;
};
FixedAnimInfoJak3 anim_code_to_info(PcTextureAnimCodesJak3 code, const TextureAnimator& animator) {
ASSERT(code > (PcTextureAnimCodesJak3)0);
FixedAnimInfoJak3 anim;
anim.code = code;
switch (code) {
case PcTextureAnimCodesJak3::SKULL_GEM: {
anim.name = "skull-gem";
anim.anim_array_idx = animator.m_skull_gem_fixed_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::DEFAULT_WATER: {
anim.name = "default-water";
anim.anim_array_idx = animator.m_default_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::DEFAULT_WARP: {
anim.name = "default-warp";
anim.anim_array_idx = animator.m_default_warp_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::TEMPLEA_WATER: {
anim.name = "templea-water";
anim.anim_array_idx = animator.m_templea_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::TEMPLEA_WARP: {
anim.name = "templea-warp";
anim.anim_array_idx = animator.m_templea_warp_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::TEMPLEB_WARP: {
anim.name = "templeb-warp";
anim.anim_array_idx = animator.m_templeb_warp_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::TEMPLEC_WATER: {
anim.name = "templec-water";
anim.anim_array_idx = animator.m_templec_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWC_WATER: {
anim.name = "sewc-water";
anim.anim_array_idx = animator.m_sewc_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWD_WATER: {
anim.name = "sewd-water";
anim.anim_array_idx = animator.m_sewd_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWE_WATER: {
anim.name = "sewe-water";
anim.anim_array_idx = animator.m_sewe_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWG_WATER: {
anim.name = "sewg-water";
anim.anim_array_idx = animator.m_sewg_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWH_WATER: {
anim.name = "sewh-water";
anim.anim_array_idx = animator.m_sewh_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWI_WATER: {
anim.name = "sewi-water";
anim.anim_array_idx = animator.m_sewi_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWJ_WATER: {
anim.name = "sewj-water";
anim.anim_array_idx = animator.m_sewj_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWL_WATER: {
anim.name = "sewl-water";
anim.anim_array_idx = animator.m_sewl_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWM_WATER: {
anim.name = "sewm-water";
anim.anim_array_idx = animator.m_sewm_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SEWN_WATER: {
anim.name = "sewn-water";
anim.anim_array_idx = animator.m_sewn_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::DESRESC_WARP: {
anim.name = "desresc-warp";
anim.anim_array_idx = animator.m_desresc_warp_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::CTYSLUMB_WATER: {
anim.name = "ctyslumb-water";
anim.anim_array_idx = animator.m_ctyslumb_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::NSTB_QUICKSAND: {
anim.name = "nstb-quicksand";
anim.anim_array_idx = animator.m_nstb_quicksand_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::CTYSLUMC_WATER: {
anim.name = "ctyslumc-water";
anim.anim_array_idx = animator.m_ctyslumc_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::FACTORYC_ALPHA: {
anim.name = "factoryc-alpha";
anim.anim_array_idx = animator.m_factoryc_alpha_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::HFRAG: {
anim.name = "hfrag";
anim.anim_array_idx = animator.m_hfrag_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::HANGA_SPRITE: {
anim.name = "hanga-sprite";
anim.anim_array_idx = animator.m_hanga_sprite_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::HANGA_WATER: {
anim.name = "hanga-water";
anim.anim_array_idx = animator.m_hanga_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::DESERTD_WATER: {
anim.name = "desertd-water";
anim.anim_array_idx = animator.m_desertd_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::LMHCITYB_TFRAG: {
anim.name = "lmhcityb-tfrag";
anim.anim_array_idx = animator.m_lmhcityb_tfrag_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::TOWERB_WATER: {
anim.name = "towerb-water";
anim.anim_array_idx = animator.m_towerb_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::COMB_FIELD: {
anim.name = "comb-field";
anim.anim_array_idx = animator.m_comb_field_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::WASSTADA_ALPHA: {
anim.name = "wasstada-alpha";
anim.anim_array_idx = animator.m_wasstada_alpha_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::FACTORYB_WATER: {
anim.name = "factoryb-water";
anim.anim_array_idx = animator.m_factoryb_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::LMHCITYA_TFRAG: {
anim.name = "lmhcitya-tfrag";
anim.anim_array_idx = animator.m_lmhcitya_tfrag_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::MHCITYA_PRIS: {
anim.name = "mhcitya-pris";
anim.anim_array_idx = animator.m_mhcitya_pris_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::RUBBLEA_WATER: {
anim.name = "rubblea-water";
anim.anim_array_idx = animator.m_rubblea_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::RUBBLEA2_WATER: {
anim.name = "rubblea2-water";
anim.anim_array_idx = animator.m_rubblea2_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::RUBBLEB_WATER: {
anim.name = "rubbleb-water";
anim.anim_array_idx = animator.m_rubbleb_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::RUBBLEC_WATER: {
anim.name = "rubblec-water";
anim.anim_array_idx = animator.m_rubblec_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::FORESTA_WATER: {
anim.name = "foresta-water";
anim.anim_array_idx = animator.m_foresta_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::FORESTB_WATER: {
anim.name = "forestb-water";
anim.anim_array_idx = animator.m_forestb_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::LFORPLNT_PRIS: {
anim.name = "lforplnt-pris";
anim.anim_array_idx = animator.m_lforplnt_pris_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::LTNFXHIP: {
anim.name = "ltnfxhip";
anim.anim_array_idx = animator.m_ltnfxhip_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::LGUNNORM_WATER: {
anim.name = "lgunnorm-water";
anim.anim_array_idx = animator.m_lgunnorm_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::LJKDXVIN: {
anim.name = "ljkdxvin";
anim.anim_array_idx = animator.m_ljkdxvin_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::SECURITY: {
anim.name = "security";
anim.anim_array_idx = animator.m_security_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::WASPAL_WATER: {
anim.name = "waspal-water";
anim.anim_array_idx = animator.m_waspal_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::MINED_TFRAG: {
anim.name = "mined-tfrag";
anim.anim_array_idx = animator.m_mined_tfrag_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::VOLCANOX_WARP: {
anim.name = "volcanox-warp";
anim.anim_array_idx = animator.m_volcanox_warp_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::TEMPLEX_WATER: {
anim.name = "templex-water";
anim.anim_array_idx = animator.m_templex_water_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::VOLCANOA_WATER: {
anim.name = "volcanoa-water";
anim.anim_array_idx = animator.m_volcanoa_anim_array_idx;
} break;
case PcTextureAnimCodesJak3::DESHOVER: {
anim.name = "deshover";
anim.anim_array_idx = animator.m_deshover_anim_array_idx;
} break;
default:
anim.name = "unknown";
lg::error("Unknown texture anim code {}", (int)code);
}
return anim;
}
// metadata for an upload from GOAL memory
struct TextureAnimPcUpload {
u32 data; // goal pointer
@@ -726,117 +1108,191 @@ void TextureAnimator::handle_texture_anim_data(DmaFollower& dma,
auto tf = dma.read_and_advance();
auto vif0 = tf.vifcode0();
if (vif0.kind == VifCode::Kind::PC_PORT) {
switch (vif0.immediate) {
case UPLOAD_CLUT_16_16: {
auto p = scoped_prof("clut-16-16");
handle_upload_clut_16_16(tf, ee_mem);
} break;
case ERASE_DEST_TEXTURE: {
auto p = scoped_prof("erase");
handle_erase_dest(dma);
} break;
case GENERIC_UPLOAD: {
auto p = scoped_prof("generic-upload");
handle_generic_upload(tf, ee_mem);
} break;
case SET_SHADER: {
auto p = scoped_prof("set-shader");
handle_set_shader(dma);
} break;
case DRAW: {
auto p = scoped_prof("draw");
handle_draw(dma, *texture_pool);
} break;
case FINISH_ARRAY:
done = true;
switch (this->m_version) {
case GameVersion::Jak2:
switch (static_cast<PcTextureAnimCodesJak2>(vif0.immediate)) {
case PcTextureAnimCodesJak2::UPLOAD_CLUT_16_16: {
auto p = scoped_prof("clut-16-16");
handle_upload_clut_16_16(tf, ee_mem);
} break;
case PcTextureAnimCodesJak2::ERASE_DEST_TEXTURE: {
auto p = scoped_prof("erase");
handle_erase_dest(dma);
} break;
case PcTextureAnimCodesJak2::GENERIC_UPLOAD: {
auto p = scoped_prof("generic-upload");
handle_generic_upload(tf, ee_mem);
} break;
case PcTextureAnimCodesJak2::SET_SHADER: {
auto p = scoped_prof("set-shader");
handle_set_shader(dma);
} break;
case PcTextureAnimCodesJak2::DRAW: {
auto p = scoped_prof("draw");
handle_draw(dma, *texture_pool);
} break;
case PcTextureAnimCodesJak2::FINISH_ARRAY:
done = true;
break;
case PcTextureAnimCodesJak2::DARKJAK: {
auto p = scoped_prof("darkjak");
run_clut_blender_group(tf, m_darkjak_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak2::PRISON_JAK: {
auto p = scoped_prof("prisonjak");
run_clut_blender_group(tf, m_jakb_prison_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak2::ORACLE_JAK: {
auto p = scoped_prof("oraclejak");
run_clut_blender_group(tf, m_jakb_oracle_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak2::NEST_JAK: {
auto p = scoped_prof("nestjak");
run_clut_blender_group(tf, m_jakb_nest_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak2::KOR_TRANSFORM: {
auto p = scoped_prof("kor");
run_clut_blender_group(tf, m_kor_transform_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak2::SKULL_GEM:
case PcTextureAnimCodesJak2::BOMB:
case PcTextureAnimCodesJak2::CAS_CONVEYOR:
case PcTextureAnimCodesJak2::SECURITY:
case PcTextureAnimCodesJak2::WATERFALL:
case PcTextureAnimCodesJak2::WATERFALL_B:
case PcTextureAnimCodesJak2::LAVA:
case PcTextureAnimCodesJak2::LAVA_B:
case PcTextureAnimCodesJak2::STADIUMB:
case PcTextureAnimCodesJak2::FORTRESS_PRIS:
case PcTextureAnimCodesJak2::FORTRESS_WARP:
case PcTextureAnimCodesJak2::METKOR:
case PcTextureAnimCodesJak2::SHIELD:
case PcTextureAnimCodesJak2::KREW_HOLO: {
auto anim =
anim_code_to_info(static_cast<PcTextureAnimCodesJak2>(vif0.immediate), *this);
auto p = scoped_prof(anim.name.c_str());
run_fixed_animation_array(anim.anim_array_idx, tf, texture_pool);
break;
}
case PcTextureAnimCodesJak2::CLOUDS_AND_FOG:
case PcTextureAnimCodesJak2::CLOUDS_HIRES: {
auto p = scoped_prof("clouds-and-fog");
handle_clouds_and_fog(tf, texture_pool,
vif0.immediate == (u16)PcTextureAnimCodesJak2::CLOUDS_HIRES);
} break;
case PcTextureAnimCodesJak2::SLIME: {
auto p = scoped_prof("slime");
handle_slime(tf, texture_pool);
break;
}
default:
lg::print("bad imm: {}\n", vif0.immediate);
ASSERT_NOT_REACHED();
}
break;
case GameVersion::Jak3:
switch (static_cast<PcTextureAnimCodesJak3>(vif0.immediate)) {
case PcTextureAnimCodesJak3::UPLOAD_CLUT_16_16: {
auto p = scoped_prof("clut-16-16");
handle_upload_clut_16_16(tf, ee_mem);
} break;
case PcTextureAnimCodesJak3::ERASE_DEST_TEXTURE: {
auto p = scoped_prof("erase");
handle_erase_dest(dma);
} break;
case PcTextureAnimCodesJak3::GENERIC_UPLOAD: {
auto p = scoped_prof("generic-upload");
handle_generic_upload(tf, ee_mem);
} break;
case PcTextureAnimCodesJak3::SET_SHADER: {
auto p = scoped_prof("set-shader");
handle_set_shader(dma);
} break;
case PcTextureAnimCodesJak3::DRAW: {
auto p = scoped_prof("draw");
handle_draw(dma, *texture_pool);
} break;
case PcTextureAnimCodesJak3::FINISH_ARRAY:
done = true;
break;
case PcTextureAnimCodesJak3::DARKJAK: {
auto p = scoped_prof("darkjak");
run_clut_blender_group(tf, m_darkjak_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak3::DARKJAK_HIGHRES: {
auto p = scoped_prof("darkjak-highres");
run_clut_blender_group(tf, m_darkjak_highres_clut_blender_idx, frame_idx);
} break;
case PcTextureAnimCodesJak3::SKULL_GEM:
case PcTextureAnimCodesJak3::DEFAULT_WATER:
case PcTextureAnimCodesJak3::DEFAULT_WARP:
case PcTextureAnimCodesJak3::TEMPLEA_WATER:
case PcTextureAnimCodesJak3::TEMPLEA_WARP:
case PcTextureAnimCodesJak3::TEMPLEB_WARP:
case PcTextureAnimCodesJak3::TEMPLEC_WATER:
case PcTextureAnimCodesJak3::SEWC_WATER:
case PcTextureAnimCodesJak3::SEWD_WATER:
case PcTextureAnimCodesJak3::SEWE_WATER:
case PcTextureAnimCodesJak3::SEWG_WATER:
case PcTextureAnimCodesJak3::SEWH_WATER:
case PcTextureAnimCodesJak3::SEWI_WATER:
case PcTextureAnimCodesJak3::SEWJ_WATER:
case PcTextureAnimCodesJak3::SEWL_WATER:
case PcTextureAnimCodesJak3::SEWM_WATER:
case PcTextureAnimCodesJak3::SEWN_WATER:
case PcTextureAnimCodesJak3::DESRESC_WARP:
case PcTextureAnimCodesJak3::CTYSLUMB_WATER:
case PcTextureAnimCodesJak3::NSTB_QUICKSAND:
case PcTextureAnimCodesJak3::CTYSLUMC_WATER:
case PcTextureAnimCodesJak3::FACTORYC_ALPHA:
case PcTextureAnimCodesJak3::HFRAG:
case PcTextureAnimCodesJak3::HANGA_SPRITE:
case PcTextureAnimCodesJak3::HANGA_WATER:
case PcTextureAnimCodesJak3::DESERTD_WATER:
case PcTextureAnimCodesJak3::LMHCITYB_TFRAG:
case PcTextureAnimCodesJak3::TOWERB_WATER:
case PcTextureAnimCodesJak3::COMB_FIELD:
case PcTextureAnimCodesJak3::WASSTADA_ALPHA:
case PcTextureAnimCodesJak3::FACTORYB_WATER:
case PcTextureAnimCodesJak3::LMHCITYA_TFRAG:
case PcTextureAnimCodesJak3::MHCITYA_PRIS:
case PcTextureAnimCodesJak3::RUBBLEA_WATER:
case PcTextureAnimCodesJak3::RUBBLEA2_WATER:
case PcTextureAnimCodesJak3::RUBBLEB_WATER:
case PcTextureAnimCodesJak3::RUBBLEC_WATER:
case PcTextureAnimCodesJak3::FORESTA_WATER:
case PcTextureAnimCodesJak3::FORESTB_WATER:
case PcTextureAnimCodesJak3::LFORPLNT_PRIS:
case PcTextureAnimCodesJak3::LTNFXHIP:
case PcTextureAnimCodesJak3::LGUNNORM_WATER:
case PcTextureAnimCodesJak3::LJKDXVIN:
case PcTextureAnimCodesJak3::SECURITY:
case PcTextureAnimCodesJak3::WASPAL_WATER:
case PcTextureAnimCodesJak3::MINED_TFRAG:
case PcTextureAnimCodesJak3::VOLCANOX_WARP:
case PcTextureAnimCodesJak3::TEMPLEX_WATER:
case PcTextureAnimCodesJak3::VOLCANOA_WATER:
case PcTextureAnimCodesJak3::DESHOVER: {
auto anim =
anim_code_to_info(static_cast<PcTextureAnimCodesJak3>(vif0.immediate), *this);
auto p = scoped_prof(anim.name.c_str());
run_fixed_animation_array(anim.anim_array_idx, tf, texture_pool);
break;
}
case PcTextureAnimCodesJak3::CLOUDS_AND_FOG:
case PcTextureAnimCodesJak3::CLOUDS_HIRES: {
auto p = scoped_prof("clouds-and-fog");
handle_clouds_and_fog(tf, texture_pool,
vif0.immediate == (u16)PcTextureAnimCodesJak3::CLOUDS_HIRES);
break;
}
default:
lg::print("bad imm: {}\n", vif0.immediate);
ASSERT_NOT_REACHED();
}
break;
case DARKJAK: {
auto p = scoped_prof("darkjak");
run_clut_blender_group(tf, m_darkjak_clut_blender_idx, frame_idx);
} break;
case PRISON_JAK: {
auto p = scoped_prof("prisonjak");
run_clut_blender_group(tf, m_jakb_prison_clut_blender_idx, frame_idx);
} break;
case ORACLE_JAK: {
auto p = scoped_prof("oraclejak");
run_clut_blender_group(tf, m_jakb_oracle_clut_blender_idx, frame_idx);
} break;
case NEST_JAK: {
auto p = scoped_prof("nestjak");
run_clut_blender_group(tf, m_jakb_nest_clut_blender_idx, frame_idx);
} break;
case KOR_TRANSFORM: {
auto p = scoped_prof("kor");
run_clut_blender_group(tf, m_kor_transform_clut_blender_idx, frame_idx);
} break;
case SKULL_GEM: {
auto p = scoped_prof("skull-gem");
run_fixed_animation_array(m_skull_gem_fixed_anim_array_idx, tf, texture_pool);
} break;
case BOMB: {
auto p = scoped_prof("bomb");
run_fixed_animation_array(m_bomb_fixed_anim_array_idx, tf, texture_pool);
} break;
case CAS_CONVEYOR: {
auto p = scoped_prof("cas-conveyor");
run_fixed_animation_array(m_cas_conveyor_anim_array_idx, tf, texture_pool);
} break;
case SECURITY: {
auto p = scoped_prof("security");
run_fixed_animation_array(m_security_anim_array_idx, tf, texture_pool);
} break;
case WATERFALL: {
auto p = scoped_prof("waterfall");
run_fixed_animation_array(m_waterfall_anim_array_idx, tf, texture_pool);
} break;
case WATERFALL_B: {
auto p = scoped_prof("waterfall-b");
run_fixed_animation_array(m_waterfall_b_anim_array_idx, tf, texture_pool);
} break;
case LAVA: {
auto p = scoped_prof("lava");
run_fixed_animation_array(m_lava_anim_array_idx, tf, texture_pool);
} break;
case LAVA_B: {
auto p = scoped_prof("lava-b");
run_fixed_animation_array(m_lava_b_anim_array_idx, tf, texture_pool);
} break;
case STADIUMB: {
auto p = scoped_prof("stadiumb");
run_fixed_animation_array(m_stadiumb_anim_array_idx, tf, texture_pool);
} break;
case FORTRESS_PRIS: {
auto p = scoped_prof("fort-pris");
run_fixed_animation_array(m_fortress_pris_anim_array_idx, tf, texture_pool);
} break;
case FORTRESS_WARP: {
auto p = scoped_prof("fort-warp");
run_fixed_animation_array(m_fortress_warp_anim_array_idx, tf, texture_pool);
} break;
case METKOR: {
auto p = scoped_prof("metkor");
run_fixed_animation_array(m_metkor_anim_array_idx, tf, texture_pool);
} break;
case SHIELD: {
auto p = scoped_prof("shield");
run_fixed_animation_array(m_shield_anim_array_idx, tf, texture_pool);
} break;
case KREW_HOLO: {
auto p = scoped_prof("krew-holo");
run_fixed_animation_array(m_krew_holo_anim_array_idx, tf, texture_pool);
} break;
case CLOUDS_AND_FOG:
case CLOUDS_HIRES: {
auto p = scoped_prof("clouds-and-fog");
handle_clouds_and_fog(tf, texture_pool, vif0.immediate == CLOUDS_HIRES);
} break;
case SLIME: {
auto p = scoped_prof("slime");
handle_slime(tf, texture_pool);
} break;
default:
lg::print("bad imm: {}\n", vif0.immediate);
lg::print("unsupported game version {}\n", (int)this->m_version);
ASSERT_NOT_REACHED();
}
} else {
@@ -1818,6 +2274,21 @@ void TextureAnimator::set_up_opengl_for_fixed(const FixedLayerDef& def,
blend_c == GsAlpha::BlendMode::SOURCE && blend_d == GsAlpha::BlendMode::DEST) {
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
} else if (blend_a == GsAlpha::BlendMode::SOURCE &&
blend_b == GsAlpha::BlendMode::ZERO_OR_FIXED &&
blend_c == GsAlpha::BlendMode::ZERO_OR_FIXED &&
blend_d == GsAlpha::BlendMode::ZERO_OR_FIXED && def.blend_fix == 128) {
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
} else if (blend_a == GsAlpha::BlendMode::SOURCE &&
blend_b == GsAlpha::BlendMode::ZERO_OR_FIXED &&
blend_c == GsAlpha::BlendMode::ZERO_OR_FIXED &&
blend_d == GsAlpha::BlendMode::DEST) {
// 0, 2, 2, 1
// (Cs - 0) * FIX / 128 + Cd
// So source is fix / 128, and dest is 1
float color = (float)def.blend_fix / 128.f;
glBlendColor(color, color, color, color);
glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE, GL_ONE, GL_ZERO);
} else {
lg::print("unhandled blend: {} {} {} {}\n", (int)blend_a, (int)blend_b, (int)blend_c,
(int)blend_d);
@@ -1958,14 +2429,23 @@ void TextureAnimator::set_draw_data_from_interpolated(DrawData* result,
const LayerVals& vals,
int w,
int h) {
ASSERT(vals.rot == 0);
result->color = (vals.color * 128.f).cast<u32>();
math::Vector2f pos_scale(vals.scale.x() * w, vals.scale.y() * h);
math::Vector2f pos_offset(2048.f + (vals.offset.x() * w), 2048.f + (vals.offset.y() * h));
math::Vector2f st_scale = vals.st_scale;
math::Vector2f st_offset = vals.st_offset;
const math::Vector2f corners[4] = {math::Vector2f{-0.5, -0.5}, math::Vector2f{0.5, -0.5},
math::Vector2f{-0.5, 0.5}, math::Vector2f{0.5, 0.5}};
math::Vector2f corners[4] = {math::Vector2f{-0.5, -0.5}, math::Vector2f{0.5, -0.5},
math::Vector2f{-0.5, 0.5}, math::Vector2f{0.5, 0.5}};
if (vals.rot) {
const float rotation_radians = 2.f * M_PI * vals.rot / 65536.f;
const float sine = std::sin(rotation_radians);
const float cosine = std::cos(rotation_radians);
math::Vector2f vx(sine, cosine);
math::Vector2f vy(cosine, -sine);
for (auto& corner : corners) {
corner = vx * corner.x() + vy * corner.y();
}
}
math::Vector2f sts[4];
math::Vector2<u32> poss[4];
@@ -2056,8 +2536,14 @@ void TextureAnimator::run_fixed_animation(FixedAnim& anim, float time) {
if (true) { // todo
glColorMask(true, true, true, false);
glUniform1f(m_uniforms.alpha_multiply, 2.f);
glUniform1i(m_uniforms.set_alpha, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glColorMask(false, false, false, true);
if (anim.def.set_alpha) {
glUniform1i(m_uniforms.set_alpha, 1);
glUniform4f(m_uniforms.rgba, draw_data.color.x(), draw_data.color.x(),
draw_data.color.x(), 128.f);
}
glUniform1f(m_uniforms.alpha_multiply, 1.f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else {
@@ -59,7 +59,7 @@ struct ShaderContext {
};
struct OpenGLTexturePool {
OpenGLTexturePool();
OpenGLTexturePool(GameVersion version);
~OpenGLTexturePool();
GLuint allocate(u64 w, u64 h);
void free(GLuint texture, u64 w, u64 h);
@@ -148,6 +148,18 @@ struct FixedLayerDef {
GsAlpha::BlendMode blend_modes[4]; // abcd
u8 blend_fix = 0;
void set_blend(GsAlpha::BlendMode a,
GsAlpha::BlendMode b,
GsAlpha::BlendMode c,
GsAlpha::BlendMode d,
u8 fix) {
blend_modes[0] = a;
blend_modes[1] = b;
blend_modes[2] = c;
blend_modes[3] = d;
blend_fix = fix;
}
void set_blend_b2_d1() {
blend_modes[0] = GsAlpha::BlendMode::SOURCE;
blend_modes[1] = GsAlpha::BlendMode::ZERO_OR_FIXED;
@@ -181,6 +193,15 @@ struct FixedAnimDef {
// alpha blend off, so alpha doesn't matter i think.
std::vector<FixedLayerDef> layers;
bool move_to_pool = false;
bool set_alpha = false;
void set_times(std::vector<std::pair<float, float>> times) {
ASSERT(layers.size() >= times.size());
for (size_t i = 0; i < layers.size(); i++) {
auto& layer = layers.at(i);
layer.start_time = times.at(i).first;
layer.end_time = times.at(i).second;
}
}
};
struct DynamicLayerData {
@@ -343,6 +364,7 @@ class TextureAnimator {
GLuint tcc;
GLuint alpha_multiply;
GLuint minimum, maximum;
GLuint set_alpha;
} m_uniforms;
struct {
@@ -396,6 +418,8 @@ class TextureAnimator {
m_psm32_to_psm8_64_64;
ClutReader m_clut_table;
public:
// jak 2
int m_skull_gem_fixed_anim_array_idx = -1;
int m_bomb_fixed_anim_array_idx = -1;
int m_cas_conveyor_anim_array_idx = -1;
@@ -411,9 +435,59 @@ class TextureAnimator {
int m_shield_anim_array_idx = -1;
int m_krew_holo_anim_array_idx = -1;
// jak 3
int m_default_water_anim_array_idx = -1;
int m_default_warp_anim_array_idx = -1;
int m_templea_water_anim_array_idx = -1;
int m_templea_warp_anim_array_idx = -1;
int m_templeb_warp_anim_array_idx = -1;
int m_templec_water_anim_array_idx = -1;
int m_sewc_water_anim_array_idx = -1;
int m_sewd_water_anim_array_idx = -1;
int m_sewe_water_anim_array_idx = -1;
int m_sewg_water_anim_array_idx = -1;
int m_sewh_water_anim_array_idx = -1;
int m_sewi_water_anim_array_idx = -1;
int m_sewj_water_anim_array_idx = -1;
int m_sewl_water_anim_array_idx = -1;
int m_sewm_water_anim_array_idx = -1;
int m_sewn_water_anim_array_idx = -1;
int m_desresc_warp_anim_array_idx = -1;
int m_ctyslumb_water_anim_array_idx = -1;
int m_nstb_quicksand_anim_array_idx = -1;
int m_ctyslumc_water_anim_array_idx = -1;
int m_factoryc_alpha_anim_array_idx = -1;
int m_hfrag_anim_array_idx = -1;
int m_hanga_sprite_anim_array_idx = -1;
int m_hanga_water_anim_array_idx = -1;
int m_desertd_water_anim_array_idx = -1;
int m_lmhcityb_tfrag_anim_array_idx = -1;
int m_towerb_water_anim_array_idx = -1;
int m_comb_field_anim_array_idx = -1;
int m_wasstada_alpha_anim_array_idx = -1;
int m_factoryb_water_anim_array_idx = -1;
int m_lmhcitya_tfrag_anim_array_idx = -1;
int m_mhcitya_pris_anim_array_idx = -1;
int m_rubblea_water_anim_array_idx = -1;
int m_rubblea2_water_anim_array_idx = -1;
int m_rubbleb_water_anim_array_idx = -1;
int m_rubblec_water_anim_array_idx = -1;
int m_foresta_water_anim_array_idx = -1;
int m_forestb_water_anim_array_idx = -1;
int m_lforplnt_pris_anim_array_idx = -1;
int m_ltnfxhip_anim_array_idx = -1;
int m_lgunnorm_water_anim_array_idx = -1;
int m_ljkdxvin_anim_array_idx = -1;
int m_waspal_water_anim_array_idx = -1;
int m_mined_tfrag_anim_array_idx = -1;
int m_volcanox_warp_anim_array_idx = -1;
int m_templex_water_anim_array_idx = -1;
int m_volcanoa_anim_array_idx = -1;
int m_deshover_anim_array_idx = -1;
int m_darkjak_highres_clut_blender_idx = -1;
std::vector<FixedAnimArray> m_fixed_anim_arrays;
public:
// note: for now these can't be easily changed because each layer has its own hand-tuned
// parameters from the original game. If you want to change it, you'll need to make up parameters
// for those new layers.
File diff suppressed because it is too large. Load diff
@@ -119,3 +119,7 @@ void TextureUploadHandler::draw_debug_window() {
m_direct->draw_debug_window();
}
}
bool TextureUploadHandler::empty() const {
return m_upload_count == 0;
}
@@ -18,6 +18,7 @@ class TextureUploadHandler : public BucketRenderer {
bool add_direct = false);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
bool empty() const override;
private:
struct TextureUpload {
@@ -129,8 +129,8 @@ Hfrag::HfragLevel* Hfrag::get_hfrag_level(const std::string& name,
// first, see if this level is loaded. If not, there's nothing we can do.
auto lev_data = render_state->loader->get_tfrag3_level(name);
if (!lev_data) {
printf("[hfrag] can't display %s because it's not loaded.\n", name.c_str());
render_state->loader->debug_print_loaded_levels();
// printf("[hfrag] can't display %s because it's not loaded.\n", name.c_str());
// render_state->loader->debug_print_loaded_levels();
return nullptr;
}
+20 -5
View File
@@ -1,9 +1,7 @@
#include "debug_gui.h"
#include <algorithm>
#include "common/global_profiler/GlobalProfiler.h"
#include "common/util/string_util.h"
#include "game/graphics/display.h"
#include "game/graphics/gfx.h"
@@ -164,10 +162,27 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
}
if (ImGui::BeginMenu("Event Profiler")) {
if (ImGui::Checkbox("Record", &record_events)) {
if (ImGui::Checkbox("Record Events", &record_events)) {
prof().set_enable(record_events);
}
ImGui::MenuItem("Dump to file", nullptr, &dump_events);
ImGui::SameLine();
ImGui::Text(fmt::format("({}/{})", prof().get_next_idx(), prof().get_max_events()).c_str());
ImGui::InputInt("Event Buffer Size", &max_event_buffer_size);
if (ImGui::Button("Resize")) {
prof().update_event_buffer_size(max_event_buffer_size);
}
if (ImGui::Button("Reset Events")) {
prof().clear();
}
ImGui::Separator();
ImGui::Checkbox("Enable Compression", &prof().m_enable_compression);
if (ImGui::Button("Dump to File")) {
record_events = false;
prof().dump_to_json();
}
// if (ImGui::Button("Open dump folder")) {
// // TODO - https://github.com/mlabbe/nativefiledialog
// }
ImGui::EndMenu();
}
+1 -1
View File
@@ -64,7 +64,7 @@ class OpenGlDebugGui {
bool small_profiler = false;
bool record_events = false;
bool dump_events = false;
int max_event_buffer_size = 65536;
bool want_reboot_in_debug = false;
bool screenshot_hotkey_enabled = true;
@@ -70,11 +70,16 @@ void Generic2::render_in_mode(DmaFollower& dma,
case Mode::LIGHTNING:
process_dma_lightning(dma, render_state->next_bucket);
break;
case Mode::PRIM:
process_dma_prim(dma, render_state->next_bucket);
break;
default:
ASSERT_NOT_REACHED();
}
}
m_empty = m_next_free_vert == 0;
{
// the next pass is to look at all of that data, and figure out the best order to draw it
// using OpenGL
@@ -84,6 +89,7 @@ void Generic2::render_in_mode(DmaFollower& dma,
setup_draws(true, true);
break;
case Mode::LIGHTNING:
case Mode::PRIM:
setup_draws(false, true);
break;
case Mode::WARP:
@@ -11,7 +11,7 @@ class Generic2 {
u32 num_buckets = 800);
~Generic2();
enum class Mode { NORMAL, LIGHTNING, WARP };
enum class Mode { NORMAL, LIGHTNING, WARP, PRIM };
void render_in_mode(DmaFollower& dma,
SharedRenderState* render_state,
@@ -19,6 +19,7 @@ class Generic2 {
Mode mode);
void draw_debug_window();
bool empty() { return m_empty; }
struct Vertex {
math::Vector<float, 3> xyz;
@@ -42,6 +43,7 @@ class Generic2 {
void process_dma_jak1(DmaFollower& dma, u32 next_bucket);
void process_dma_lightning(DmaFollower& dma, u32 next_bucket);
void process_dma_jak2(DmaFollower& dma, u32 next_bucket);
void process_dma_prim(DmaFollower& dma, u32 next_bucket);
void setup_draws(bool enable_at, bool default_fog);
void do_draws(SharedRenderState* render_state, ScopedProfilerNode& prof);
void do_draws_for_alpha(SharedRenderState* render_state,
@@ -84,6 +86,8 @@ class Generic2 {
float hud_mat_23, hud_mat_32, hud_mat_33;
bool uses_hud = false;
bool uses_full_matrix = false;
math::Vector4f full_matrix[4];
} m_drawing_config;
struct GsState {
@@ -198,8 +202,10 @@ class Generic2 {
GLuint vertex_buffer;
GLuint index_buffer;
GLuint alpha_reject, color_mult, fog_color, scale, mat_23, mat_32, mat_33, fog_consts,
hvdf_offset;
hvdf_offset, use_full_matrix, full_matrix;
GLuint gfx_hack_no_tex;
GLuint warp_sample_mode;
} m_ogl;
bool m_empty = false;
};
@@ -22,4 +22,9 @@ void Generic2BucketRenderer::render(DmaFollower& dma,
return;
}
m_generic->render_in_mode(dma, render_state, prof, m_mode);
m_empty = m_generic->empty();
}
bool Generic2BucketRenderer::empty() const {
return m_empty;
}
@@ -11,8 +11,10 @@ class Generic2BucketRenderer : public BucketRenderer {
Generic2::Mode mode);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
bool empty() const override;
private:
std::shared_ptr<Generic2> m_generic;
Generic2::Mode m_mode;
bool m_empty = false;
};
@@ -190,6 +190,16 @@ void Generic2::determine_draw_modes(bool enable_at, bool default_fog) {
current_mode.set_alpha_fail(reg.afail());
current_mode.set_zt(reg.zte());
current_mode.set_depth_test(reg.ztest());
// detect a strange way of disabling z writes by light-trail.
// we don't actually handle alpha_fail later on in Direct - it doesn't map well to modern
// graphics and would require a draw call per primitive.
if (current_mode.get_alpha_fail() == GsTest::AlphaFail::FB_ONLY &&
current_mode.get_aref() == 0x80 &&
current_mode.get_alpha_test() == DrawMode::AlphaTest::GEQUAL) {
current_mode.set_alpha_test(DrawMode::AlphaTest::ALWAYS);
current_mode.disable_depth_write();
}
}
m_adgifs[i].mode = current_mode;
@@ -271,6 +281,10 @@ void Generic2::process_matrices() {
bool found_proj_matrix = false;
std::array<math::Vector4f, 4> projection_matrix, hud_matrix;
for (u32 i = 0; i < m_next_free_frag; i++) {
if (m_drawing_config.uses_full_matrix) {
memcpy(&m_drawing_config.full_matrix, m_fragments[i].header, 64);
break;
}
float mat_33;
memcpy(&mat_33, m_fragments[i].header + 15 * sizeof(float), sizeof(float));
if (mat_33 == 0) {
@@ -550,6 +550,83 @@ void unpack_vertex(Generic2::Vertex* out, const u8* in, int count) {
}
}
void Generic2::process_dma_prim(DmaFollower& dma, u32 next_bucket) {
reset_buffers();
auto first_data = dma.read_and_advance();
// handle the stuff at the beginning.
// if the engine didn't run the generic renderer setup function, this bucket will end here.
if (is_nop_zero(first_data) && next_bucket == dma.current_tag_offset()) {
return;
}
// MARK NOP (for profiling)
auto v0k = first_data.vifcode0().kind;
ASSERT((v0k == VifCode::Kind::MARK || v0k == VifCode::Kind::NOP) &&
first_data.vifcode1().kind == VifCode::Kind::NOP);
// NOP DIRECT (set up GS, from generic setup)
auto direct_setup = dma.read_and_advance();
ASSERT(direct_setup.size_bytes == 32 && direct_setup.vifcode0().kind == VifCode::Kind::NOP &&
direct_setup.vifcode1().kind == VifCode::Kind::DIRECT);
m_drawing_config.zmsk = false;
m_drawing_config.uses_full_matrix = true;
// STYCYCL to set up generic VU1 constants
auto constants = dma.read_and_advance();
ASSERT(constants.size_bytes == 128);
ASSERT(constants.vifcode0().kind == VifCode::Kind::STCYCL);
ASSERT(constants.vifcode1().kind == VifCode::Kind::UNPACK_V4_32);
memcpy(&m_drawing_config.pfog0, constants.data + 0, 4);
memcpy(&m_drawing_config.fog_min, constants.data + 4, 4);
memcpy(&m_drawing_config.fog_max, constants.data + 8, 4);
memcpy(m_drawing_config.hvdf_offset.data(), constants.data + 32, 16);
// 32: MSCALF 0x0 STMOD 0b0 init generic VU1
auto mscalf = dma.read_and_advance();
ASSERT(mscalf.vifcode0().kind == VifCode::Kind::MSCALF &&
mscalf.vifcode1().kind == VifCode::Kind::STMOD);
// 0: NOP NOP
// auto another_nop = dma.read_and_advance();
// ASSERT(is_nop_zero(another_nop));
while (dma.current_tag_offset() != next_bucket) {
auto up1 = dma.read_and_advance();
while (up1.vifcode0().kind == VifCode::Kind::NOP && up1.vifcode1().kind == VifCode::Kind::NOP) {
up1 = dma.read_and_advance();
}
if (up1.vifcode0().kind == VifCode::Kind::FLUSHA) {
while (dma.current_tag_offset() != next_bucket) {
auto it = dma.read_and_advance();
}
break;
}
auto up2 = dma.read_and_advance();
auto call = dma.read_and_advance();
// up1 is a 12 qw upload for control:
// up2 is vertex upload.
auto* frag = &next_frag();
ASSERT(up1.size_bytes == Generic2::FRAG_HEADER_SIZE + 5 * 16); // header + adgif
memcpy(frag->header, up1.data, Generic2::FRAG_HEADER_SIZE);
frag->adgif_idx = m_next_free_adgif;
frag->adgif_count = 1;
frag->mscal_addr = 6;
frag->uses_hud = false;
auto* adgif = &next_adgif();
memcpy(&adgif->data, up1.data + Generic2::FRAG_HEADER_SIZE, sizeof(AdGifData));
// (new 'static 'gif-tag-regs-32 :regs0 (gif-reg-id st) :regs1 (gif-reg-id rgbaq) :regs2
// (gif-reg-id xyzf2))
int num_vtx = up2.size_bytes / (16 * 3);
frag->vtx_count = num_vtx;
frag->vtx_idx = m_next_free_vert;
alloc_vtx(num_vtx);
unpack_vertex(&m_verts[frag->vtx_idx], up2.data, num_vtx);
}
}
void Generic2::process_dma_lightning(DmaFollower& dma, u32 next_bucket) {
reset_buffers();
auto first_data = dma.read_and_advance();
@@ -74,6 +74,8 @@ void Generic2::opengl_setup(ShaderLibrary& shaders) {
m_ogl.hvdf_offset = glGetUniformLocation(id, "hvdf_offset");
m_ogl.gfx_hack_no_tex = glGetUniformLocation(id, "gfx_hack_no_tex");
m_ogl.warp_sample_mode = glGetUniformLocation(id, "warp_sample_mode");
m_ogl.use_full_matrix = glGetUniformLocation(id, "use_full_matrix");
m_ogl.full_matrix = glGetUniformLocation(id, "full_matrix");
}
void Generic2::opengl_cleanup() {
@@ -307,6 +309,12 @@ void Generic2::do_draws(SharedRenderState* render_state, ScopedProfilerNode& pro
glPrimitiveRestartIndex(UINT32_MAX);
opengl_bind_and_setup_proj(render_state);
if (m_drawing_config.uses_full_matrix) {
glUniform1i(m_ogl.use_full_matrix, 1);
glUniformMatrix4fv(m_ogl.full_matrix, 1, GL_FALSE, m_drawing_config.full_matrix[0].data());
} else {
glUniform1i(m_ogl.use_full_matrix, 0);
}
constexpr DrawMode::AlphaBlend alpha_order[ALPHA_MODE_COUNT] = {
DrawMode::AlphaBlend::SRC_0_FIX_DST, DrawMode::AlphaBlend::SRC_SRC_SRC_SRC,
DrawMode::AlphaBlend::SRC_DST_SRC_DST, DrawMode::AlphaBlend::SRC_0_SRC_DST,
@@ -7,6 +7,7 @@
#endif
#include "common/global_profiler/GlobalProfiler.h"
#include "common/util/fnv.h"
#include "game/graphics/opengl_renderer/EyeRenderer.h"
#include "game/graphics/opengl_renderer/background/background_common.h"
@@ -54,6 +55,8 @@ std::mutex g_merc_data_mutex;
Merc2::Merc2(ShaderLibrary& shaders, const std::vector<GLuint>* anim_slot_array)
: m_anim_slot_array(anim_slot_array) {
ASSERT(fnv64("the quick brown fox jumps over the lazy dog") == 0x7404cea13ff89bb0);
// Set up main vertex array. This will point to the data stored in the .FR3 level file, and will
// be uploaded to the GPU by the Loader.
glGenVertexArrays(1, &m_vao);
@@ -535,11 +538,14 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
auto* flags = (const PcMercFlags*)input_data;
int num_effects = flags->effect_count; // mostly just a sanity check
ASSERT(num_effects < kMaxEffect);
// hack for custom models to disable blerc/mod draws
bool is_custom_model = model->effects.at(0).all_draws.at(0).no_strip;
u64 current_ignore_alpha_bits = flags->ignore_alpha_mask; // shader settings
u64 current_effect_enable_bits = flags->enable_mask; // mask for game to disable an effect
bool model_uses_mod = flags->bitflags & 1; // if we should update vertices from game.
bool model_uses_mod =
flags->bitflags & 1 && !is_custom_model; // if we should update vertices from game.
bool model_disables_fog = flags->bitflags & 2;
bool model_uses_pc_blerc = flags->bitflags & 4;
bool model_uses_pc_blerc = flags->bitflags & 4 && !is_custom_model;
bool model_disables_envmap = flags->bitflags & 8;
input_data += 32;
@@ -609,6 +615,8 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
u32 lights = alloc_lights(current_lights);
stats->num_lights++;
u64 hash = fnv64(model->name);
// loop over effects, creating draws for each
for (size_t ei = 0; ei < model->effects.size(); ei++) {
// game has disabled it?
@@ -633,7 +641,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// do fixed draws:
for (auto& fdraw : effect.mod.fix_draw) {
alloc_normal_draw(fdraw, ignore_alpha, lev_bucket, first_bone, lights, uses_water,
model_disables_fog);
model_disables_fog, hash);
if (should_envmap) {
try_alloc_envmap_draw(fdraw, effect.envmap_mode, effect.envmap_texture, lev_bucket,
fade_buffer + 4 * ei, first_bone, lights, uses_water);
@@ -643,7 +651,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// do mod draws
for (auto& mdraw : effect.mod.mod_draw) {
auto n = alloc_normal_draw(mdraw, ignore_alpha, lev_bucket, first_bone, lights, uses_water,
model_disables_fog);
model_disables_fog, hash);
// modify the draw, set the mod flag and point it to the opengl buffer
n->flags |= MOD_VTX;
n->mod_vtx_buffer = mod_opengl_buffers[ei];
@@ -665,7 +673,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
fade_buffer + 4 * ei, first_bone, lights, uses_water);
}
alloc_normal_draw(draw, ignore_alpha, lev_bucket, first_bone, lights, uses_water,
model_disables_fog);
model_disables_fog, hash);
}
}
}
@@ -761,7 +769,9 @@ void Merc2::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
MercDebugStats* stats) {
bool hack = stats->collect_debug_model_list;
*stats = {};
stats->collect_debug_model_list = hack;
if (stats->collect_debug_model_list) {
stats->model_list.clear();
}
@@ -1053,6 +1063,7 @@ Merc2::Draw* Merc2::try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw,
draw->first_index = mdraw.first_index;
draw->index_count = mdraw.index_count;
draw->mode = envmap_mode;
draw->hash = 0;
if (jak1_water_mode) {
draw->mode.enable_ab();
draw->mode.disable_depth_write();
@@ -1073,12 +1084,14 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw,
u32 first_bone,
u32 lights,
bool jak1_water_mode,
bool disable_fog) {
bool disable_fog,
u64 hash) {
Draw* draw = &lev_bucket->draws[lev_bucket->next_free_draw++];
draw->flags = 0;
draw->first_index = mdraw.first_index;
draw->index_count = mdraw.index_count;
draw->mode = mdraw.mode;
draw->hash = hash;
if (jak1_water_mode) {
draw->mode.set_ab(true);
draw->mode.disable_depth_write();
@@ -1205,7 +1218,7 @@ void Merc2::do_draws(const Draw* draw_array,
bool set_fade,
SharedRenderState* render_state) {
glBindVertexArray(m_vao);
int last_tex = -1;
s32 last_tex = INT32_MIN;
int last_light = -1;
bool normal_vtx_buffer_bound = true;
@@ -1244,10 +1257,19 @@ void Merc2::do_draws(const Draw* draw_array,
if (draw.texture < (int)lev->textures.size() && draw.texture >= 0) {
glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture));
} else if ((draw.texture & 0xffffff00) == 0xefffff00) {
auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff);
if (maybe_eye) {
glBindTexture(GL_TEXTURE_2D, *maybe_eye);
if (render_state->version >= GameVersion::Jak3) {
auto maybe_eye =
render_state->eye_renderer->lookup_eye_texture_hash(draw.hash, (draw.texture & 1));
if (maybe_eye) {
glBindTexture(GL_TEXTURE_2D, *maybe_eye);
}
} else {
auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff);
if (maybe_eye) {
glBindTexture(GL_TEXTURE_2D, *maybe_eye);
}
}
use_mipmaps_for_filtering = false;
} else if (draw.texture < 0) {
int slot = -(draw.texture + 1);
@@ -192,6 +192,7 @@ class Merc2 {
u8 fade[4];
// no strip hack for custom models
u8 no_strip;
u64 hash;
};
struct LevelDrawBucket {
@@ -213,7 +214,8 @@ class Merc2 {
u32 first_bone,
u32 lights,
bool jak1_water_mode,
bool disable_fog);
bool disable_fog,
u64 hash);
Draw* try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw,
const DrawMode& envmap_mode,
@@ -17,8 +17,14 @@ void Merc2BucketRenderer::render(DmaFollower& dma,
}
m_renderer->render(dma, render_state, prof, &m_debug_stats);
m_empty = m_debug_stats.num_predicted_draws == 0;
}
void Merc2BucketRenderer::draw_debug_window() {
m_renderer->draw_debug_window(&m_debug_stats);
}
bool Merc2BucketRenderer::empty() const {
return m_empty;
}
@@ -8,8 +8,10 @@ class Merc2BucketRenderer : public BucketRenderer {
Merc2BucketRenderer(const std::string& name, int my_id, std::shared_ptr<Merc2> merc);
void draw_debug_window() override;
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
bool empty() const override;
private:
bool m_empty = false;
std::shared_ptr<Merc2> m_renderer;
MercDebugStats m_debug_stats;
};
@@ -10,6 +10,8 @@ uniform vec3 fog_constants;
uniform vec4 scale;
uniform float mat_23;
uniform float mat_33;
uniform bool use_full_matrix;
uniform mat4 full_matrix;
uniform vec4 hvdf_offset;
uniform uint warp_sample_mode;
@@ -38,11 +40,17 @@ void main() {
// maddax.xyzw ACC, vf08, vf16 matrix multiply X
// vu.acc.madda(Mask::xyzw, gen.mat0, gen.vtx_load0.x());
transformed.xyz = position_in * scale.xyz;
transformed.z += mat_32;
transformed.w = mat_23 * position_in.z + mat_33;
transformed *= -1; // todo?
if (use_full_matrix) {
transformed = -full_matrix[3];
transformed -= full_matrix[0] * position_in.x ;
transformed -= full_matrix[1] * position_in.y;
transformed -= full_matrix[2] * position_in.z ;
} else {
transformed.xyz = position_in * scale.xyz;
transformed.z += mat_32;
transformed.w = mat_23 * position_in.z + mat_33;
transformed *= -1;
}
// div Q, vf01.x, vf12.w perspective divide
@@ -4,6 +4,7 @@ out vec4 color;
uniform vec4 rgba;
uniform int enable_tex;
uniform int set_alpha;
uniform int tcc;
uniform ivec4 channel_scramble;
uniform float alpha_multiply;
@@ -57,5 +58,9 @@ void main() {
} else {
color = (rgba / 128.);
}
color.a *= alpha_multiply;
if (set_alpha == 1) {
color.a = 0.5;
} else {
color.a *= alpha_multiply;
}
}
-18
View File
@@ -448,23 +448,6 @@ void render_game_frame(int game_width,
}
}
void update_global_profiler() {
if (g_gfx_data->debug_gui.dump_events) {
prof().set_enable(false);
g_gfx_data->debug_gui.dump_events = false;
// TODO - the file rotation code had an infinite loop here if it couldn't find anything
// matching the format
//
// Does the existing log rotation code have that problem?
auto file_path = file_util::get_jak_project_dir() / "profile_data" /
fmt::format("prof-{}.json", str_util::current_local_timestamp_no_colons());
file_util::create_dir_if_needed_for_file(file_path);
prof().dump_to_json(file_path.string());
}
}
void GLDisplay::process_sdl_events() {
SDL_Event evt;
while (SDL_PollEvent(&evt) != 0) {
@@ -603,7 +586,6 @@ void GLDisplay::render() {
// Start timing for the next frame.
g_gfx_data->debug_gui.start_frame();
prof().instant_event("ROOT");
update_global_profiler();
// toggle even odd and wake up engine waiting on vsync.
// TODO: we could play with moving this earlier, right after the final bucket renderer.
-1
View File
@@ -125,7 +125,6 @@ struct link_control {
}
};
void klink_init_globals();
Ptr<u8> c_symlink2(Ptr<u8> objData, Ptr<u8> linkObj, Ptr<u8> relocTable);
extern link_control saved_link_control;
+33 -17
View File
@@ -448,6 +448,19 @@ u64 pc_get_mips2c(u32 name) {
return Mips2C::gLinkedFunctionTable.get(n);
}
u64 pc_get_display_id() {
if (Display::GetMainDisplay()) {
return Display::GetMainDisplay()->get_display_manager()->get_active_display_id();
}
return 0;
}
void pc_set_display_id(u64 display_id) {
if (Display::GetMainDisplay()) {
Display::GetMainDisplay()->get_display_manager()->enqueue_set_display_id(display_id);
}
}
u64 pc_get_display_name(u32 id, u32 str_dest_ptr) {
std::string name = "";
if (Display::GetMainDisplay()) {
@@ -469,17 +482,16 @@ u64 pc_get_display_name(u32 id, u32 str_dest_ptr) {
}
u32 pc_get_display_mode() {
auto display_mode = WindowDisplayMode::Windowed;
auto display_mode = game_settings::DisplaySettings::DisplayMode::Windowed;
if (Display::GetMainDisplay()) {
display_mode = Display::GetMainDisplay()->get_display_manager()->get_window_display_mode();
display_mode = Display::GetMainDisplay()->get_display_manager()->get_display_mode();
}
switch (display_mode) {
case WindowDisplayMode::Borderless:
case game_settings::DisplaySettings::DisplayMode::Borderless:
return g_pc_port_funcs.intern_from_c("borderless").offset;
case WindowDisplayMode::Fullscreen:
case game_settings::DisplaySettings::DisplayMode::Fullscreen:
return g_pc_port_funcs.intern_from_c("fullscreen").offset;
default:
case WindowDisplayMode::Windowed:
return g_pc_port_funcs.intern_from_c("windowed").offset;
}
}
@@ -490,13 +502,13 @@ void pc_set_display_mode(u32 symptr) {
}
if (symptr == g_pc_port_funcs.intern_from_c("windowed").offset || symptr == s7.offset) {
Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode(
WindowDisplayMode::Windowed);
game_settings::DisplaySettings::DisplayMode::Windowed);
} else if (symptr == g_pc_port_funcs.intern_from_c("borderless").offset) {
Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode(
WindowDisplayMode::Borderless);
game_settings::DisplaySettings::DisplayMode::Borderless);
} else if (symptr == g_pc_port_funcs.intern_from_c("fullscreen").offset) {
Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_display_mode(
WindowDisplayMode::Fullscreen);
game_settings::DisplaySettings::DisplayMode::Fullscreen);
}
}
@@ -568,12 +580,6 @@ void pc_get_window_scale(u32 x_ptr, u32 y_ptr) {
}
}
void pc_get_fullscreen_display(u64 display_id) {
if (Display::GetMainDisplay()) {
Display::GetMainDisplay()->get_display_manager()->enqueue_set_fullscreen_display_id(display_id);
}
}
void pc_set_window_size(u64 width, u64 height) {
if (Display::GetMainDisplay()) {
Display::GetMainDisplay()->get_display_manager()->enqueue_set_window_size(width, height);
@@ -601,6 +607,14 @@ void pc_get_resolution(u32 id, u32 w_ptr, u32 h_ptr) {
}
}
u64 pc_is_supported_resolution(u64 width, u64 height) {
if (Display::GetMainDisplay()) {
return bool_to_symbol(
Display::GetMainDisplay()->get_display_manager()->is_supported_resolution(width, height));
}
return bool_to_symbol(false);
}
u64 pc_get_controller_name(u32 id, u32 str_dest_ptr) {
std::string name = "";
if (Display::GetMainDisplay()) {
@@ -912,9 +926,11 @@ void init_common_pc_port_functions(
// -- DISPLAY RELATED --
// Returns the name of the display with the given id or #f if not found / empty
make_func_symbol_func("pc-get-display-id", (void*)pc_get_display_id);
make_func_symbol_func("pc-set-display-id!", (void*)pc_set_display_id);
make_func_symbol_func("pc-get-display-name", (void*)pc_get_display_name);
make_func_symbol_func("pc-get-display-mode", (void*)pc_get_display_mode);
make_func_symbol_func("pc-set-display-mode", (void*)pc_set_display_mode);
make_func_symbol_func("pc-set-display-mode!", (void*)pc_set_display_mode);
make_func_symbol_func("pc-get-display-count", (void*)pc_get_display_count);
// Returns resolution of the monitor's current display mode
make_func_symbol_func("pc-get-active-display-size", (void*)pc_get_active_display_size);
@@ -925,10 +941,10 @@ void init_common_pc_port_functions(
make_func_symbol_func("pc-get-window-size", (void*)pc_get_window_size);
// Returns scale of window. This is for DPI stuff.
make_func_symbol_func("pc-get-window-scale", (void*)pc_get_window_scale);
make_func_symbol_func("pc-set-fullscreen-display", (void*)pc_get_fullscreen_display);
make_func_symbol_func("pc-set-window-size", (void*)pc_set_window_size);
make_func_symbol_func("pc-set-window-size!", (void*)pc_set_window_size);
make_func_symbol_func("pc-get-num-resolutions", (void*)pc_get_num_resolutions);
make_func_symbol_func("pc-get-resolution", (void*)pc_get_resolution);
make_func_symbol_func("pc-is-supported-resolution?", (void*)pc_is_supported_resolution);
// -- INPUT RELATED --
// Returns the name of the display with the given id or #f if not found / empty
-1
View File
@@ -16,7 +16,6 @@ void load_and_link_dgo_from_c_fast(const char* name,
Ptr<kheapinfo> heap,
u32 linkFlag,
s32 bufferSize);
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
void kdgo_init_globals();
extern RPC_Dgo_Cmd sMsg[2];
extern RPC_Dgo_Cmd* sLastMsg;
+5 -4
View File
@@ -8,6 +8,7 @@
#include "game/kernel/common/kdgo.h"
#include "game/kernel/common/kmalloc.h"
#include "game/kernel/jak3/klink.h"
#include "game/overlord/jak3/rpc_interface.h"
namespace jak3 {
@@ -39,7 +40,7 @@ void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8>
RpcSync(DGO_RPC_CHANNEL); // make sure old RPC is finished
// put a dummy value here just to make sure the IOP overwrites it.
sMsg[msgID].result = DGO_RPC_RESULT_INIT; // !! this is 666
sMsg[msgID].status = DGO_RPC_RESULT_INIT; // !! this is 666
// inform IOP of buffers
sMsg[msgID].buffer1 = buffer1.offset;
@@ -78,13 +79,13 @@ Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
Ptr<u8> buffer(0);
if (sLastMsg) {
// if we got a good result, get pointer to object
if ((sLastMsg->result == DGO_RPC_RESULT_MORE) || (sLastMsg->result == DGO_RPC_RESULT_DONE)) {
if ((sLastMsg->status == DGO_RPC_RESULT_MORE) || (sLastMsg->status == DGO_RPC_RESULT_DONE)) {
buffer.offset =
sLastMsg->buffer1; // buffer 1 always contains location of most recently loaded object.
}
// not the last one, so don't set the flag.
if (sLastMsg->result == DGO_RPC_RESULT_MORE) {
if (sLastMsg->status == DGO_RPC_RESULT_MORE) {
*lastObjectFlag = 0;
}
@@ -111,7 +112,7 @@ void ContinueLoadingDGO(Ptr<u8> b1, Ptr<u8> b2, Ptr<u8> heapPtr) {
u32 msgID = sMsgNum;
jak3::RPC_Dgo_Cmd* sendBuff = sMsg + sMsgNum;
sMsgNum = sMsgNum ^ 1;
sendBuff->result = DGO_RPC_RESULT_INIT;
sendBuff->status = DGO_RPC_RESULT_INIT;
sMsg[msgID].buffer1 = b1.offset;
sMsg[msgID].buffer2 = b2.offset;
sMsg[msgID].buffer_heap_top = heapPtr.offset;
+220
View File
@@ -0,0 +1,220 @@
//--------------------------MIPS2C---------------------
// clang-format off
#include "game/mips2c/mips2c_private.h"
#include "game/kernel/jak3/kscheme.h"
using ::jak3::intern_from_c;
namespace Mips2C::jak3 {
namespace method_21_cloth_system {
u64 execute(void* ctxt) {
auto* c = (ExecutionContext*)ctxt;
bool bc = false;
u32 call_addr = 0;
c->daddiu(sp, sp, -80); // daddiu sp, sp, -80
c->daddiu(a3, sp, 16); // daddiu a3, sp, 16
c->daddiu(t0, sp, 32); // daddiu t0, sp, 32
c->lwu(v1, 68, a0); // lwu v1, 68(a0)
c->lw(v1, 0, v1); // lw v1, 0(v1)
c->lw(a1, 96, a0); // lw a1, 96(a0)
c->daddiu(a1, a1, -1); // daddiu a1, a1, -1
c->lw(a2, 100, a0); // lw a2, 100(a0)
c->daddiu(a2, a2, -1); // daddiu a2, a2, -1
c->addiu(t1, r0, 0); // addiu t1, r0, 0
c->addiu(t1, r0, 0); // addiu t1, r0, 0
c->mov64(t1, a3); // or t1, a3, r0
c->lui(t2, 16000); // lui t2, 16000
c->mtc1(f0, t2); // mtc1 f0, t2
c->swc1(f0, 0, t1); // swc1 f0, 0(t1)
c->lui(t2, 16000); // lui t2, 16000
c->mtc1(f0, t2); // mtc1 f0, t2
c->swc1(f0, 4, t1); // swc1 f0, 4(t1)
c->lui(t2, 16000); // lui t2, 16000
c->mtc1(f0, t2); // mtc1 f0, t2
c->swc1(f0, 8, t1); // swc1 f0, 8(t1)
c->lui(t2, 16000); // lui t2, 16000
c->mtc1(f0, t2); // mtc1 f0, t2
c->swc1(f0, 12, t1); // swc1 f0, 12(t1)
c->mov64(t1, t0); // or t1, t0, r0
c->mtc1(f0, r0); // mtc1 f0, r0
c->swc1(f0, 0, t1); // swc1 f0, 0(t1)
c->mtc1(f0, r0); // mtc1 f0, r0
c->swc1(f0, 4, t1); // swc1 f0, 4(t1)
c->mtc1(f0, r0); // mtc1 f0, r0
c->swc1(f0, 8, t1); // swc1 f0, 8(t1)
c->lwc1(f0, 144, a0); // lwc1 f0, 144(a0)
c->swc1(f0, 12, t1); // swc1 f0, 12(t1)
c->vmax_bc(DEST::xyzw, BC::w, vf1, vf0, vf0); // vmaxw.xyzw vf1, vf0, vf0
c->lqc2(vf10, 0, a3); // lqc2 vf10, 0(a3)
c->lqc2(vf15, 0, t0); // lqc2 vf15, 0(t0)
c->sd(r0, 48, sp); // sd r0, 48(sp)
c->addiu(a3, r0, 0); // addiu a3, r0, 0
//beq r0, r0, L187 // beq r0, r0, L187
// nop // sll r0, r0, 0
goto block_10; // branch always
block_1:
c->addiu(t0, r0, 0); // addiu t0, r0, 0
//beq r0, r0, L186 // beq r0, r0, L186
// nop // sll r0, r0, 0
goto block_8; // branch always
block_2:
c->addiu(t1, r0, 48); // addiu t1, r0, 48
c->ld(t2, 48, sp); // ld t2, 48(sp)
c->mult3(t1, t1, t2); // mult3 t1, t1, t2
c->daddiu(t1, t1, 12); // daddiu t1, t1, 12
c->lwu(t2, 0, a0); // lwu t2, 0(a0)
c->daddu(t1, t1, t2); // daddu t1, t1, t2
c->sw(t1, 56, sp); // sw t1, 56(sp)
c->addiu(t1, r0, 48); // addiu t1, r0, 48
c->ld(t2, 48, sp); // ld t2, 48(sp)
c->daddiu(t2, t2, 1); // daddiu t2, t2, 1
c->mult3(t1, t1, t2); // mult3 t1, t1, t2
c->daddiu(t1, t1, 12); // daddiu t1, t1, 12
c->lwu(t2, 0, a0); // lwu t2, 0(a0)
c->daddu(t1, t1, t2); // daddu t1, t1, t2
c->sw(t1, 60, sp); // sw t1, 60(sp)
c->addiu(t1, r0, 48); // addiu t1, r0, 48
c->ld(t2, 48, sp); // ld t2, 48(sp)
c->lw(t3, 96, a0); // lw t3, 96(a0)
c->daddu(t2, t2, t3); // daddu t2, t2, t3
c->mult3(t1, t1, t2); // mult3 t1, t1, t2
c->daddiu(t1, t1, 12); // daddiu t1, t1, 12
c->lwu(t2, 0, a0); // lwu t2, 0(a0)
c->daddu(t1, t1, t2); // daddu t1, t1, t2
c->sw(t1, 64, sp); // sw t1, 64(sp)
c->addiu(t1, r0, 48); // addiu t1, r0, 48
c->lw(t2, 96, a0); // lw t2, 96(a0)
c->daddiu(t2, t2, 1); // daddiu t2, t2, 1
c->ld(t3, 48, sp); // ld t3, 48(sp)
c->daddu(t2, t2, t3); // daddu t2, t2, t3
c->mult3(t1, t1, t2); // mult3 t1, t1, t2
c->daddiu(t1, t1, 12); // daddiu t1, t1, 12
c->lwu(t2, 0, a0); // lwu t2, 0(a0)
c->daddu(t1, t1, t2); // daddu t1, t1, t2
c->sw(t1, 68, sp); // sw t1, 68(sp)
c->lwu(t1, 56, sp); // lwu t1, 56(sp)
c->lqc2(vf2, 0, t1); // lqc2 vf2, 0(t1)
c->lwu(t1, 68, sp); // lwu t1, 68(sp)
c->lqc2(vf5, 0, t1); // lqc2 vf5, 0(t1)
c->lwu(t1, 60, sp); // lwu t1, 60(sp)
c->lqc2(vf3, 0, t1); // lqc2 vf3, 0(t1)
c->lwu(t1, 64, sp); // lwu t1, 64(sp)
c->lqc2(vf4, 0, t1); // lqc2 vf4, 0(t1)
c->lwu(t1, 56, sp); // lwu t1, 56(sp)
c->lqc2(vf20, 32, t1); // lqc2 vf20, 32(t1)
c->lwu(t1, 68, sp); // lwu t1, 68(sp)
c->lqc2(vf23, 32, t1); // lqc2 vf23, 32(t1)
c->lwu(t1, 60, sp); // lwu t1, 60(sp)
c->lqc2(vf21, 32, t1); // lqc2 vf21, 32(t1)
c->lwu(t1, 64, sp); // lwu t1, 64(sp)
c->lqc2(vf22, 32, t1); // lqc2 vf22, 32(t1)
c->vmula_bc(DEST::xyzw, BC::x, vf2, vf20); // vmulax.xyzw acc, vf2, vf20
c->vmadda_bc(DEST::xyzw, BC::x, vf3, vf21); // vmaddax.xyzw acc, vf3, vf21
c->vmadda_bc(DEST::xyzw, BC::x, vf5, vf23); // vmaddax.xyzw acc, vf5, vf23
c->vmadd_bc(DEST::xyzw, BC::x, vf8, vf4, vf22); // vmaddx.xyzw vf8, vf4, vf22
c->vmul_bc(DEST::xyzw, BC::x, vf8, vf8, vf10); // vmulx.xyzw vf8, vf8, vf10
c->lwu(t1, 68, a0); // lwu t1, 68(a0)
c->daddiu(t1, t1, 12); // daddiu t1, t1, 12
c->addiu(t2, r0, 0); // addiu t2, r0, 0
//beq r0, r0, L185 // beq r0, r0, L185
// nop // sll r0, r0, 0
goto block_6; // branch always
block_3:
c->lqc2(vf11, 0, t1); // lqc2 vf11, 0(t1)
c->vsub(DEST::xyzw, vf13, vf8, vf11); // vsub.xyzw vf13, vf8, vf11
c->vmul(DEST::xyzw, vf9, vf13, vf13); // vmul.xyzw vf9, vf13, vf13
c->vmula_bc(DEST::xyzw, BC::x, vf1, vf9); // vmulax.xyzw acc, vf1, vf9
c->vmadda_bc(DEST::xyzw, BC::y, vf1, vf9); // vmadday.xyzw acc, vf1, vf9
c->vmadd_bc(DEST::xyzw, BC::z, vf14, vf1, vf9); // vmaddz.xyzw vf14, vf1, vf9
c->vadd_bc(DEST::w, BC::w, vf11, vf11, vf15); // vaddw.w vf11, vf11, vf15
c->vmul(DEST::xyzw, vf12, vf11, vf11); // vmul.xyzw vf12, vf11, vf11
c->vsub_bc(DEST::xyzw, BC::w, vf9, vf14, vf12); // vsubw.xyzw vf9, vf14, vf12
c->mov128_gpr_vf(t3, vf9); // qmfc2.i t3, vf9
bc = ((s64)c->sgpr64(t3)) >= 0; // bgez t3, L184
// nop // sll r0, r0, 0
if (bc) {goto block_5;} // branch non-likely
c->vrsqrt(vf11, BC::w, vf14, BC::x); // vrsqrt Q, vf11.w, vf14.x
c->vmax_bc(DEST::xyz, BC::x, vf20, vf0, vf20); // vmaxx.xyz vf20, vf0, vf20
c->vmax_bc(DEST::xyz, BC::x, vf21, vf0, vf21); // vmaxx.xyz vf21, vf0, vf21
c->vmax_bc(DEST::xyz, BC::x, vf22, vf0, vf22); // vmaxx.xyz vf22, vf0, vf22
c->vmax_bc(DEST::xyz, BC::x, vf23, vf0, vf23); // vmaxx.xyz vf23, vf0, vf23
c->vwaitq(); // vwaitq
// Unknown instr: vmulaq.xyzw acc, vf13, Q
c->vmula_q(DEST::xyzw, vf13);
c->vmsuba(DEST::xyzw, vf1, vf13); // vmsuba.xyzw acc, vf1, vf13
c->vmadd(DEST::xyzw, vf2, vf1, vf2); // vmadd.xyzw vf2, vf1, vf2
c->vmadd(DEST::xyzw, vf3, vf1, vf3); // vmadd.xyzw vf3, vf1, vf3
c->vmadd(DEST::xyzw, vf5, vf1, vf5); // vmadd.xyzw vf5, vf1, vf5
c->vmadd(DEST::xyzw, vf4, vf1, vf4); // vmadd.xyzw vf4, vf1, vf4
c->vmadd(DEST::xyzw, vf8, vf1, vf8); // vmadd.xyzw vf8, vf1, vf8
block_5:
c->daddiu(t1, t1, 16); // daddiu t1, t1, 16
c->daddiu(t2, t2, 1); // daddiu t2, t2, 1
block_6:
c->slt(t3, t2, v1); // slt t3, t2, v1
bc = c->sgpr64(t3) != 0; // bne t3, r0, L183
// nop // sll r0, r0, 0
if (bc) {goto block_3;} // branch non-likely
c->mov64(t1, s7); // or t1, s7, r0
c->mov64(t1, s7); // or t1, s7, r0
c->lwu(t1, 56, sp); // lwu t1, 56(sp)
c->sqc2(vf2, 0, t1); // sqc2 vf2, 0(t1)
c->lwu(t1, 68, sp); // lwu t1, 68(sp)
c->sqc2(vf5, 0, t1); // sqc2 vf5, 0(t1)
c->lwu(t1, 60, sp); // lwu t1, 60(sp)
c->sqc2(vf3, 0, t1); // sqc2 vf3, 0(t1)
c->lwu(t1, 64, sp); // lwu t1, 64(sp)
c->sqc2(vf4, 0, t1); // sqc2 vf4, 0(t1)
c->mov128_gpr_vf(t1, vf4); // qmfc2.i t1, vf4
c->ld(t1, 48, sp); // ld t1, 48(sp)
c->daddiu(t1, t1, 1); // daddiu t1, t1, 1
c->sd(t1, 48, sp); // sd t1, 48(sp)
c->daddiu(t0, t0, 1); // daddiu t0, t0, 1
block_8:
c->slt(t1, t0, a1); // slt t1, t0, a1
bc = c->sgpr64(t1) != 0; // bne t1, r0, L182
// nop // sll r0, r0, 0
if (bc) {goto block_2;} // branch non-likely
c->mov64(t0, s7); // or t0, s7, r0
c->mov64(t0, s7); // or t0, s7, r0
c->ld(t0, 48, sp); // ld t0, 48(sp)
c->daddiu(t0, t0, 1); // daddiu t0, t0, 1
c->sd(t0, 48, sp); // sd t0, 48(sp)
c->daddiu(a3, a3, 1); // daddiu a3, a3, 1
block_10:
c->slt(t0, a3, a2); // slt t0, a3, a2
bc = c->sgpr64(t0) != 0; // bne t0, r0, L181
// nop // sll r0, r0, 0
if (bc) {goto block_1;} // branch non-likely
c->mov64(v1, s7); // or v1, s7, r0
c->mov64(v0, s7); // or v0, s7, r0
//jr ra // jr ra
c->daddiu(sp, sp, 80); // daddiu sp, sp, 80
goto end_of_function; // return
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
// nop // sll r0, r0, 0
end_of_function:
return c->gprs[v0].du64[0];
}
void link() {
gLinkedFunctionTable.reg("(method 21 cloth-system)", execute, 256);
}
} // namespace method_21_cloth_system
} // namespace Mips2C
+10
View File
@@ -905,6 +905,16 @@ struct ExecutionContext {
}
}
void vmula_q(DEST mask, int src0) {
auto s0 = vf_src(src0);
for (int i = 0; i < 4; i++) {
if ((u64)mask & (1 << i)) {
acc.f[i] = s0.f[i] * Q;
}
}
}
void vadda_bc(DEST mask, BC bc, int src0, int src1) {
auto s0 = vf_src(src0);
auto s1 = vf_src(src1);
+3 -1
View File
@@ -393,6 +393,7 @@ namespace shadow_add_double_edges { extern void link(); }
namespace shadow_add_single_tris { extern void link(); }
namespace shadow_add_double_tris { extern void link(); }
namespace shadow_execute { extern void link(); }
namespace method_21_cloth_system { extern void link(); }
}
// clang-format on
@@ -654,7 +655,8 @@ PerGameVersion<std::unordered_map<std::string, std::vector<void (*)()>>> gMips2C
jak3::shadow_find_single_edges::link, jak3::shadow_find_double_edges::link,
jak3::shadow_add_verts::link, jak3::shadow_add_facing_single_tris::link,
jak3::shadow_add_single_edges::link, jak3::shadow_add_double_edges::link,
jak3::shadow_add_single_tris::link, jak3::shadow_add_double_tris::link}}}};
jak3::shadow_add_single_tris::link, jak3::shadow_add_double_tris::link}},
{"cloth", {jak3::method_21_cloth_system::link}}}};
void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 stack_size) {
const auto& it = m_executes.insert({name, {exec, Ptr<u8>()}});
+3
View File
@@ -460,6 +460,9 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
snd_SetPlayBackMode(0);
}
} break;
case Jak2SoundCommand::mirror_mode: {
gMirrorMode = cmd->mirror.value;
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Loader command {}", int(cmd->j2command)));
}
+1
View File
@@ -58,6 +58,7 @@ enum class Jak2SoundCommand : u16 {
iop_mem = 48,
cancel_dgo = 49,
set_stereo_mode = 50,
mirror_mode = 201,
};
struct SoundRpcCommand {
+1 -1
View File
@@ -30,7 +30,7 @@ void StopVAG(VagCmd* cmd, int /*param_2*/);
enum VolumeCategory {
DIALOGUE = 2, // VAG streams. Copied "dialogue" name from jak 1.
};
int MasterVolume[17];
int MasterVolume[32];
void vag_init_globals() {
memset(VagCmds, 0, sizeof(VagCmds));
+1 -1
View File
@@ -113,7 +113,7 @@ extern int TrapSRAM[N_VAG_CMDS];
extern int StreamVoice[N_VAG_CMDS];
extern int ActiveVagStreams;
extern int MasterVolume[17];
extern int MasterVolume[32];
void vag_init_globals();
+333
View File
@@ -0,0 +1,333 @@
#include "basefile.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/jak3/overlord.h"
#include "game/overlord/jak3/pagemanager.h"
#include "game/overlord/jak3/vag.h"
namespace jak3 {
void jak3_overlord_init_globals_basefile() {}
/*!
* Construct a CBaseFile in an unused state.
*/
CBaseFile::CBaseFile() {
m_Buffer.m_pCurrentData = nullptr;
m_Buffer.m_pCurrentPageStart = nullptr;
m_Buffer.m_nMinNumPages = 1;
m_Buffer.m_nMaxNumPages = kDefaultBufferPageCount;
m_Buffer.m_nDataLength = 0;
m_Buffer.m_pPageList = nullptr;
m_Buffer.m_pIsoCmd = nullptr;
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
m_ProcessDataSemaphore = -1;
m_FileDef = nullptr;
m_FileKind = Kind::UNKNOWN;
m_Status = EIsoStatus::NONE_0;
m_ReadRate = 0;
m_LengthPages = 0;
m_PageOffset = 0;
m_nNumPages = kDefaultBufferPageCount;
}
/*!
* Construct a CBaseFile for a given file, but keep it in the "idle" state, with no buffer
* allocated.
*/
CBaseFile::CBaseFile(const jak3::ISOFileDef* file, int semaphore) {
m_Buffer.m_pCurrentData = nullptr;
m_Buffer.m_nMaxNumPages = kDefaultBufferPageCount;
m_Buffer.m_nDataLength = 0;
m_Buffer.m_nMinNumPages = 1;
m_Buffer.m_pPageList = nullptr;
m_Buffer.m_pIsoCmd = nullptr;
m_Buffer.m_pCurrentPageStart = nullptr;
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
m_nNumPages = kDefaultBufferPageCount;
m_FileDef = file;
m_ProcessDataSemaphore = semaphore;
m_FileKind = Kind::UNKNOWN;
m_Status = EIsoStatus::IDLE_1;
m_ReadRate = 0;
m_LengthPages = 0;
m_PageOffset = 0;
}
/*!
* Update our buffer to handle crossing page boundaries, return pointer to next data.
*/
uint8_t* CBaseFile::CheckPageBoundary() {
// Can't check page boundary if the buffer is not allocated.
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
// Can't check page boundary if there is no manager
ASSERT(m_Buffer.m_pPageList);
CPageList* page_list = m_Buffer.m_pPageList;
CPage* page = page_list->m_pCurrentActivePage;
// can only return data if there's an active page
if (!page || page_list->m_nNumActivePages <= 0) {
return nullptr;
}
// buffer doesn't know the current page, just reset it to the start of the active page.
if (m_Buffer.m_pCurrentPageStart == nullptr) {
m_Buffer.m_pCurrentData = page->m_pPageMemStart;
m_Buffer.m_pCurrentPageStart = page->m_pPageMemStart;
} else {
uint8_t* end_ptr = page->m_pPageMemEnd;
// check if our data pointer crossed the page boundary
if (end_ptr <= m_Buffer.m_pCurrentData) {
// it did!
uint8_t* past_boundary_ptr = m_Buffer.m_pCurrentData;
// get the next active page
CPage* next_page = page_list->StepActivePage();
if (!next_page) {
// no more active pages, no data to process
m_Buffer.m_pCurrentPageStart = nullptr;
m_Buffer.m_pCurrentData = nullptr;
ovrld_log(LogCategory::PAGING, "File {} ran out of pages in CheckPageBoundary",
m_FileDef->name.data);
} else {
// this is a little weird, but if we went past the end of the previous page, we actually
// start at an offset into the next page - perhaps the user could know that pages are
// consecutive in memory?
uint8_t* new_page_mem = next_page->m_pPageMemStart;
m_Buffer.m_pCurrentData = new_page_mem + (end_ptr + 1 - past_boundary_ptr);
m_Buffer.m_pCurrentPageStart = new_page_mem;
ovrld_log(LogCategory::PAGING, "File {} advanced to next page (wrapped {} bytes)",
m_FileDef->name.data, (end_ptr + 1 - past_boundary_ptr));
}
}
}
return m_Buffer.m_pCurrentData;
}
/*!
* Allocate pages and set up the CBuffer for the given ISO Msg and type.
*/
int CBaseFile::InitBuffer(CBuffer::BufferType type, jak3::ISO_Hdr* msg) {
ASSERT(msg);
m_Buffer.m_pCurrentData = nullptr;
m_Buffer.m_pPageList = nullptr;
m_Buffer.m_pIsoCmd = msg;
m_Buffer.m_pCurrentPageStart = nullptr;
m_Buffer.m_nDataLength = 0;
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
m_Buffer.m_nMinNumPages = 1;
m_Buffer.m_nMaxNumPages = kDefaultBufferPageCount;
m_nNumPages = 4;
// adjust size based on the request buffer type
switch (type) {
case CBuffer::BufferType::REQUEST_NORMAL: { // 3
m_Buffer.m_pCurrentData = nullptr;
m_Buffer.m_nMinNumPages = 1;
m_Buffer.m_nMaxNumPages = 4;
m_nNumPages = 4;
m_Buffer.m_nDataLength = 0;
m_Buffer.m_pCurrentPageStart = nullptr;
m_Buffer.m_eBufferType = CBuffer::BufferType::NORMAL; // 1
// and the iso msg request.
switch (msg->msg_type) {
case ISO_Hdr::MsgType::LOAD_EE:
case ISO_Hdr::MsgType::LOAD_IOP:
case ISO_Hdr::MsgType::LOAD_EE_CHUNK:
m_Buffer.m_nMinNumPages = 1;
m_nNumPages = 4;
m_Buffer.m_nMaxNumPages = 4;
break;
case ISO_Hdr::MsgType::LOAD_SOUNDBANK:
m_Buffer.m_nMinNumPages = 1;
m_Buffer.m_nMaxNumPages = 2;
break;
default:
break;
}
} break;
case CBuffer::BufferType::REQUEST_VAG: {
m_Buffer.m_pCurrentData = nullptr;
m_Buffer.m_eBufferType = CBuffer::BufferType::VAG;
m_Buffer.m_nMinNumPages = 1;
m_nNumPages = 0x10;
m_Buffer.m_nDataLength = 0;
m_Buffer.m_pCurrentPageStart = nullptr;
m_Buffer.m_nMaxNumPages = 0x10;
} break;
default:
ASSERT_NOT_REACHED(); // bad buffer type
}
ovrld_log(LogCategory::PAGING, "File {} initializing buffer ({} pages {} min {} max)",
m_FileDef->name.data, m_nNumPages, m_Buffer.m_nMinNumPages, m_Buffer.m_nMaxNumPages);
// Actual allocation of page data
CPageList* page_list = AllocPages();
if (page_list) {
// set up the current pointers of the buffer.
m_Buffer.m_pCurrentPageStart = page_list->m_pFirstPage->m_pPageMemStart;
m_Buffer.m_pCurrentData = m_Buffer.m_pCurrentPageStart;
return 1;
} else {
ovrld_log(LogCategory::WARN, "File {} failed to allocate a page list.", m_FileDef->name.data);
// if it failed, terminate the buffer
TerminateBuffer();
return 0;
}
}
/*!
* Free page memory, clear event flags for cpages.
*/
void CBaseFile::TerminateBuffer() {
ovrld_log(LogCategory::PAGING, "File {} terminating buffer.", m_FileDef->name.data);
auto buffer_type = m_Buffer.m_eBufferType;
if (buffer_type != CBuffer::BufferType::EBT_FREE) {
// clean up our pages
auto* page_list = m_Buffer.m_pPageList;
if (page_list) {
// stop try to load
page_list->CancelActivePages();
}
switch (buffer_type) {
case CBuffer::BufferType::EBT_FREE:
case CBuffer::BufferType::NORMAL:
case CBuffer::BufferType::VAG:
case CBuffer::BufferType::REQUEST_NORMAL:
case CBuffer::BufferType::REQUEST_VAG:
// return memory
FreePages();
// reset our buffer
m_Buffer.m_nDataLength = 0;
m_Buffer.m_pCurrentPageStart = 0;
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
// reset our command
if (buffer_type != CBuffer::BufferType::EBT_FREE) {
ASSERT(m_Buffer.m_pIsoCmd);
m_Buffer.m_pIsoCmd = nullptr;
}
break;
default:
ASSERT_NOT_REACHED(); // Invalid buffer type
}
} else {
ASSERT_NOT_REACHED(); // Buffer already terminated, shouldn't happen again.
}
}
/*!
* Allocate CPage and CPageList as needed to reach the target number of pages.
*/
CPageList* CBaseFile::AllocPages() {
// should have picked a buffer type
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
if (m_Buffer.m_eBufferType == CBuffer::BufferType::EBT_FREE) {
return nullptr;
}
// We might have a PageList or not
auto* old_plist = m_Buffer.m_pPageList;
int old_unstepped_pages = 0;
bool have_plist = old_plist != nullptr;
CPageList* ret_plist = nullptr;
// if we do have a pagelist, we'll reuse it and any unstepped pages.
if (have_plist) {
old_unstepped_pages = old_plist->m_nNumUnsteppedPages;
ret_plist = old_plist;
}
int page_alloc_count = 0;
// to increase unstepped pages to the target m_nNumPages, we need to allocate this many more.
page_alloc_count = m_nNumPages - old_unstepped_pages;
// lg::warn("page counts in AllocPages: {} {}", m_nNumPages, old_unstepped_pages);
if (old_plist) {
// lg::warn(" {} {}", old_plist->m_nNumPages, old_plist->m_nNumActivePages);
}
if (old_unstepped_pages < (int)m_nNumPages) {
// alloc count will be positive.
// reduce allocation count to at most the number of free pages
if (get_page_manager()->m_CCache.m_nNumFreePages < page_alloc_count) {
page_alloc_count = get_page_manager()->m_CCache.m_nNumFreePages;
}
} else {
// alloc count would be negative, don't allocate
page_alloc_count = 0;
}
switch (m_Buffer.m_eBufferType) {
case CBuffer::BufferType::NORMAL:
case CBuffer::BufferType::REQUEST_NORMAL: {
// don't do anything special
} break;
case CBuffer::BufferType::VAG:
case CBuffer::BufferType::REQUEST_VAG: {
// for VAG commands, we don't bother buffering more than the DMA transfer size
int dma_xfer_size = ((ISO_VAGCommand*)m_Buffer.m_pIsoCmd)->xfer_size;
if (dma_xfer_size) {
ASSERT(dma_xfer_size > 0);
int limit = (dma_xfer_size + 0x7fff) >> 0xf;
if (page_alloc_count > limit) {
ovrld_log(LogCategory::WARN, "File {} wants {} pages, but VAG DMA sizes limit us to ",
m_FileDef->name.data, page_alloc_count, limit);
lg::info("page count dma limit {} -> {}\n", page_alloc_count, limit);
page_alloc_count = limit;
}
}
} break;
default:
ASSERT_NOT_REACHED(); // bad buffer type.
}
if (page_alloc_count == 0) {
return ret_plist;
}
ovrld_log(LogCategory::PAGING, "File {} wants {} more pages in AllocPages.", m_FileDef->name.data,
page_alloc_count);
if (have_plist) {
ret_plist = get_page_manager()->GrowPageList(m_Buffer.m_pPageList, page_alloc_count);
} else {
ret_plist = get_page_manager()->AllocPageList(page_alloc_count, 0);
}
if (ret_plist) {
m_Buffer.m_pPageList = ret_plist;
if (!have_plist) {
m_Buffer.m_pCurrentData = ret_plist->m_pFirstPage->m_pPageMemStart;
}
} else {
ASSERT_NOT_REACHED(); // might be ok.
}
return ret_plist;
}
/*!
* Free all pages and the page list.
*/
void CBaseFile::FreePages() {
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
if (m_Buffer.m_pPageList) {
get_page_manager()->FreePageList(m_Buffer.m_pPageList);
}
m_Buffer.m_pPageList = nullptr;
}
} // namespace jak3
+76
View File
@@ -0,0 +1,76 @@
#pragma once
#include "common/common_types.h"
#include "game/overlord/jak3/isocommon.h"
#include "game/overlord/jak3/pagemanager.h"
namespace jak3 {
void jak3_overlord_init_globals_basefile();
struct CPageList;
struct ISOFileDef;
struct ISO_Hdr;
constexpr int kDefaultBufferPageCount = 4;
/*!
* Base class for a file that the ISO system is processing.
* This represents an "open" file, and contains references to the buffer holding this file's data
*/
struct CBaseFile {
CBaseFile();
CBaseFile(const ISOFileDef* file, int semaphore);
virtual ~CBaseFile() = default;
uint8_t* CheckPageBoundary();
int InitBuffer(CBuffer::BufferType type, ISO_Hdr* msg);
CPageList* AllocPages();
void TerminateBuffer();
void FreePages();
// buffer that stores some contents of this file
CBuffer m_Buffer;
// the number of pages that were allocated for reading this file.
u32 m_nNumPages;
// Metadata about the file
const ISOFileDef* m_FileDef;
// The compression format used on the file
enum class Kind {
UNKNOWN = 0,
NORMAL = 1,
LZO_COMPRESSED = 2,
} m_FileKind = Kind::UNKNOWN;
EIsoStatus m_Status = EIsoStatus::NONE_0;
// The expected read rate for streaming, used to prioritize CD reads. Can be 0 if unknown/not
// applicable.
int m_ReadRate = 0;
// Number of sectors that we should read in total, decided based on the file size and request from
// user when they opened this file.
int m_LengthPages = 0; // really, in pages...
// The current offset. (todo: is this for data we read, processed?)
int m_PageOffset = 0;
// Semaphore that we should wait on before handing new data to the process callback.
// Set to -1 if there is no semaphore.
// (this is a bit of hack, only used for VAG streaming).
int m_ProcessDataSemaphore = 0;
// virtual methods
virtual EIsoStatus BeginRead() = 0;
virtual EIsoStatus SyncRead() = 0;
virtual void Close() = 0;
virtual int RecoverPages(int num_pages) = 0;
virtual int GetSector() = 0;
// ??
// ??
// ??
};
} // namespace jak3
+26
View File
@@ -0,0 +1,26 @@
#include "basefilesystem.h"
#include "common/util/Assert.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak3 {
void jak3_overlord_init_globals_basefilesystem() {}
CBaseFileSystem::CBaseFileSystem() {
for (auto& sema : m_Sema) {
sema = -1;
SemaParam param;
param.max_count = 1;
param.attr = 0;
param.init_count = 1;
param.option = 0;
sema = CreateSema(&param);
if (sema < 0) {
ASSERT_NOT_REACHED();
}
}
}
} // namespace jak3
+33
View File
@@ -0,0 +1,33 @@
#pragma once
namespace jak3 {
void jak3_overlord_init_globals_basefilesystem();
struct ISOFileDef;
struct ISOName;
struct CBaseFile;
struct VagDirEntry;
constexpr int kMaxOpenFiles = 16;
/*!
* Base class for "FileSystem", which supports finding and opening files.
* The only implementation we have is CISOCDFileSystem
*/
struct CBaseFileSystem {
CBaseFileSystem();
virtual ~CBaseFileSystem() = default;
// semaphores for processing open files
int m_Sema[kMaxOpenFiles];
virtual int Init() = 0;
// polldrive
virtual ISOFileDef* Find(const char* name) = 0;
virtual ISOFileDef* FindIN(const ISOName* name) = 0;
virtual int GetLength(const ISOFileDef* file) = 0;
virtual CBaseFile* Open(const ISOFileDef* file_def, int sector_offset, int file_kind) = 0;
virtual CBaseFile* OpenWAD(const ISOFileDef* file_def, int page_offset) = 0;
virtual VagDirEntry* FindVAGFile(const char* name) = 0;
};
} // namespace jak3
+526
View File
@@ -0,0 +1,526 @@
#include "dma.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/jak3/basefile.h"
#include "game/overlord/jak3/overlord.h"
#include "game/overlord/jak3/vag.h"
#include "game/sce/iop.h"
#include "game/sound/sdshim.h"
#include "game/sound/sndshim.h"
#define VOICE_BIT(voice) (1 << ((voice) >> 1))
namespace jak3 {
using namespace iop;
namespace {
// most recent call to voice_trans_wrapper's arguments
u32 g_voiceTransMode = 0;
u32 g_voiceTransSize = 0;
s16 g_voiceTransChannel = 0;
const void* g_voiceTransAddr = nullptr;
u32 g_voiceTransSpuAddr = 0;
// if we've started a transfer recently
bool g_voiceTransRunning = false;
// when that transfer was started
u32 g_voiceTransTime = 0;
// despite the name, this is really an indicator that the SPU streaming system is waiting
// for a SPU interrupt on completion.
bool g_bSpuDmaBusy = false;
int g_nSpuDmaChannel = 0;
ISO_VAGCommand* g_pDmaVagCmd = nullptr;
ISO_VAGCommand* g_pDmaStereoVagCmd = nullptr;
int g_nSpuDmaChunks = 0;
std::array<DmaQueueEntry, 16> g_aSpuDmaQueue;
int g_nSpuDmaQueueHead = 0;
int g_nSpuDmaQueueTail = 0;
int g_nSpuDmaQueueCount = 0;
struct DmaInterruptHandlerHack {
s32 chan = 0;
sceSdTransIntrHandler cb = nullptr;
void* data;
int countdown = 0;
} g_DmaInterruptHack;
} // namespace
void jak3_overlord_init_globals_dma() {
g_voiceTransMode = 0;
g_voiceTransSize = 0;
g_voiceTransChannel = 0;
g_voiceTransAddr = nullptr;
g_voiceTransSpuAddr = 0;
g_voiceTransRunning = false;
g_voiceTransTime = 0;
g_bSpuDmaBusy = false;
g_nSpuDmaChannel = 0;
g_pDmaVagCmd = nullptr;
g_pDmaStereoVagCmd = nullptr;
g_nSpuDmaChunks = 0;
g_aSpuDmaQueue = {};
g_nSpuDmaQueueHead = 0;
g_nSpuDmaQueueCount = 0;
g_nSpuDmaQueueTail = 0;
g_DmaInterruptHack = {};
}
// The DMA callback hack below is used to defer dma completion "interrupts" until the next run
// of the ISO Thread. This avoids re-entry type problems where the original design would set off
// a dma transfer in the completion handler of the previous transfer, and expect a few instructions
// to run after.
void uninstall_dma_intr() {
g_DmaInterruptHack = {};
}
void set_dma_intr_handler_hack(s32 chan, sceSdTransIntrHandler cb, void* data) {
ASSERT(!g_DmaInterruptHack.cb);
g_DmaInterruptHack.chan = chan;
g_DmaInterruptHack.cb = cb;
g_DmaInterruptHack.data = data;
g_DmaInterruptHack.countdown = 10;
}
int SPUDmaIntr(int channel, void* userdata);
void dma_intr_hack() {
if (g_DmaInterruptHack.countdown) {
g_DmaInterruptHack.countdown--;
if (g_DmaInterruptHack.countdown == 0) {
int chan = g_DmaInterruptHack.chan;
void* data = g_DmaInterruptHack.data;
g_DmaInterruptHack = {};
SPUDmaIntr(chan, data);
}
}
}
/*!
* This function is used to set up a DMA transfer to SPU DMA.
*
* This wrapper was added very close to the end of Jak 3's development.
*
* I believe it basically checks for dma transfers that are somehow "dropped", and retries them.
* Since I don't think our IOP framework will ever do this, we have an assert if the dropped logic
* ever goes off.
*/
int voice_trans_wrapper(s16 chan, u32 mode, const void* iop_addr, u32 spu_addr, u32 size) {
// remember the transfer settings. If there's a transfer in progress, so we can't start here,
// we'll use these to start the transfer later.
g_voiceTransMode = mode;
g_voiceTransSize = size;
g_voiceTransChannel = chan;
g_voiceTransAddr = iop_addr;
g_voiceTransSpuAddr = spu_addr;
if (g_voiceTransRunning) {
// I claim this should never happen, and this is their workaround for a bug.
ASSERT_NOT_REACHED();
return -0xd2; // busy
} else {
g_voiceTransRunning = true;
g_voiceTransTime = GetSystemTimeLow();
return sceSdVoiceTrans(chan, mode, iop_addr, spu_addr, size);
}
}
u32 read_rate_calc(u32 pitch) {
u64 pitch1 = (pitch >> 3);
u64 mult_result = pitch1 * 0x2492'4925ull;
return mult_result >> 32;
}
/*!
* The worst function of all time - the SPU DMA completion interrupt.
*/
int SPUDmaIntr(int channel, void* userdata) {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr enter! {} 0x{:x}", channel, (u64)userdata);
if (!g_bSpuDmaBusy) {
// we got an interrupt, but weren't expecting it, or no longer have the need for the data.
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr exit - not busy");
return 0;
}
if (channel != g_nSpuDmaChannel) {
// interrupt was for the wrong channel, somehow.
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr exit - not our channel ??");
return 0;
}
// since we're in the completion handler, we know that there is no voice trans (SPU DMA) running.
g_voiceTransRunning = false;
// This next block will handle updating the playback command that triggered this dma:
if (g_pDmaVagCmd) {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDma for cmd {}", g_pDmaVagCmd->name);
if (!g_pDmaStereoVagCmd) {
// non-stereo audio
// set a flag to indicate even/odd number of chunks have been dma'd
if ((g_nSpuDmaChunks & 1) == 0) {
g_pDmaVagCmd->flags.dma_complete_even_chunk_count = 1;
} else {
g_pDmaVagCmd->flags.dma_complete_odd_chunk_count = 1;
}
} else {
// stereo audio. This requires two uploads, one for left/right audio. If we've finished the
// first, start the second one here:
if (g_pDmaStereoVagCmd->xfer_size) {
// parameters for second upload
int chan = g_pDmaVagCmd->dma_chan;
const u8* iop_addr = g_pDmaStereoVagCmd->dma_iop_mem_ptr;
int size = g_pDmaStereoVagCmd->xfer_size;
// SPU addr - toggle the buffer based on stereo side:
// TODO: better explanation of why this picks the correct buffer.
int spu_addr;
if ((g_nSpuDmaChunks & 1) == 0) {
spu_addr = g_pDmaStereoVagCmd->stream_sram;
} else {
spu_addr = g_pDmaStereoVagCmd->stream_sram + 0x2000;
}
// these lines reordered to possibly support immediate dma completion callback??
// clear flag so we know not to transfer the next part
g_pDmaStereoVagCmd->xfer_size = 0;
g_pDmaStereoVagCmd->dma_iop_mem_ptr = nullptr;
// start next transfer
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr starting stereo sibling transfer");
set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, userdata);
voice_trans_wrapper(chan, 0, iop_addr, spu_addr, size);
return 0;
}
// second stereo upload completed - update double-buffering flags
if ((g_nSpuDmaChunks & 1) == 0) {
g_pDmaVagCmd->flags.dma_complete_even_chunk_count = 1;
g_pDmaStereoVagCmd->flags.dma_complete_even_chunk_count = 1;
} else {
g_pDmaVagCmd->flags.dma_complete_odd_chunk_count = 1;
g_pDmaStereoVagCmd->flags.dma_complete_odd_chunk_count = 1;
}
}
// if this is the first chunk, we'll start the actual audio here:
// lg::warn("----------> interrupt with chunks {}\n", g_nSpuDmaChunks);
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks count {}", g_nSpuDmaChunks);
if (g_nSpuDmaChunks == 0) {
// compute pitch/playback rate
int pitch = CalculateVAGPitch(g_pDmaVagCmd->pitch1, g_pDmaVagCmd->pitch_cmd);
ASSERT(pitch == (pitch & 0xffff));
// inform the ISO system how fast we're reading
if (g_pDmaVagCmd->m_pBaseFile) {
// unlike actual playback, this is done with the pitch1 value from the file itself - so if
// we speed up/slow down stuff in debug, it won't change streaming modes
const int pitch_from_file =
CalculateVAGPitch(g_pDmaVagCmd->pitch1_file, g_pDmaVagCmd->pitch_cmd);
int rate = g_pDmaStereoVagCmd ? pitch_from_file * 0x2ee : pitch_from_file * 0x177;
g_pDmaVagCmd->m_pBaseFile->m_ReadRate = read_rate_calc(rate);
}
// start!
u32 voice_mask = 0;
if (!g_pDmaStereoVagCmd) {
// forget any previous spu address
g_pDmaVagCmd->current_spu_address = 0;
static_assert(SD_VA_SSA == 0x2040);
static_assert(SD_S_KOFF == 0x1600);
static_assert(SD_S_KON == 0x1500);
static_assert(SD_VP_ADSR1 == 0x300);
static_assert(SD_VP_ADSR2 == 0x400);
static_assert(SD_VP_PITCH == 0x200);
// before touching SPU2 hardware, wait for voice safety:
BlockUntilVoiceSafe(g_pDmaVagCmd->voice, 0x900);
// set address and ADSR settings
sceSdSetAddr(g_pDmaVagCmd->voice | SD_VA_SSA, g_pDmaVagCmd->stream_sram + 0x30);
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR1, 0xff);
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR2, 0x1fc0);
if (g_pDmaVagCmd->flags.paused) {
pitch = 0;
}
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, pitch);
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice);
} else {
// forget any previous spu address
g_pDmaVagCmd->current_spu_address = 0;
g_pDmaStereoVagCmd->current_spu_address = 0;
// wait for voices to be safe to adjust
BlockUntilVoiceSafe(g_pDmaVagCmd->voice, 0x900);
BlockUntilVoiceSafe(g_pDmaStereoVagCmd->voice, 0x900);
// set voice params
sceSdSetAddr(g_pDmaVagCmd->voice | SD_VA_SSA, g_pDmaVagCmd->stream_sram + 0x30);
sceSdSetAddr(g_pDmaStereoVagCmd->voice | SD_VA_SSA, g_pDmaStereoVagCmd->stream_sram + 0x30);
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR1, 0xff);
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_ADSR1, 0xff);
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR2, 0x1fc0);
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_ADSR2, 0x1fc0);
if (g_pDmaVagCmd->flags.paused) {
pitch = 0;
}
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, pitch);
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_PITCH, pitch);
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice) | VOICE_BIT(g_pDmaStereoVagCmd->voice);
}
// do key-on or key-off
if (g_pDmaVagCmd->flags.paused) {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 0, key off");
BlockUntilAllVoicesSafe();
sceSdSetSwitch(SD_S_KOFF | (g_pDmaVagCmd->voice & 1), voice_mask);
} else {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 0, key on");
BlockUntilAllVoicesSafe();
sceSdSetSwitch(SD_S_KON | (g_pDmaVagCmd->voice & 1), voice_mask);
}
// remember the time of the key-on/off. This is used to avoid sending voice commands
// quickly, which somehow confuses the sound hardware.
auto sys_time = GetSystemTimeLow();
MarkVoiceKeyedOnOff(g_pDmaVagCmd->voice, sys_time);
if (g_pDmaStereoVagCmd) {
MarkVoiceKeyedOnOff(g_pDmaStereoVagCmd->voice, sys_time);
}
} else if (g_nSpuDmaChunks == 1) {
g_pDmaVagCmd->flags.saw_chunks1 = 1;
if (g_pDmaStereoVagCmd) {
g_pDmaStereoVagCmd->flags.saw_chunks1 = 1;
}
if (g_pDmaVagCmd->flags.paused) {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 1, pausing");
u32 voice_mask = 0;
if (!g_pDmaStereoVagCmd) {
// pause by setting pitches to 0
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, 0);
BlockUntilVoiceSafe(VOICE_BIT(g_pDmaVagCmd->voice), 0x900);
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice);
} else {
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_PITCH, 0);
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, 0);
BlockUntilVoiceSafe(VOICE_BIT(g_pDmaVagCmd->voice), 0x900);
BlockUntilVoiceSafe(VOICE_BIT(g_pDmaStereoVagCmd->voice), 0x900);
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice) | VOICE_BIT(g_pDmaStereoVagCmd->voice);
}
// switch off
BlockUntilAllVoicesSafe();
sceSdSetSwitch(SD_S_KOFF | (g_pDmaVagCmd->voice & 1), voice_mask);
auto sys_time = GetSystemTimeLow();
MarkVoiceKeyedOnOff(g_pDmaVagCmd->voice, sys_time);
if (g_pDmaStereoVagCmd) {
MarkVoiceKeyedOnOff(g_pDmaStereoVagCmd->voice, sys_time);
}
} else {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 1, unpausing by call to UnPauseVAG");
g_pDmaVagCmd->flags.paused = 1;
UnPauseVAG(g_pDmaVagCmd);
}
}
// now that we've processed the command from this interrupt, mark it as safe to modify
g_pDmaVagCmd->safe_to_modify_dma = 1;
if (g_pDmaStereoVagCmd) {
g_pDmaStereoVagCmd->safe_to_modify_dma = 1;
}
// and forget it!
g_pDmaVagCmd = nullptr;
g_pDmaStereoVagCmd = nullptr;
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr dma handling of VAG cmd is complete");
}
// release ref on this page. (interestingly, not a dma ref...)
if (userdata) {
CPage* page = (CPage*)userdata;
int ret = page->ReleaseRef();
ASSERT(ret >= 0);
}
// now - see if we have another queued dma transfer
ASSERT(g_nSpuDmaQueueCount >= 0);
if (g_nSpuDmaQueueCount == 0) {
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr dma queue is empty, disabling interrupt");
// we're done!
// set_dma_intr_handler_hack(channel, nullptr, nullptr);
uninstall_dma_intr();
// if (-1 < channel) {
// snd_FreeSPUDMA(channel);
// }
g_bSpuDmaBusy = false;
} else {
ovrld_log(LogCategory::SPU_DMA_STR,
"SPUDmaIntr dma queue is not empty, preparing to run {} ({} pending)",
g_nSpuDmaQueueHead, g_nSpuDmaQueueCount);
// nope, more dma to run
auto* next_xfer = &g_aSpuDmaQueue[g_nSpuDmaQueueHead];
// set up the next interrupt handler
set_dma_intr_handler_hack(channel, SPUDmaIntr, next_xfer->user_data);
// args for the dma transfer
int next_chan = channel;
int next_mode = 0;
const void* next_iop = next_xfer->iop_mem;
u32 next_spu = next_xfer->spu_addr;
u32 next_length = next_xfer->length;
// load up the commands to handle
g_pDmaVagCmd = next_xfer->command;
g_pDmaStereoVagCmd = nullptr;
if (g_pDmaVagCmd) {
g_pDmaStereoVagCmd = g_pDmaVagCmd->stereo_sibling;
}
g_nSpuDmaChunks = next_xfer->num_isobuffered_chunks;
// advance the queue!
g_nSpuDmaQueueCount = g_nSpuDmaQueueCount + -1;
g_nSpuDmaQueueHead = g_nSpuDmaQueueHead + 1;
if (0xf < g_nSpuDmaQueueHead) {
g_nSpuDmaQueueHead = 0;
}
// start the next one!
// set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, userdata);
voice_trans_wrapper(next_chan, next_mode, next_iop, next_spu, next_length);
}
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr exit - end of function");
return 0;
}
/*!
* Start DMA to EE.
*/
void DMA_SendToEE(void* ee_dest,
const void* iop_src,
u32 length,
void callback(void*),
void* callback_arg) {
ASSERT(iop_src);
ASSERT(ee_dest);
ASSERT(((uintptr_t)iop_src & 3) == 0);
ASSERT(((uintptr_t)ee_dest & 0xf) == 0);
ASSERT(length < 0xffff0);
sceSifDmaData cmd; // DMA settings
// setup command
cmd.mode = 0;
cmd.data = iop_src;
cmd.addr = ee_dest;
cmd.size = length;
// instant DMA
// ovrld_log(LogCategory::EE_DMA, "DMA_SendToEE: 0x{:x}, size {}", (u64)ee_dest, length);
sceSifSetDma(&cmd, 1);
// for now, we'll do the callback here, but I bet it will cause problems
if (callback) {
callback(callback_arg);
}
}
/*!
* Start DMA transfer to SPU. Despite the name, this does not actually "sync" - the transfer will
* be ongoing. If there is an ongoing transfer when this is called, the transfer will be queued.
*/
int DMA_SendToSPUAndSync(const u8* iop_mem,
int length,
int spu_addr,
ISO_VAGCommand* cmd,
void* user_data) {
// CpuSuspendIntr(local_28);
int ret = 1;
bool defer = false;
ovrld_log(LogCategory::SPU_DMA_STR,
"DMA to SPU requested for {}, {} bytes to 0x{:x}, currently busy? {}",
cmd ? cmd->name : "NO-CMD", length, spu_addr, g_bSpuDmaBusy);
if (g_bSpuDmaBusy == 0) {
// not busy, we can actually start dma now.
g_nSpuDmaChannel = snd_GetFreeSPUDMA();
if (g_nSpuDmaChannel == -1) {
return 0;
}
// set globals for DMA processing
if (cmd) {
g_nSpuDmaChunks = cmd->num_isobuffered_chunks;
g_pDmaStereoVagCmd = cmd->stereo_sibling;
g_pDmaVagCmd = cmd;
}
} else {
// busy, need to queue the dma
ASSERT(g_nSpuDmaQueueCount <= (int)g_aSpuDmaQueue.size());
// set values:
g_aSpuDmaQueue[g_nSpuDmaQueueTail].length = length;
g_aSpuDmaQueue[g_nSpuDmaQueueTail].spu_addr = spu_addr;
g_aSpuDmaQueue[g_nSpuDmaQueueTail].user_data = user_data;
g_aSpuDmaQueue[g_nSpuDmaQueueTail].num_isobuffered_chunks =
cmd ? cmd->num_isobuffered_chunks : 0;
g_aSpuDmaQueue[g_nSpuDmaQueueTail].command = cmd;
g_aSpuDmaQueue[g_nSpuDmaQueueTail].iop_mem = iop_mem;
g_nSpuDmaQueueCount = g_nSpuDmaQueueCount + 1;
g_nSpuDmaQueueTail = g_nSpuDmaQueueTail + 1;
if (0xf < g_nSpuDmaQueueTail) {
g_nSpuDmaQueueTail = 0;
}
defer = true;
}
// set up the stereo command
if (cmd) {
cmd->safe_to_modify_dma = 0;
auto* stereo = cmd->stereo_sibling;
if (stereo) {
stereo->num_isobuffered_chunks = cmd->num_isobuffered_chunks;
stereo->dma_iop_mem_ptr = iop_mem + length;
cmd->dma_chan = g_nSpuDmaChannel;
stereo->xfer_size = length;
}
}
// kick off dma, if we decided not to queue.
if (!defer) {
g_bSpuDmaBusy = true;
set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, user_data);
voice_trans_wrapper(g_nSpuDmaChannel, 0, iop_mem, spu_addr, length);
}
return ret;
}
/*!
* Run a dma transfer that was delayed or dropped.
*/
void RunDeferredVoiceTrans() {
// only if there's a currently happening transfer.
if (g_voiceTransRunning) {
if (GetSystemTimeLow() - g_voiceTransTime > 0x384000) {
ovrld_log(LogCategory::WARN, "DeferredVoiceTrans has detected hung dma... expect problems.");
// original game also check sceSdVoiceTransStatus here, we'll possibly need to mess with this
// if we delay dma completion interrupts...
g_voiceTransRunning = false;
voice_trans_wrapper(g_voiceTransChannel, g_voiceTransMode, g_voiceTransAddr,
g_voiceTransSpuAddr, g_voiceTransSize);
}
}
}
} // namespace jak3
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include "common/common_types.h"
namespace jak3 {
void jak3_overlord_init_globals_dma();
struct ISO_VAGCommand;
int voice_trans_wrapper(s16 chan, u32 mode, const void* iop_addr, u32 spu_addr, u32 size);
void DMA_SendToEE(void* ee_dest,
const void* iop_src,
u32 length,
void callback(void*),
void* callback_arg);
int DMA_SendToSPUAndSync(const u8* iop_mem,
int length,
int spu_addr,
ISO_VAGCommand* cmd,
void* user_data);
void RunDeferredVoiceTrans();
struct ISO_VAGCommand;
struct DmaQueueEntry {
ISO_VAGCommand* command = nullptr;
const void* iop_mem = nullptr;
u32 spu_addr = 0;
u32 length = 0;
void* user_data = nullptr;
u32 num_isobuffered_chunks = 0;
};
void dma_intr_hack();
} // namespace jak3
+583
View File
@@ -0,0 +1,583 @@
#include "dvd_driver.h"
#include <cstring>
#include <memory>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/overlord/jak3/isocommon.h"
#include "game/overlord/jak3/overlord.h"
#include "game/sce/iop.h"
namespace jak3 {
using namespace iop;
std::unique_ptr<CDvdDriver> g_DvdDriver;
s32 g_nDvdDriverThread = -1;
void jak3_overlord_init_globals_dvd_driver() {
g_DvdDriver = std::make_unique<CDvdDriver>();
g_nDvdDriverThread = -1;
}
CDvdDriver* get_driver() {
return g_DvdDriver.get();
}
CMsg::CMsg(jak3::CMsg::MsgKind msg) : m_msg(msg) {
m_ret = 1;
m_thread = GetThreadId();
}
int CMsg::send() {
// note: changed from passing data
// and removed corresponding -4 to skip back past the vtable in DVD thread
// (what were they thinking?)
s32 ret = SendMbx(get_driver()->msgbox, this);
if (ret == 0) {
get_driver()->KickDvdThread();
SleepThread();
return m_ret;
}
return ret;
}
// CMsgLock::CMsgLock() : CMsg(CMsg::MsgKind::LOCK) {}
//
// void CMsgLock::handler() {
// get_driver()->Lock();
// m_ret = 0;
// }
// CMsgReadRaw::CMsgReadRaw(jak3::BlockParams* params) : CMsg(CMsg::MsgKind::READ_RAW) {
// m_block_params = *params;
// }
//
// void CMsgReadRaw::handler() {
// m_ret = get_driver()->ReadDirect(&m_block_params);
// }
CMsgCancelRead::CMsgCancelRead(jak3::CDescriptor* desc) : CMsg(CMsg::MsgKind::CANCEL_READ) {
m_desc = desc;
}
void CMsgCancelRead::handler() {
get_driver()->CancelRead(m_desc);
m_ret = 0;
}
u32 DvdThread();
CDvdDriver::CDvdDriver() {
fifo_entry_sema = -1;
current_thread_priority = 0x13;
disk_type = 5;
tray_flag = 1;
// m_nLockCount = 0;
event_flag = -1;
fifo_access_sema = -1;
tray_flag2 = 1;
initialized = 0;
m_nNumFifoEntries = 0;
ring_head = 0;
ring_tail = 0;
read_in_progress = 0;
callback = nullptr;
// locked = false;
trayflag3 = 0;
m_nDvdThreadAccessSemaCount = 0;
memset(ring, 0, sizeof(Block) * 16);
}
void CDvdDriver::Initialize() {
if (!initialized) {
*this = {};
ThreadParam thread_param;
thread_param.attr = 0x2000000;
// mbox_param.option = gDvdDriverThreadOptions; // ???
thread_param.entry = DvdThread;
thread_param.stackSize = 0x800;
thread_param.initPriority = 0x13;
thread_param.option = 0;
strcpy(thread_param.name, "dvd");
// mbox_param.attr = (int)PTR_DvdThread_00015c98; // ???
g_nDvdDriverThread = CreateThread(&thread_param);
ASSERT(g_nDvdDriverThread >= 0);
SemaParam sema_param;
sema_param.attr = 0;
sema_param.init_count = 1;
sema_param.max_count = 1;
sema_param.option = 0;
fifo_access_sema = CreateSema(&sema_param);
ASSERT(fifo_access_sema >= 0);
sema_param.max_count = 0x10;
sema_param.attr = 0;
sema_param.init_count = 0x10;
sema_param.option = 0;
fifo_entry_sema = CreateSema(&sema_param);
ASSERT(fifo_entry_sema >= 0);
MbxParam mbox_param;
mbox_param.attr = 0;
mbox_param.option = 0;
msgbox = CreateMbx(&mbox_param);
ASSERT(msgbox >= 0);
EventFlagParam param;
param.attr = 0;
param.option = 0;
param.init_pattern = 0;
event_flag = CreateEventFlag(&param);
ASSERT(event_flag >= 0);
StartThread(g_nDvdDriverThread, 0); // this...
}
initialized = 1;
}
void CDvdDriver::SetDriverCallback(std::function<void(int)> f) {
callback = f;
}
// GetDriveCallback
// Poll - would kick the thread...
// void CDvdDriver::Lock() {
// ASSERT_NOT_REACHED();
// if (GetThreadId() == g_nDvdDriverThread) {
// m_nLockCount++;
// locked = true;
// // needs break HACK
// needs_break = false;
// } else {
// CMsgLock lock;
// lock.send();
// }
// }
// Read
int CDvdDriver::ReadMultiple(CDescriptor* descriptor,
int* pages_read_out,
BlockParams* params,
int num_blocks,
bool block_if_queue_full) {
*pages_read_out = 0;
s32 ret = 1;
// check block parameters are reasonable
if (ValidateBlockParams(params, num_blocks) != 0) {
bool from_dvd_thread = GetThreadId() == g_nDvdDriverThread;
if (from_dvd_thread) {
// there is a setting to control if this function should block if there are too many
// queued reads. If the is called from the DVD thread, then this would deadlock.
// the original game ignored the block argument, but I'm asserting
block_if_queue_full = 0;
ASSERT_NOT_REACHED();
}
ovrld_log(LogCategory::DRIVER, "[driver] ReadMultiple (from our thread? {}) num_blocks {}",
from_dvd_thread, num_blocks);
ret = 0;
if (0 < num_blocks) {
// loop, until we've done all the requested reads.
do {
s32 acquired_slots = 0;
if (0 < num_blocks) {
// loop to try to get up to num_blocks slots in the fifo
// but, if we get less, we'll take that too
do {
if (PollSema(this->fifo_entry_sema) == -0x1a3)
break;
acquired_slots = acquired_slots + 1;
} while (acquired_slots < num_blocks);
}
ovrld_log(LogCategory::DRIVER, "[driver] ReadMultiple acquired {} slots in ring",
acquired_slots);
// if we are blocking, and we acquired no slots, then we'll wait here until we get one slot.
if ((block_if_queue_full != 0) && (acquired_slots < 1)) {
ovrld_log(LogCategory::DRIVER, "[driver] ring is full, blocking!");
acquired_slots = 1; // the one we'll get from the WaitSema below
do {
} while (WaitSema(fifo_entry_sema) != 0);
}
// lock, now that we've gotten the slots
AcquireFIFOSema(from_dvd_thread);
num_blocks = num_blocks - acquired_slots;
// if we didn't get any slots, bail.
if (acquired_slots < 1) {
ovrld_log(LogCategory::DRIVER, "[driver] ring is full, bailing!");
ReleaseFIFOSema(from_dvd_thread);
if (0 < *pages_read_out) {
KickDvdThread();
}
return 2;
}
// loop, updating the ring for each slot we aquired
do {
auto* slot = ring + ring_tail;
ovrld_log(LogCategory::DRIVER, "[driver] inserting in ring slot {}", ring_tail);
ring_tail++;
if (0xf < ring_tail) {
ring_tail = 0;
}
m_nNumFifoEntries++;
auto* tail = descriptor->m_pTail;
slot->descriptor = descriptor;
slot->params = params[*pages_read_out];
*pages_read_out = (*pages_read_out) + 1;
if (!tail) {
descriptor->m_pHead = slot;
} else {
tail->next = slot;
}
acquired_slots = acquired_slots + -1;
descriptor->m_pTail = slot;
slot->next = nullptr;
} while (acquired_slots != 0);
ReleaseFIFOSema(from_dvd_thread);
KickDvdThread();
ret = 0;
} while (0 < num_blocks);
}
} else {
ASSERT_NOT_REACHED();
}
return ret;
}
void CDvdDriver::CancelRead(jak3::CDescriptor* desc) {
if (GetThreadId() == g_nDvdDriverThread) {
AcquireFIFOSema(true);
if ((read_in_progress != 0) && (ring[ring_head].descriptor == desc)) {
// while (iVar1 = sceCdBreak(), iVar1 == 0) {
// DelayThread(8000);
// sceCdSync(0);
// }
// sceCdSync(0);
read_in_progress = 0;
}
Block* iter = desc->m_pHead;
Block* tail = desc->m_pTail;
while (iter) {
if (iter->descriptor) {
CompletionHandler(iter, 6);
}
iter->descriptor = nullptr;
iter->next = nullptr;
if (iter == tail)
break;
iter = desc->m_pHead;
}
if (desc->m_pTail == tail) {
desc->m_pTail = nullptr;
}
ReleaseFIFOSema(true);
} else {
CMsgCancelRead msg(desc);
msg.send();
}
}
s32 CDvdDriver::ValidateBlockParams(jak3::BlockParams* params, int num_params) {
if (!params) {
lg::die("Invalid BlockParams: nullptr");
return 0;
}
if (num_params < 1) {
lg::die("Invalid BlockParams: size == 0");
return 0;
}
for (int i = 0; i < num_params; i++) {
auto& p = params[i];
if (p.destination == nullptr) {
lg::die("Invalid BlockParams: {} had nullptr dest", i);
return 0;
}
int kMaxSector = 1000000;
if (p.sector_num > kMaxSector) {
// this is just a sanity check - if we ever have larger files this is okay to increase.
lg::die("Invalid BlockParams: {} had sector num {}", i, p.sector_num);
return 0;
}
if (p.num_sectors > 0x1d0) {
lg::die("Invalid BlockParams: {} had sector count {}", i, p.num_sectors);
return 0;
}
if (!p.file_def) {
lg::die("Invalid BlockParams: {} had no file", i);
return 0;
}
}
return 1;
}
void CDvdDriver::KickDvdThread() {
while (SetEventFlag(event_flag, 1)) {
;
}
}
int CDvdDriver::AcquireFIFOSema(bool from_dvd_thread) {
int iVar1;
int iVar2;
if ((from_dvd_thread != 0) &&
(iVar2 = m_nDvdThreadAccessSemaCount, m_nDvdThreadAccessSemaCount = iVar2 + 1, 0 < iVar2)) {
return 0;
}
iVar1 = WaitSema(this->fifo_access_sema);
iVar2 = 0;
if ((iVar1 != 0) && (iVar2 = iVar1, from_dvd_thread != 0)) {
m_nDvdThreadAccessSemaCount = m_nDvdThreadAccessSemaCount + -1;
iVar2 = iVar1;
}
return iVar2;
}
int CDvdDriver::ReleaseFIFOSema(bool from_dvd_thread) {
int iVar1;
int iVar2;
if (from_dvd_thread != 0) {
iVar2 = m_nDvdThreadAccessSemaCount + -1;
if (m_nDvdThreadAccessSemaCount < 1) {
return -0x1a4;
}
m_nDvdThreadAccessSemaCount = iVar2;
if (0 < iVar2) {
return 0;
}
}
iVar1 = SignalSema(fifo_access_sema);
iVar2 = 0;
if ((iVar1 != 0) && (iVar2 = iVar1, from_dvd_thread != 0)) {
m_nDvdThreadAccessSemaCount = m_nDvdThreadAccessSemaCount + 1;
iVar2 = iVar1;
}
return iVar2;
}
/*!
* PC port added function to actually do a read of a block.
*/
void CDvdDriver::read_from_file(const jak3::Block* block) {
const auto* fd = block->params.file_def;
ASSERT(fd);
FileCacheEntry* selected_entry = nullptr;
// get a cache entry
for (auto& entry : m_file_cache) {
// if we already opened this file, use that
if (entry.def == fd) {
selected_entry = &entry;
break;
}
// otherwise pick the least recently used
if (!selected_entry) {
selected_entry = &entry;
} else {
if (selected_entry->last_use_count > entry.last_use_count) {
selected_entry = &entry;
}
}
}
// open a new file if needed
if (selected_entry->def != fd) {
lg::debug("CDvdDriver swapping files {} - > {}",
selected_entry->def ? selected_entry->def->name.data : "NONE", fd->name.data);
if (selected_entry->def) {
fclose(selected_entry->fp);
}
selected_entry->def = fd;
selected_entry->fp = file_util::open_file(fd->full_path, "rb");
if (!selected_entry->fp) {
lg::die("Failed to open {} {}", fd->full_path, strerror(errno));
}
selected_entry->offset_in_file = 0;
}
// increment use counter
selected_entry->last_use_count = m_file_cache_counter++;
const u64 desired_offset = block->params.sector_num * 0x800;
// see if we're reading entirely past the end of the file
if (desired_offset >= fd->length) {
return;
}
if (selected_entry->offset_in_file != desired_offset) {
lg::debug("CDvdDriver jumping in file {}: {} -> {}", fd->name.data,
selected_entry->offset_in_file, desired_offset);
if (fseek(selected_entry->fp, desired_offset, SEEK_SET)) {
ASSERT_NOT_REACHED_MSG("Failed to fseek");
}
selected_entry->offset_in_file = desired_offset;
}
// read
s64 read_length = block->params.num_sectors * 0x800;
s64 extra_length = read_length + desired_offset - fd->length;
if (extra_length > 0) {
read_length -= extra_length;
}
auto ret = fread(block->params.destination, read_length, 1, selected_entry->fp);
if (ret != 1) {
lg::die("Failed to read {} {}, size {} of {} (ret {})", fd->full_path, strerror(errno),
read_length, fd->length, ret);
}
selected_entry->offset_in_file += read_length;
}
u32 DvdThread() {
auto* driver = get_driver();
while (true) {
// Poll for messages
CMsg* msg = nullptr;
while (true) {
int poll_status = PollMbx((MsgPacket**)&msg, driver->msgbox);
if (poll_status || !msg) {
break;
}
// run message code
msg->handler();
// wake the waiting thread.
WakeupThread(msg->m_thread);
}
bool completed = false;
// if a read is in progress, wait for it to finish.
if (driver->read_in_progress) {
// TODO: if we switch to async reads, this is where we'd want to sync.
// sceCdSync(0);
completed = true;
// error checking
}
driver->AcquireFIFOSema(true);
// error handling
// handle ring book-keeping.
// note that these are somewhat double-buffered - we'll sync on read i-1, start read i, then
// run the completion handler for read i - 1.
// the completion is what actually removes stuff from the ring.
s32 fifo_slots_freed = completed ? 1 : 0;
s32 ring_entry = driver->ring_head + (completed ? 1 : 0);
if (ring_entry > 15) {
ring_entry = 0;
}
Block* block = driver->ring + ring_entry;
s32 fifo_entries = driver->m_nNumFifoEntries - fifo_slots_freed;
if (fifo_entries) {
// start a new read.
driver->read_in_progress = 1;
ovrld_log(LogCategory::DRIVER, "[driver thread] Reading for slot {}", block - driver->ring);
driver->read_from_file(block);
} else {
driver->read_in_progress = 0;
}
// run completion handler for the previous read - not the one we just started!
Block sblock;
if (completed) {
auto* last_block = &driver->ring[driver->ring_head];
ovrld_log(LogCategory::DRIVER, "[driver thread] Completion handler for {}",
driver->ring_head);
sblock = *last_block;
if (sblock.descriptor && sblock.descriptor->m_pHead) {
ASSERT(sblock.descriptor->m_pHead == last_block);
}
if (((sblock.descriptor)->m_pHead == last_block) &&
((sblock.descriptor)->m_pHead = &sblock, (sblock.descriptor)->m_pTail == last_block)) {
(sblock.descriptor)->m_pTail = &sblock;
}
}
driver->ring_head = ring_entry;
driver->m_nNumFifoEntries = fifo_entries;
while (0 < fifo_slots_freed) {
fifo_slots_freed = fifo_slots_freed + -1;
SignalSema(driver->fifo_entry_sema);
}
if (completed != 0) {
driver->CompletionHandler(&sblock, 0);
}
driver->ReleaseFIFOSema(true);
// this logic here was modified - we go to waiting if we have no more reads.
if (driver->m_nNumFifoEntries == 0) {
ovrld_log(LogCategory::DRIVER, "[driver thread] No work, waiting.");
WaitEventFlag(driver->event_flag, 1, 0x11);
ovrld_log(LogCategory::DRIVER, "[driver thread] Woken up!");
}
// s32 status;
// if ((((driver->locked != false)) ||
// ((driver->needs_break == 0 && (driver->m_nNumFifoEntries == 0)))) &&
// ((status = WaitEventFlag(driver->event_flag, 1, 0x11),
// status != 0 && (status != -0x1a2)))) {
// if (status == -0x1a9) {
// do {
// SleepThread();
// } while (true);
// }
// DelayThread(8000);
// }
}
}
void CDvdDriver::CompletionHandler(jak3::Block* block, int code) {
// there is some janky thread priority changes here,
// but they do not seem to be needed with the changes to the ISO thread.
// PushPri(this,local_20,0x35);
auto* desc = block->descriptor;
if (desc && desc->m_pHead) {
desc->m_status = code;
int thread = desc->m_ThreadID;
if (desc->m_Callback) {
(desc->m_Callback)(desc->m_File, block, code);
}
if (0 < thread) {
WakeupThread(thread);
}
Block* next;
if ((desc->m_pHead == block) && (next = block->next, desc->m_pHead = next, next == nullptr)) {
desc->m_pTail = nullptr;
}
}
block->next = nullptr;
block->descriptor = nullptr;
// PopPri(this, local_20[0]);
}
CDvdDriver::~CDvdDriver() {
for (auto& entry : m_file_cache) {
if (entry.fp) {
fclose(entry.fp);
}
}
}
} // namespace jak3
+144
View File
@@ -0,0 +1,144 @@
#pragma once
#include <functional>
#include "common/common_types.h"
#include "game/overlord/jak3/isocommon.h"
namespace jak3 {
void jak3_overlord_init_globals_dvd_driver();
/*!
* The DVD Driver is what actually called the Sony CD library functions.
* It also had many special cases for handling errors, open trays, retries, etc that are not
* recreated in this port.
*
* The original DVD driver thread didn't run the actual reads - the Sony CD library had its own
* threads for async reads. However, they would call sceCdSync() to wait until a read finished.
* It's not clear if sceCdSync would allow other threads to run while waiting on the read to finish.
* but if it did, this is a bit of a difference in the blocking behavior.
*
* This is something that could be revisited, as freads will now essentially block the entire
* overlord, including refilling sound RAM.
*/
struct CDescriptor;
struct BlockParams;
struct CPage;
struct ISOFileDef;
// The CMSG system is used to pass messages from external threads to the driver. It's mostly used
// internally by the driver, when functions are called on it from outside threads. In these cases
// the Cmsg will get send to the driver thread and handler will run on it here.
struct CMsg {
enum class MsgKind {
// LOCK = 0,
READ_RAW = 1,
CANCEL_READ = 2,
};
CMsg(MsgKind msg);
virtual int send();
virtual void handler() = 0;
u8 data[8];
MsgKind m_msg;
int m_thread;
int m_ret;
};
// struct CMsgLock : public CMsg {
// CMsgLock();
// void handler() override;
// };
// struct CMsgReadRaw : public CMsg {
// explicit CMsgReadRaw(BlockParams* params);
// void handler() override;
// BlockParams m_block_params;
// };
struct CMsgCancelRead : public CMsg {
explicit CMsgCancelRead(CDescriptor* desc);
void handler() override;
CDescriptor* m_desc;
};
struct CISOCDFile;
/*!
* Reference to an ongoing or requested read at the driver level, possibly made up of multiple
* blocks. In this case, the head/tail pointers point to Blocks stored inside the CDvdDriver's
* internal FIFO.
*/
struct CDescriptor {
CDescriptor() = default;
int m_unk0 = 0;
void (*m_Callback)(CISOCDFile*, Block*, s32) = nullptr;
int m_ThreadID = 0;
int m_status = 0;
CISOCDFile* m_File = nullptr;
Block* m_pHead = nullptr;
Block* m_pTail = nullptr;
};
class CDvdDriver {
public:
CDvdDriver();
~CDvdDriver();
void Initialize();
void CancelRead(CDescriptor* descriptor);
int ReadMultiple(CDescriptor* descriptor,
int* pages_read_out,
BlockParams* params,
int num_pages,
bool block_if_queue_full);
void SetDriverCallback(std::function<void(int)> f);
// int ReadDirect(BlockParams* params);
void KickDvdThread();
// void Lock();
int GetDiskType() const { return disk_type; }
s32 ValidateBlockParams(BlockParams* params, int num_params);
int ReleaseFIFOSema(bool from_dvd_thread);
int AcquireFIFOSema(bool from_dvd_thread);
void read_from_file(const Block* block);
void CompletionHandler(Block* block, int code);
u8 initialized = 0;
s32 event_flag = -1;
s32 fifo_access_sema = -1;
s32 fifo_entry_sema = -1;
s32 msgbox = -1;
s32 m_nNumFifoEntries = 0;
s32 ring_head = 0;
s32 ring_tail = 0;
Block ring[16];
u8 read_in_progress; // more likely: read in progress
std::function<void(int)> callback;
s32 current_thread_priority = 0;
// s32 m_nLockCount = 0;
// bool locked = false;
//
s32 disk_type;
u8 tray_flag2;
u8 trayflag3;
u8 tray_flag;
s32 m_nDvdThreadAccessSemaCount = 0;
private:
struct FileCacheEntry {
const ISOFileDef* def = nullptr;
FILE* fp = nullptr;
u32 last_use_count = 0;
u64 offset_in_file = 0;
};
u32 m_file_cache_counter = 0;
static constexpr int kNumFileCacheEntries = 6;
FileCacheEntry m_file_cache[kNumFileCacheEntries];
};
// replacement for g_DvdDriver
CDvdDriver* get_driver();
} // namespace jak3
+50
View File
@@ -0,0 +1,50 @@
#include "game/overlord/jak3/basefile.h"
#include "game/overlord/jak3/basefilesystem.h"
#include "game/overlord/jak3/dma.h"
#include "game/overlord/jak3/dvd_driver.h"
#include "game/overlord/jak3/iso.h"
#include "game/overlord/jak3/iso_api.h"
#include "game/overlord/jak3/iso_cd.h"
#include "game/overlord/jak3/iso_queue.h"
#include "game/overlord/jak3/isocommon.h"
#include "game/overlord/jak3/list.h"
#include "game/overlord/jak3/overlord.h"
#include "game/overlord/jak3/pagemanager.h"
#include "game/overlord/jak3/ramdisk.h"
#include "game/overlord/jak3/sbank.h"
#include "game/overlord/jak3/soundcommon.h"
#include "game/overlord/jak3/spustreams.h"
#include "game/overlord/jak3/srpc.h"
#include "game/overlord/jak3/ssound.h"
#include "game/overlord/jak3/stream.h"
#include "game/overlord/jak3/streamlist.h"
#include "game/overlord/jak3/vag.h"
#include "game/overlord/jak3/vblank_handler.h"
namespace jak3 {
void jak3_overlord_init_globals_all() {
jak3_overlord_init_globals_overlord();
jak3_overlord_init_globals_pagemanager();
jak3_overlord_init_globals_iso_cd();
jak3_overlord_init_globals_dma();
jak3_overlord_init_globals_iso();
jak3_overlord_init_globals_iso_queue();
jak3_overlord_init_globals_srpc();
jak3_overlord_init_globals_vag();
jak3_overlord_init_globals_ssound();
jak3_overlord_init_globals_iso_api();
jak3_overlord_init_globals_spustreams();
jak3_overlord_init_globals_list();
jak3_overlord_init_globals_vblank_handler();
jak3_overlord_init_globals_dvd_driver();
jak3_overlord_init_globals_basefile();
jak3_overlord_init_globals_basefilesystem();
jak3_overlord_init_globals_ramdisk();
jak3_overlord_init_globals_isocommon();
jak3_overlord_init_globals_stream();
jak3_overlord_init_globals_sbank();
jak3_overlord_init_globals_soundcommon();
jak3_overlord_init_globals_streamlist();
}
} // namespace jak3
+5
View File
@@ -0,0 +1,5 @@
#pragma once
namespace jak3 {
void jak3_overlord_init_globals_all();
}
File diff suppressed because it is too large. Load diff
+92
View File
@@ -0,0 +1,92 @@
#pragma once
#include "common/link_types.h"
#include "game/overlord/jak3/isocommon.h"
namespace jak3 {
struct ISOFileDef;
void jak3_overlord_init_globals_iso();
void InitISOFS();
const ISOFileDef* FindISOFile(const char*);
struct ISO_VAGCommand;
struct VagStreamData;
struct RPC_Dgo_Cmd;
struct ISO_DGOCommand : public ISO_Hdr {
u8* buffer1 = nullptr;
u8* buffer2 = nullptr;
u8* buffer_top = nullptr;
DgoHeader dgo_header; //
ObjectHeader obj_header; //
u8* ee_dest_buffer = nullptr; // 192
int bytes_processed = 0; // 196
int objects_loaded = 0; // 200
enum class State {
INIT = 0,
READ_DGO_HEADER = 1,
FINISH_OBJ = 2,
READ_LAST_OBJ = 3,
READ_OBJ_HEADER = 4,
READ_OBJ_DATA = 5,
FINISH_DGO = 6,
FINISH_OBJ_SINGLE_BUFFER = 7,
} state = State::INIT; // 204
int finished_first_object = 0; // 208
int buffer_toggle = 0; // 212
u8* selected_buffer = nullptr; // 216
int selected_id = 0; // 220
int last_id = 0; // 224
int acked_cancel_id = 0; // 228
u8 nosync_cancel_pending_flag = 0; // 232
int request_cancel_id = 0; // 236
u8 nosync_cancel_ack = 0; // 240
int sync_sent_count = 0; // 244
// the number of times we looked in the iso thread's sync mbox
// I think just used for debugging/asserts.
int sync_mbox_wait_count = 0; // 248
int sync_ret_count = 0; // 252
int want_abort = 0; // 256
};
enum class CopyKind {
EE = 0,
IOP = 1,
SBK = 2,
};
void set_active_a(ISO_Hdr* cmd, int val);
void set_active_b(ISO_Hdr* cmd, int val);
void set_active_c(ISO_Hdr* cmd, int val);
void IsoStopVagStream(ISO_VAGCommand* cmd);
void IsoPlayVagStream(ISO_VAGCommand* user_cmd);
EIsoStatus NullCallback(ISO_Hdr* msg);
EIsoStatus CopyDataToIOP(ISO_Hdr* msg);
EIsoStatus CopyDataSbkLoad(ISO_Hdr* msg);
EIsoStatus CopyDataToEE(ISO_Hdr* msg);
EIsoStatus RunDGOStateMachine(ISO_Hdr* msg);
void QueueVAGStream(VagStreamData* cmd);
void CopyDataDmaCallback(void*);
void* RPC_DGO(unsigned int fno, void* msg_ptr, int);
void LoadDGO(RPC_Dgo_Cmd* cmd);
void CancelDGO(RPC_Dgo_Cmd* cmd);
void LoadNextDGO(RPC_Dgo_Cmd* cmd);
EIsoStatus CopyData(ISO_LoadCommon* msg, CopyKind kind);
void CancelDGONoSync(int id);
void IsoPlayMusicStream(ISO_VAGCommand* user_cmd);
void IsoQueueVagStream(ISO_VAGCommand* user_cmd);
extern int g_nISOThreadID;
extern int g_nISOMbx;
extern bool g_bMusicPause;
extern int g_nMusicSemaphore;
extern char g_szTargetMusicName[0x30];
extern int g_nActiveMusicStreams;
extern bool g_bVagCmdsInitialized;
extern bool g_bMusicIsPaused;
extern bool g_bAnotherMusicPauseFlag;
extern int g_nMusicFade;
extern int g_nMusicTweak;
extern int g_nMusicFadeDir;
} // namespace jak3
+229
View File
@@ -0,0 +1,229 @@
#include "iso_api.h"
#include <cstring>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/overlord/jak3/iso.h"
#include "game/overlord/jak3/iso_cd.h"
#include "game/overlord/jak3/iso_queue.h"
#include "game/overlord/jak3/srpc.h"
#include "game/overlord/jak3/vag.h"
#include "game/sce/iop.h"
namespace jak3 {
using namespace iop;
void jak3_overlord_init_globals_iso_api() {}
/*!
* This file is the "API" that implementations of RPCs or other code can use to submit things to the
* ISO thread. Note that not all RPCs use this API - for example the DGO RPC just manually submits
* messages. Generally, these functions will not return until the actual action is complete, like a
* file is loaded.
* - except for messages to pause/play audio, those functions will return immediately, but there may
* be a delay until they are actually processed.
*/
// PluginVagAndVagWad not ported.
u32 EEVagAndVagWad(ISO_VAGCommand* cmd, char* name) {
ISOName name_buff;
if (*name == '$' || strlen(name) < 9) {
if (*name == '$') {
name++;
}
MakeISOName(&name_buff, name);
} else {
file_util::ISONameFromAnimationName(name_buff.data, name);
}
cmd->vag_dir_entry = get_file_system()->FindVAGFile(name_buff.data);
if (cmd->vag_dir_entry) {
memcpy(name_buff.data, "VAGWAD ", 8);
strncpy(name_buff.data + 8, g_pszLanguage, 4);
auto* file_def = get_file_system()->FindIN(&name_buff);
// piVar1 = g_pFileSystem;
cmd->file_def = file_def;
strncpy(name_buff.data + 8, "INT", 4);
cmd->vag_file_def = get_file_system()->FindIN(&name_buff);
if (cmd->vag_dir_entry && cmd->file_def && cmd->vag_file_def) {
return 1;
}
}
cmd->vag_dir_entry = nullptr;
cmd->file_def = nullptr;
cmd->vag_file_def = nullptr;
return 0;
}
int LoadISOFileToIOP(const ISOFileDef* file_def, void* addr, int length) {
ISO_LoadSingle cmd;
cmd.msg_type = ISO_Hdr::MsgType::LOAD_IOP;
cmd.mbox_reply = 0;
cmd.thread_to_wake = GetThreadId();
cmd.file_def = file_def;
cmd.addr = (u8*)addr;
cmd.maxlen = length;
lg::warn("--------------- LoadISOFileToIOP START");
SendMbx(g_nISOMbx, &cmd);
SleepThread();
lg::warn("--------------- LoadISOFileToIOP END");
if (cmd.status == EIsoStatus::NONE_0) {
return cmd.length_to_copy;
} else {
return 0;
}
}
int LoadISOFileToEE(const ISOFileDef* file_def, u32 addr, int length) {
ISO_LoadSingle cmd;
cmd.msg_type = ISO_Hdr::MsgType::LOAD_EE;
cmd.mbox_reply = 0;
cmd.thread_to_wake = GetThreadId();
cmd.file_def = file_def;
cmd.addr = (u8*)(u64)addr;
cmd.maxlen = length;
lg::warn("--------------- LoadISOFileToEE START");
SendMbx(g_nISOMbx, &cmd);
SleepThread();
lg::warn("--------------- LoadISOFileToEE END");
if (cmd.status == EIsoStatus::NONE_0) {
return cmd.length_to_copy;
}
return 0;
}
int LoadISOFileChunkToEE(const ISOFileDef* file_def, u32 addr, int max_len, int sector_offset) {
ISO_LoadSingle cmd;
cmd.msg_type = ISO_Hdr::MsgType::LOAD_EE_CHUNK;
cmd.mbox_reply = 0;
cmd.thread_to_wake = GetThreadId();
cmd.file_def = file_def;
cmd.addr = (u8*)(u64)addr;
cmd.maxlen = max_len;
cmd.sector_offset = sector_offset;
lg::warn("--------------- LoadISOFileChunkToEE START");
SendMbx(g_nISOMbx, &cmd);
SleepThread();
lg::warn("--------------- LoadISOFileChunkToEE END");
if (cmd.status == EIsoStatus::NONE_0) {
return cmd.length_to_copy;
}
return 0;
}
u32 LoadSoundBankToIOP(const char* name, SoundBankInfo* bank, u32 mode) {
ISO_LoadSoundbank cmd;
cmd.msg_type = ISO_Hdr::MsgType::LOAD_SOUNDBANK;
cmd.mbox_reply = 0;
cmd.thread_to_wake = GetThreadId();
cmd.bank_info = bank;
cmd.name = name;
cmd.priority = mode;
lg::warn("--------------- LoadSoundBankToIOP START");
SendMbx(g_nISOMbx, &cmd);
SleepThread();
lg::warn("--------------- LoadSoundBankToIOP END");
return (u32)cmd.status;
}
void PlayMusicStream(VagStreamData* stream) {
int iVar1;
ISO_VAGCommand cmd;
cmd.msg_type = ISO_Hdr::MsgType::PLAY_MUSIC_STREAM;
cmd.mbox_reply = 0;
cmd.thread_to_wake = 0;
iVar1 = EEVagAndVagWad(&cmd, stream->name);
if (iVar1 == 0) {
// if (bWarn == 0) {
// bWarn = 1;
// }
} else {
cmd.play_volume = 0x400;
// bWarn = 0;
strncpy(cmd.name, stream->name, 0x30);
cmd.id = stream->id;
cmd.priority_pq = 9;
cmd.music_flag = 1;
cmd.maybe_sound_handler = 0;
cmd.plugin_id = 0;
cmd.art_flag = 0;
cmd.movie_flag = 0;
cmd.updated_trans = 0;
IsoPlayMusicStream(&cmd);
}
}
void QueueVAGStream(VagStreamData* stream) {
bool bVar1;
bool bVar2;
ISO_VAGCommand cmd;
cmd.msg_type = ISO_Hdr::MsgType::VAG_QUEUE;
cmd.mbox_reply = 0;
cmd.thread_to_wake = 0;
if (stream->sound_handler == 0) {
EEVagAndVagWad(&cmd, stream->name);
cmd.play_volume = 0x400;
cmd.play_group = 2;
} else {
ASSERT_NOT_REACHED();
// PluginVagAndVagWad(&cmd,stream);
// cmd.play_volume = stream->maybe_volume2;
// cmd.oog = stream->maybe_volume_3;
// cmd.play_group = stream->group;
}
strncpy(cmd.name, stream->name, 0x30);
cmd.id = stream->id;
cmd.plugin_id = stream->plugin_id;
cmd.priority_pq = stream->priority;
cmd.maybe_sound_handler = stream->sound_handler;
bVar1 = stream->movie_art_load != 0;
cmd.movie_flag = bVar1;
bVar2 = stream->art_load != 0;
cmd.art_flag = bVar2;
cmd.music_flag = 0;
if (bVar2) {
cmd.flags.art = 1;
}
if (bVar1) {
cmd.flags.movie = 1;
}
cmd.updated_trans = 0;
IsoQueueVagStream(&cmd);
}
void PauseVAGStreams() {
auto* cmd = GetVAGCommand();
cmd->msg_type = ISO_Hdr::MsgType::VAG_PAUSE;
cmd->mbox_reply = 0;
cmd->thread_to_wake = 0;
SendMbx(g_nISOMbx, cmd);
}
void UnpauseVAGStreams() {
auto* cmd = GetVAGCommand();
cmd->msg_type = ISO_Hdr::MsgType::VAG_UNPAUSE;
cmd->mbox_reply = 0;
cmd->thread_to_wake = 0;
SendMbx(g_nISOMbx, cmd);
}
void SetVAGStreamPitch(int id, int pitch) {
auto* cmd = GetVAGCommand();
cmd->msg_type = ISO_Hdr::MsgType::VAG_SET_PITCH_VOL;
cmd->id = id;
cmd->pitch_cmd = pitch;
cmd->mbox_reply = 0;
cmd->thread_to_wake = 0;
SendMbx(g_nISOMbx, cmd);
}
} // namespace jak3
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include "common/common_types.h"
namespace jak3 {
void jak3_overlord_init_globals_iso_api();
struct ISOFileDef;
struct VagStreamData;
struct SoundBankInfo;
int LoadISOFileToEE(const ISOFileDef* file_def, u32 addr, int length);
int LoadISOFileToIOP(const ISOFileDef* file_def, void* addr, int length);
void PlayMusicStream(VagStreamData* data);
int LoadISOFileChunkToEE(const ISOFileDef* file_def, u32 addr, int max_len, int sector_offset);
void SetVAGStreamPitch(s32 id, s32 pitch);
void UnpauseVAGStreams();
u32 LoadSoundBankToIOP(const char* name, SoundBankInfo* bank, u32 mode);
} // namespace jak3
+529
View File
@@ -0,0 +1,529 @@
#include "iso_cd.h"
#include <vector>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/overlord/jak3/iso.h"
#include "game/overlord/jak3/isocommon.h"
#include "game/overlord/jak3/overlord.h"
#include "game/overlord/jak3/spustreams.h"
#include "game/sce/iop.h"
using namespace iop;
namespace jak3 {
VagDir g_VagDir;
MusicTweaks gMusicTweakInfo;
CISOCDFile g_CISOCDFiles[kMaxOpenFiles];
namespace {
CISOCDFile* g_pReadInfo = nullptr;
std::vector<ISOFileDef> g_FileDefs;
std::unique_ptr<CISOCDFileSystem> g_ISOCDFileSystem;
} // namespace
void jak3_overlord_init_globals_iso_cd() {
g_pReadInfo = nullptr;
for (auto& f : g_CISOCDFiles) {
f = CISOCDFile();
}
g_FileDefs.clear();
g_VagDir = {};
g_ISOCDFileSystem = std::make_unique<CISOCDFileSystem>();
gMusicTweakInfo = {};
}
CBaseFileSystem* get_file_system() {
return g_ISOCDFileSystem.get();
}
CISOCDFile::CISOCDFile() {
m_nSector = -1;
m_nLoaded = 0;
m_nLength = 0;
}
namespace {
void ReadPagesCallbackF(CISOCDFile* file, Block* block, s32 error) {
file->ReadPagesCallback(block, error);
}
} // namespace
CISOCDFile::CISOCDFile(const jak3::ISOFileDef* filedef, s32 process_data_semaphore)
: CBaseFile(filedef, process_data_semaphore) {
m_nSector = -1;
m_nLoaded = 0;
m_nLength = 0;
m_Descriptor.m_File = this;
m_Descriptor.m_Callback = ReadPagesCallbackF;
}
/*!
* Call ReadPages to read data from this file into its PageList. I believe that the read
* is finished after this function returns. Note that this returns COMPLETE enum in many cases, both
* if the read succeeds, or if the read is not attempted for some reasons.
*/
EIsoStatus CISOCDFile::BeginRead() {
ASSERT(m_Buffer.m_pPageList);
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
if (m_Status == EIsoStatus::NONE_0) {
return EIsoStatus::ERROR_b;
}
if (m_Status == EIsoStatus::OK_2) {
return EIsoStatus::OK_2;
}
ASSERT(m_Status == EIsoStatus::IDLE_1);
auto* plist = m_Buffer.m_pPageList;
// how many pages are in our list, but not filled?
int num_pages_desired = plist->m_nNumPages - plist->m_nNumActivePages;
if (!num_pages_desired) {
// no space to read
return EIsoStatus::OK_2;
}
// convert pages to active, indicating that a read will attempt to fill them:
auto* first_page = plist->AddActivePages(num_pages_desired);
if (!first_page) {
lg::warn("Failed to add {} active pages", num_pages_desired);
}
// convert buffer type - ??
switch (m_Buffer.m_eBufferType) {
case CBuffer::BufferType::EBT_FREE:
ASSERT_NOT_REACHED();
case CBuffer::BufferType::NORMAL:
m_Buffer.m_eBufferType = CBuffer::BufferType::REQUEST_NORMAL;
break;
case CBuffer::BufferType::VAG:
m_Buffer.m_eBufferType = CBuffer::BufferType::REQUEST_VAG;
break;
case CBuffer::BufferType::REQUEST_NORMAL:
case CBuffer::BufferType::REQUEST_VAG:
break;
}
m_Status = EIsoStatus::OK_2;
// remember this as our currently reading file
g_pReadInfo = this;
if (m_FileKind != CBaseFile::Kind::LZO_COMPRESSED) {
if (m_nLength == 0 || m_nLoaded < m_nLength) {
ovrld_log(LogCategory::PAGING, "Calling ReadPages: sector {}, pages {}", m_nSector,
num_pages_desired);
ReadPages(m_nSector, first_page, num_pages_desired, nullptr, true);
int bytes = 0x8000 * num_pages_desired;
m_nLoaded += bytes;
m_Buffer.AddData(bytes);
m_nSector += bytes >> 0xb;
}
} else {
ASSERT_NOT_REACHED();
}
return EIsoStatus::OK_2;
}
/*!
* Called by ?? to indicate that the read is done.
*/
EIsoStatus CISOCDFile::SyncRead() {
if (m_Status != EIsoStatus::IDLE_1 && g_pReadInfo) {
if (m_Status == EIsoStatus::OK_2) {
m_Status = EIsoStatus::IDLE_1;
}
g_pReadInfo = nullptr;
return EIsoStatus::OK_2;
}
return EIsoStatus::ERROR_b;
}
/*!
* As soon as possible, stop ongoing reads and free buffers.
*/
void CISOCDFile::Close() {
// cancel ongoing reading in the driver
get_driver()->CancelRead(&m_Descriptor);
ASSERT(m_FileKind != CBaseFile::Kind::LZO_COMPRESSED); // unsupported in pc
if (this == g_pReadInfo) {
g_pReadInfo = nullptr;
}
if (m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE) {
TerminateBuffer();
}
// reset self
*this = CISOCDFile();
}
/*!
* In the case where we have run out of page memory, take pages that we have loaded, but aren't yet
* using, and discard them back to the pool.
*/
int CISOCDFile::RecoverPages(int num_pages_desired) {
// we only allow ourselves to recover pages for VAG streaming.
if (!m_Buffer.m_pIsoCmd || m_Buffer.m_pIsoCmd->callback != ProcessVAGData) {
return 0;
}
// lock semaphore for processing
ASSERT(m_ProcessDataSemaphore != -1);
WaitSema(m_ProcessDataSemaphore);
auto* plist = m_Buffer.m_pPageList;
int num_removed = 0;
if (plist) {
int pages_to_ask_for = plist->m_nNumUnsteppedPages;
if (pages_to_ask_for > 1) {
// don't ask for more than user asked for
if (pages_to_ask_for > num_pages_desired) {
pages_to_ask_for = num_pages_desired;
}
num_removed = plist->RemoveActivePages(pages_to_ask_for);
if (num_removed) {
m_nSector -= 0x10 * num_removed;
m_nLoaded -= 0x8000 * num_removed;
// suspend intr
ASSERT(m_Buffer.m_nDataLength >= 0);
if (m_Buffer.m_nDataLength > 0) {
m_Buffer.AddData(-num_removed * 0x8000);
}
// resume intr
ASSERT(m_nLoaded >= 0);
}
}
}
// move our reading pointer back.
m_PageOffset -= num_removed;
// unlock processing
SignalSema(m_ProcessDataSemaphore);
return num_removed;
}
/*!
* Get the next sector to read.
*/
int CISOCDFile::GetSector() {
return m_nSector;
}
// WaitForLZOPages - not ported.
void CISOCDFile::ReadPages(int sector,
jak3::CPage* destination_page,
int num_pages,
char* done_flag_ptr,
bool sleep_until_done) {
ASSERT(destination_page);
ASSERT(num_pages >= 1);
constexpr int kMaxPages = 24;
// I _think_ this might have been an alloca...
BlockParams params_array[kMaxPages];
ASSERT(num_pages <= kMaxPages);
// iVar2 = -((num_pages + -1) * 0x14 + 0x1bU & 0xfffffff8);
// params = (BlockParams*)((int)local_30 + iVar2);
BlockParams* params = params_array;
// increment our progress in the file - the distributed update of the various progress integers
// is somewhat confusing, especially since failures here don't seem to reset it properly.
m_PageOffset += num_pages;
// Set up block params for each page to read
CPage* page = destination_page;
int pg_remaining = num_pages;
do {
page->input_state = CPage::State::READING;
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
params->destination = page->m_pPageMemStart;
params->num_sectors = 0x10;
params->sector_num = sector;
params->file_def = m_FileDef;
params->page = page;
params->flag = nullptr;
if (pg_remaining == 1) { // on the last page...
params->flag = (char*)0xffffffff; // flag this
if (!sleep_until_done) {
params->flag = done_flag_ptr;
}
}
ovrld_log(LogCategory::PAGING, "[paging] building block for driver: 0x{:x}, {}, {}, flag: {}",
(u64)params->destination, params->sector_num, params->file_def->name.data,
(u64)params->flag);
page = page->m_pNextPage;
pg_remaining = pg_remaining + -1;
sector += 0x10;
params++;
} while (page && pg_remaining > 0);
if (pg_remaining == 0) {
// we set up block params for all the requested pages.
int pages_actually_read = -1;
ovrld_log(LogCategory::PAGING, "[paging] Submitting {} blocks to driver.\n", num_pages);
int status = get_driver()->ReadMultiple(&m_Descriptor, &pages_actually_read, params_array,
num_pages, true);
if ((status == 0) && pages_actually_read == num_pages) {
if (!sleep_until_done) {
return;
}
// put us to sleep...
ovrld_log(LogCategory::PAGING, "[paging] Sleeping, waiting for driver to read {}",
pages_actually_read);
SleepThread();
ovrld_log(LogCategory::PAGING, "[paging] Driver woke us up!");
return;
}
ASSERT_NOT_REACHED(); // unexpected failure to issue read
} else {
// ran out of pages....
ASSERT_NOT_REACHED(); // this was just a warning.. but I think it shouldn't happen.
if (destination_page) {
do {
destination_page->input_state = CPage::State::ACTIVE;
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag,
~destination_page->mask);
destination_page = destination_page->m_pNextPage;
} while (destination_page);
}
}
ASSERT_NOT_REACHED();
// only get here is we failed.... set the done flag.
if (sleep_until_done == 0 && done_flag_ptr) {
*done_flag_ptr = 1;
}
}
/*!
* Callback run by the dvd driver when a page is finished reading.
*/
void CISOCDFile::ReadPagesCallback(jak3::Block* block, int error) {
if (error == 0) {
ASSERT(block->params.page->m_nAllocState == 1);
// flag page as done
block->params.page->input_state = CPage::State::READ_DONE;
// set flag
SetEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, block->params.page->mask);
// if this block has a notification, process it:
if (block->params.flag == (char*)0xffffffff) { // indicates we should wake caller
// we assume the caller is the iso thread
WakeupThread(g_nISOThreadID);
} else if (block->params.flag) {
// in this case, it's a pointer.
*block->params.flag = 1;
}
}
}
// DecompressBlock - not ported
/*!
* Initialize the file system - find all files and set up their definitions.
*/
int CISOCDFileSystem::Init() {
// drive ready event flag - not ported
// get disc type - not ported
// this is mostly not needed... except for some vag pausing nonsense :(
get_driver()->SetDriverCallback([&](int a) { DvdDriverCallback(a); });
// skipped a bunch of lzo pages crap
ReadDirectory();
LoadMusicTweaks();
// skip load Disc ID
// should already be done in the constructor...
for (auto& f : g_CISOCDFiles) {
f.m_FileDef = nullptr;
}
return 0;
}
// PollDrive - not ported
/*!
* Find a file definition by name.
*/
ISOFileDef* CISOCDFileSystem::Find(const char* name) {
ISOName iname;
file_util::MakeISOName(iname.data, name);
return FindIN(&iname);
}
/*!
* Find a file definition by its "ISO Name", a 12-byte name.
*/
ISOFileDef* CISOCDFileSystem::FindIN(const jak3::ISOName* name) {
for (auto& def : g_FileDefs) {
if (def.name == *name) {
return &def;
}
}
return nullptr;
}
/*!
* Get the length of a file, in bytes.
*/
int CISOCDFileSystem::GetLength(const jak3::ISOFileDef* file) {
// return file->length;
lg::info("getlength");
file_util::assert_file_exists(file->full_path.c_str(), "CISOCDFileSystem GetLength");
FILE* fp = file_util::open_file(file->full_path.c_str(), "rb");
ASSERT(fp);
fseek(fp, 0, SEEK_END);
uint32_t len = ftell(fp);
fclose(fp);
return len;
}
/*!
* Open a file for reading.
*/
CBaseFile* CISOCDFileSystem::Open(const jak3::ISOFileDef* file_def,
int sector_offset,
int file_kind) {
auto* file = AllocateFile(file_def);
ASSERT(file);
// file kind must be known to be non-compressed (1). (TODO: remove arg)
ASSERT(file_kind == 1);
file->m_FileKind = CBaseFile::Kind::NORMAL;
file->m_nLength = file_def->length;
file->m_LengthPages = (0x7fff + file->m_nLength) >> 0xf;
if (file->m_LengthPages < 1) {
ASSERT_NOT_REACHED();
}
file->m_nLoaded = 0;
file->m_PageOffset = 0;
// in the original game, this was the sector of the start of the file
// we just make it 0, since we don't build up a whole iso file.
file->m_nSector = 0;
if (sector_offset != -1) {
file->m_nSector += sector_offset;
}
return file;
}
/*!
* Open a WAD file for reading, given the offset of the data to read, in pages.
*/
CBaseFile* CISOCDFileSystem::OpenWAD(const jak3::ISOFileDef* file_def, int page_offset) {
auto* file = AllocateFile(file_def);
ASSERT(file);
file->m_LengthPages = 1; // this is not really true..
file->m_FileKind = CBaseFile::Kind::NORMAL;
file->m_PageOffset = 0;
file->m_nSector = page_offset * 0x10;
file->m_nLoaded = 0;
file->m_nLength = 0;
return file;
}
/*!
* Locate the entry for a VAG file in the VAG directory.
*/
VagDirEntry* CISOCDFileSystem::FindVAGFile(const char* name) {
u32 packed_name[2];
PackVAGFileName(packed_name, name);
for (int i = 0; i < g_VagDir.num_entries; i++) {
auto& entry = g_VagDir.entries[i];
if (packed_name[0] == entry.words[0] && packed_name[1] == (entry.words[1] & 0x3ff)) {
return &entry;
}
}
return nullptr;
}
/*!
* Get a CISOCDFile* for a newly opened file.
*/
CISOCDFile* CISOCDFileSystem::AllocateFile(const jak3::ISOFileDef* file_def) {
for (int i = 0; i < kMaxOpenFiles; i++) {
auto* file = &g_CISOCDFiles[i];
if (!file->m_FileDef) {
*file = CISOCDFile(file_def, m_Sema[i]);
return file;
}
}
ASSERT_NOT_REACHED();
}
/*!
* Callback from the DVD driver itself into the filesystem. This was originally used for notifying
* when the tray is opened or closed.
*/
void CISOCDFileSystem::DvdDriverCallback(int) {
// the only callbacks that do anything are tray open/close, which we don't care about
ASSERT_NOT_REACHED();
}
// CheckDiscID - not ported
// LoadDiscID - not ported
// ReadSectorsNow - not ported
/*!
* Find all the files on the disc and set up their information. This is modified for the PC port to
* just search for files in the appropriate out folder.
*/
void CISOCDFileSystem::ReadDirectory() {
for (const auto& f :
fs::directory_iterator(file_util::get_jak_project_dir() / "out" / "jak3" / "iso")) {
if (f.is_regular_file()) {
auto& e = g_FileDefs.emplace_back();
std::string file_name = f.path().filename().string();
ASSERT(file_name.length() < 16); // should be 8.3.
MakeISOName(&e.name, file_name.c_str());
e.full_path =
fmt::format("{}/out/jak3/iso/{}", file_util::get_jak_project_dir().string(), file_name);
auto* fp = file_util::open_file(e.full_path, "rb");
ASSERT(fp);
fseek(fp, 0, SEEK_END);
e.length = ftell(fp);
fclose(fp);
}
}
}
/*!
* Load the "Music Tweaks" file, which contains a volume setting per music track.
*/
void CISOCDFileSystem::LoadMusicTweaks() {
ISOName tweakname;
MakeISOName(&tweakname, "TWEAKVAL.MUS");
auto file = g_ISOCDFileSystem->FindIN(&tweakname);
if (file) {
auto fp = file_util::open_file(file->full_path, "rb");
ASSERT(fp);
ASSERT(file->length <= sizeof(gMusicTweakInfo));
auto ret = fread(&gMusicTweakInfo, file->length, 1, fp);
ASSERT(ret == 1);
fclose(fp);
} else {
lg::warn("Failed to open music tweak file.");
gMusicTweakInfo.TweakCount = 0;
}
}
// Crc32 - not ported
// ReadU32 - not ported
} // namespace jak3
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include <string>
#include "game/overlord/jak3/basefile.h"
#include "game/overlord/jak3/basefilesystem.h"
#include "game/overlord/jak3/dvd_driver.h"
#include "game/overlord/jak3/isocommon.h"
namespace jak3 {
void jak3_overlord_init_globals_iso_cd();
CBaseFileSystem* get_file_system();
extern VagDir g_VagDir;
extern MusicTweaks gMusicTweakInfo;
struct CISOCDFile : public CBaseFile {
CISOCDFile();
CISOCDFile(const ISOFileDef* filedef, s32 process_data_semaphore);
int m_nLoaded = 0; // bytes loaded so far
int m_nSector = 0; // next sector to read.
int m_nLength = 0; // bytes that we want to load. 0 for the whole file
CDescriptor m_Descriptor;
EIsoStatus BeginRead() override;
void ReadPages(int sector,
CPage* destination_page,
int num_pages,
char* done_flag_ptr,
bool flag);
EIsoStatus SyncRead() override;
void Close() override;
int RecoverPages(int num_pages) override;
int GetSector() override;
void ReadPagesCallback(Block* block, int error);
};
struct CISOCDFileSystem : public CBaseFileSystem {
CISOCDFileSystem() = default;
int Init() override;
ISOFileDef* Find(const char* name) override;
ISOFileDef* FindIN(const ISOName* name) override;
int GetLength(const ISOFileDef* file) override;
CBaseFile* Open(const ISOFileDef* file_def, int sector_offset, int file_kind) override;
CBaseFile* OpenWAD(const ISOFileDef* file_def, int page_offset) override;
VagDirEntry* FindVAGFile(const char* name) override;
void DvdDriverCallback(int a);
void ReadDirectory();
void LoadMusicTweaks();
CISOCDFile* AllocateFile(const ISOFileDef* file);
// int m_drive_ready_event_flag = -1;
};
} // namespace jak3
+732
View File
@@ -0,0 +1,732 @@
#include "iso_queue.h"
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "game/overlord/jak3/basefile.h"
#include "game/overlord/jak3/dma.h"
#include "game/overlord/jak3/iso.h"
#include "game/overlord/jak3/isocommon.h"
#include "game/overlord/jak3/pagemanager.h"
#include "game/overlord/jak3/spustreams.h"
#include "game/overlord/jak3/vag.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
namespace jak3 {
s32 g_nPriQueueSema = 0;
s32 g_VagCmdSema = 0;
u32 g_auStrmSRAM[6];
u32 g_auTrapSRAM[6];
PriStackEntry gPriStack[2];
extern u32 time_of_last_unknown_rate_drive_op;
u32 g_cmds_with_speed_total = 0;
bool unk_time_mode_flag = false;
ISO_Hdr* g_selected_cmd = nullptr;
bool unk_time_mflag = 0;
s32 unk_sector = 0;
u32 vag_cmd_cnt = 0;
u32 vag_cmd_used = 0;
u32 max_vag_cmd_cnt = 0;
ISO_VAGCommand vag_cmds[16];
static constexpr s32 LOOP_END = 1;
static constexpr s32 LOOP_REPEAT = 2;
static constexpr s32 LOOP_START = 4;
// Empty ADPCM block with loop flags
// clang-format off
u8 VAG_SilentLoop[0x60] = {
0x0, LOOP_START | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, LOOP_END | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
// clang-format on
void jak3_overlord_init_globals_iso_queue() {
g_nPriQueueSema = 0;
g_VagCmdSema = 0;
for (auto& x : gPriStack) {
x = {};
}
g_cmds_with_speed_total = 0;
unk_time_mode_flag = false;
g_selected_cmd = nullptr;
unk_time_mflag = 0;
unk_sector = 0;
vag_cmd_cnt = 0;
vag_cmd_used = 0;
for (auto& x : vag_cmds) {
x = {};
}
}
/*!
* Added function to check if there is a pending DGO load command.
* On PC, DOG loads are really the only loading time that users see. If there is a pending
* DGO load, we can modify logic to take advantage of PCs being dramatically faster than PS2 and
* get much better load times.
*/
bool DgoCmdWaiting() {
for (auto& level : gPriStack) {
for (int i = 0; i < level.count; i++) {
auto* cmd = level.cmds[i];
if (cmd && cmd->msg_type == ISO_Hdr::MsgType::DGO_LOAD) {
if (cmd->m_pBaseFile) {
auto* file = cmd->m_pBaseFile;
if (file->m_Buffer.m_nDataLength) {
return true;
}
}
}
}
}
return false;
}
void InitBuffers() {
SemaParam sema_param;
sema_param.max_count = 1;
sema_param.init_count = 1;
sema_param.attr = 0;
sema_param.option = 0;
g_nPriQueueSema = CreateSema(&sema_param);
ASSERT(g_nPriQueueSema >= 0);
get_page_manager()->Initialize();
g_auStrmSRAM[0] = 0x5040;
g_auTrapSRAM[0] = 0x9040;
snd_SRAMMarkUsed(0x5040, 0x4040);
g_auStrmSRAM[1] = 0x9080;
g_auTrapSRAM[1] = 0xd080;
snd_SRAMMarkUsed(0x9080, 0x4040);
g_auStrmSRAM[2] = 0xd0c0;
g_auTrapSRAM[2] = 0x110c0;
snd_SRAMMarkUsed(0xd0c0, 0x4040);
g_auStrmSRAM[3] = 0x11100;
g_auTrapSRAM[3] = 0x15100;
snd_SRAMMarkUsed(0x11100, 0x4040);
g_auStrmSRAM[4] = 0x15140; // 86384 - 48
g_auTrapSRAM[4] = 0x19140;
snd_SRAMMarkUsed(0x15140, 0x4040);
g_auStrmSRAM[5] = 0x019180;
g_auTrapSRAM[5] = 0x001d180;
snd_SRAMMarkUsed(0x19180, 0x4040);
for (int i = 0; i < 6; i++) {
if (!DMA_SendToSPUAndSync(VAG_SilentLoop, 0x30, g_auTrapSRAM[i], nullptr, nullptr)) {
DelayThread(1000);
ASSERT_NOT_REACHED();
break;
}
}
sema_param.max_count = 1;
sema_param.attr = 1;
sema_param.init_count = 1;
sema_param.option = 0;
g_VagCmdSema = CreateSema(&sema_param);
ASSERT(g_VagCmdSema >= 0);
}
int QueueMessage(ISO_Hdr* msg, int pri) {
msg->status = EIsoStatus::OK_2;
msg->priority = pri;
WaitSema(g_nPriQueueSema);
int queue_idx = (pri == 5) ? 1 : 0;
bool ok = gPriStack[queue_idx].count != 8;
if (ok) {
gPriStack[queue_idx].cmds[gPriStack[queue_idx].count] = msg;
gPriStack[queue_idx].count++;
SignalSema(g_nPriQueueSema);
} else {
msg->status = EIsoStatus::FAILED_TO_QUEUE_4;
SignalSema(g_nPriQueueSema);
ReturnMessage(msg);
ASSERT_NOT_REACHED();
}
return ok;
}
int UnqueueMessage(ISO_Hdr* msg) {
WaitSema(g_nPriQueueSema);
int iVar5 = 0;
PriStackEntry* stack = gPriStack;
do {
int iVar4 = 0;
ISO_Hdr** cmd = stack->cmds;
if (0 < stack->count) {
do {
if (*cmd == msg)
break;
iVar4 = iVar4 + 1;
cmd++;
} while (iVar4 < stack->count);
}
iVar5 = iVar5 + 1;
if (iVar4 < stack->count) {
stack->count = stack->count + -1;
if (iVar4 < stack->count) {
ISO_Hdr** ppIVar3 = stack->cmds + iVar4;
do {
iVar4 = iVar4 + 1;
*ppIVar3 = ppIVar3[1];
ppIVar3 = ppIVar3 + 1;
} while (iVar4 < stack->count);
}
return SignalSema(g_nPriQueueSema);
}
stack = stack + 1;
if (1 < iVar5) {
return SignalSema(g_nPriQueueSema);
}
} while (true);
}
/*!
* Select which command to read for next
* This function considers things like seeking time, reading rates of streamed files,
* and buffer sizing. To be entirely honest, I don't understand it almost at all, and it's not
* clear that it works as expected. It seems to work good enough, and no commands get entirely
* starved of data while there are multiple streams.
*/
ISO_Hdr* GetMessage() {
// bool been_a_while;
// bool bVar2;
// int now;
// int iVar3;
// int iVar4;
// CISOCDFile *file;
// CISOCDFile *pCVar5;
// int iVar6;
// CPageList *plist;
// uint uVar7;
// int iVar8;
// int iVar9;
// PriStack *local_t2_216;
// PriStack *pri_level;
// CISOCDFile *tfile4;
// code *pcVar10;
// CISOCDFile *tfile2;
// CISOCDFile *tfile3;
// uint uVar11;
// ISO_VAGCommand *cmd;
// int idx_on_level;
// ISO_VAGCommand **ppIVar12;
// int iVar13;
// CBaseFile *tfile;
// int cmds_total;
// int iVar14;
// uint uVar15;
ISO_Hdr* cmds_array[16];
u32 read_rates_array[16];
int num_pages_array[16];
int unstepped_pages_array[16];
int remaining_pages_array[16];
// uint its_been_a_while;
// int pages_total;
// int read_rate_total;
// int min_nospeed_pages_total;
// int max_pages_total;
// int min_speed_pages_total;
// ISO_VAGCommand *selected_cmd;
// int cmds_with_speed_total;
// uint cmd2_read_rate;
// int selected_cmd_rem_sectors;
// int pri_level_idx;
// simple logic to select which command to use next.
u32 now = GetSystemTimeLow();
bool been_a_while = false;
if (unk_time_mode_flag == 0) {
been_a_while = 0x384000 < (now - time_of_last_unknown_rate_drive_op);
} else {
unk_time_mode_flag = 0;
time_of_last_unknown_rate_drive_op = now;
}
s32 cmds_total = 0;
get_page_manager()->GarbageCollect();
s32 pages_total = 0;
s32 read_rate_total = 0;
s32 min_nospeed_pages_total = 0;
s32 max_pages_total = 0;
s32 min_speed_pages_total = 0;
LAB_000080e4:
s32 iVar9 = g_cmds_with_speed_total * 400 + 0x2ee;
s32 iVar13 = 0x7fffffff;
ISO_Hdr* selected_cmd = nullptr;
s32 cmds_with_speed_total = 0;
s32 cmd2_read_rate = 0;
s32 selected_cmd_rem_sectors = -1;
s32 pri_level_idx = 1;
PriStackEntry* pri_level = gPriStack + 1;
s32 iVar8 = iVar13;
// loop over priority levels
do {
s32 idx_on_level = pri_level->count + -1;
// if any exist on this level
if (-1 < idx_on_level) {
ISO_Hdr** ppIVar12 = pri_level->cmds + idx_on_level;
// iVar14 = cmds_total << 2;
// loop over commands on this level
do {
ISO_Hdr* cmd = *ppIVar12;
CBaseFile* file = nullptr;
// iVar4 = iVar14;
// basic check if this command is even valid:
if (cmd && (file = cmd->m_pBaseFile, file) && cmd->status == EIsoStatus::OK_2 &&
cmd->active_a != 0) {
u32 read_rate = file->m_ReadRate;
read_rate_total = read_rate_total + read_rate; // maybe this is an inverse rate...
// build up arrays of info for each command
read_rates_array[cmds_total] = read_rate;
cmds_array[cmds_total] = cmd;
if ((int)read_rate < 1) {
min_nospeed_pages_total = min_nospeed_pages_total + file->m_Buffer.m_nMinNumPages;
max_pages_total = max_pages_total + file->m_Buffer.m_nMaxNumPages;
} else {
min_speed_pages_total = min_speed_pages_total + file->m_Buffer.m_nMinNumPages;
cmds_with_speed_total = cmds_with_speed_total + 1;
}
CPageList* plist = file->m_Buffer.m_pPageList;
s32 npages = 0;
if (plist != (CPageList*)0x0) {
npages = plist->m_nNumPages;
}
pages_total = pages_total + npages;
num_pages_array[cmds_total] = npages;
s32 n_untepped_pages = 0;
if (plist != (CPageList*)0x0) {
n_untepped_pages = plist->m_nNumUnsteppedPages;
}
unstepped_pages_array[cmds_total] = n_untepped_pages;
s32 n_remaining_pages = 4;
if (cmd->callback != RunDGOStateMachine) {
n_remaining_pages = n_untepped_pages + file->m_LengthPages - file->m_PageOffset;
// lg::warn("remaining pages is {} = {} + {} - {}", n_remaining_pages, n_untepped_pages,
// file->m_LengthPages, file->m_PageOffset);
}
remaining_pages_array[cmds_total] = n_remaining_pages;
if (remaining_pages_array[cmds_total] < 1) {
remaining_pages_array[cmds_total] = 1;
}
// careful, this increments in a weird spot.
// I use cmds_total - 1 below...
int old_cmds_total = cmds_total;
cmds_total = cmds_total + 1;
// iVar4 = iVar14 + 4;
// next, we'll determine a desired page cutoff and discard commands where
// we have enough pages.
if (read_rate && plist) {
// 3/4 * mNumPages, this is if our allocated buffer is 75% full.
s32 desired_pages = (int)(file->m_nNumPages * 0x30) >> 6;
// but, we want at least min pages
if (desired_pages < file->m_Buffer.m_nMinNumPages) {
desired_pages = file->m_Buffer.m_nMinNumPages;
}
// and we want at least 2
if (desired_pages < 2) {
desired_pages = 2;
}
// but, we never want more pages than we plan to eventually read.
if (remaining_pages_array[old_cmds_total] < desired_pages) {
desired_pages = remaining_pages_array[old_cmds_total];
}
// if we have that many, we can just forget the command - no point in filling it now.
if (desired_pages <= unstepped_pages_array[old_cmds_total])
goto LAB_00008420;
}
s32 iVar14 = iVar9;
if ((0 < (int)read_rate) &&
(iVar14 = (int)(file->m_Buffer.m_nDataLength * 1000) / (int)read_rate,
read_rate == 0)) {
ASSERT_NOT_REACHED();
}
s32 pri = cmd->priority;
// really not sure what this is...
if (been_a_while) {
if ((read_rate == 0) || (iVar14 < 0x2ee)) {
been_a_while = false;
goto LAB_000080e4;
}
// if this isn't the command we said last time.
if (g_selected_cmd != cmd) {
s32 current_sector = file->GetSector();
current_sector = current_sector - unk_sector;
bool sector_ok = false;
if (current_sector < 0) {
current_sector = -current_sector;
if (unk_time_mflag != 0) {
LAB_000083ac:
current_sector = current_sector + 10000000;
}
sector_ok = current_sector < iVar13;
} else {
sector_ok = current_sector < iVar13;
if ((0 < current_sector) && (unk_time_mflag == 0))
goto LAB_000083ac;
}
if (sector_ok) {
iVar13 = current_sector;
iVar8 = iVar14;
selected_cmd = cmd;
cmd2_read_rate = read_rate;
}
}
} else {
// normal selection logic???
if ((((iVar14 == iVar8) && (selected_cmd_rem_sectors < pri)) || (iVar14 < iVar8)) &&
(iVar14 <= iVar9)) {
iVar8 = iVar14;
selected_cmd = cmd;
cmd2_read_rate = read_rate;
selected_cmd_rem_sectors = pri;
}
}
}
LAB_00008420:
idx_on_level = idx_on_level + -1;
/* WARNING: ptrarith problems */
ppIVar12 = ppIVar12 + -1;
// iVar14 = iVar4;
} while (-1 < idx_on_level);
}
pri_level--;
pri_level_idx = pri_level_idx + -1;
} while (-1 < pri_level_idx);
if (selected_cmd) {
if (cmd2_read_rate == 0) {
unk_time_mode_flag = 1;
time_of_last_unknown_rate_drive_op = now;
}
iVar13 = unk_sector;
if (selected_cmd->m_pBaseFile) {
iVar13 = selected_cmd->m_pBaseFile->GetSector();
}
if (unk_sector < iVar13) {
unk_time_mflag = 1;
unk_sector = iVar13;
} else {
been_a_while = iVar13 != unk_sector;
unk_sector = iVar13;
if (been_a_while) {
unk_time_mflag = 0;
unk_sector = iVar13;
}
}
}
min_speed_pages_total = min_speed_pages_total + min_nospeed_pages_total;
g_cmds_with_speed_total = cmds_with_speed_total;
g_selected_cmd = selected_cmd;
pages_total = get_page_manager()->m_CCache.m_nNumFreePages + pages_total;
if ((0 < min_speed_pages_total) && (0 < pages_total)) {
if (min_speed_pages_total == 0) {
// trap(0x1c00);
ASSERT_NOT_REACHED();
}
min_speed_pages_total = (min_nospeed_pages_total * pages_total) / min_speed_pages_total;
if (min_speed_pages_total < min_nospeed_pages_total) {
min_speed_pages_total = min_nospeed_pages_total;
}
if (max_pages_total < min_speed_pages_total) {
min_speed_pages_total = max_pages_total;
}
pages_total = pages_total - min_speed_pages_total;
iVar9 = 0;
iVar13 = min_speed_pages_total;
iVar8 = pages_total;
if (0 < cmds_total) {
// iVar14 = 0;
iVar13 = min_speed_pages_total;
iVar8 = pages_total;
do {
CBaseFile* tfile = cmds_array[iVar9]->m_pBaseFile;
if (read_rates_array[iVar9] < 1) {
s32 uVar11 = (tfile->m_Buffer).m_nMaxNumPages;
if (max_pages_total == 0) {
ASSERT_NOT_REACHED();
}
s32 uVar15 = (tfile->m_Buffer).m_nMinNumPages;
s32 uVar7 = (int)(uVar11 * min_speed_pages_total) / max_pages_total;
if ((int)uVar7 < (int)uVar15) {
uVar7 = uVar15;
}
tfile->m_nNumPages = uVar7;
if ((int)uVar11 < (int)uVar7) {
uVar7 = uVar11;
}
tfile->m_nNumPages = uVar7;
if (remaining_pages_array[iVar9] < (int)uVar7) {
uVar7 = remaining_pages_array[iVar9];
}
tfile->m_nNumPages = uVar7;
// lg::warn("num pages mod {}", uVar7);
iVar13 = iVar13 - uVar7;
} else {
s32 uVar11 = (tfile->m_Buffer).m_nMinNumPages;
s32 uVar7 = (tfile->m_Buffer).m_nMaxNumPages;
// lg::warn("taking else case: {} {} {}", uVar11, uVar7, tfile->m_nNumPages);
s32 uVar15 = -1;
if (read_rate_total == 0) {
LAB_000085e4:
// lg::warn("going to min! (rrt is {})", read_rate_total);
uVar15 = uVar11;
} else {
if (read_rate_total == 0) {
ASSERT_NOT_REACHED();
}
uVar15 = (read_rates_array[iVar9] * pages_total) / read_rate_total;
// lg::warn("read rate math is {}", uVar15);
if ((int)uVar15 < (int)uVar11)
goto LAB_000085e4;
}
// lg::warn("vals {} {}", uVar7, uVar15);
if ((int)uVar7 < (int)uVar15) {
uVar15 = uVar7;
}
// lg::warn("vals 2 {} {}", remaining_pages_array[iVar9], uVar15);
if (remaining_pages_array[iVar9] < (int)uVar15) {
uVar15 = remaining_pages_array[iVar9];
}
iVar8 = iVar8 - uVar15;
tfile->m_nNumPages = uVar15;
// lg::warn("num pages mod 2 {}", uVar15);
}
iVar9 = iVar9 + 1;
// iVar14 = iVar9 * 4;
} while (iVar9 < cmds_total);
}
while (0 < iVar13) {
iVar9 = 0;
s32 iVar14 = iVar13;
if (0 < cmds_total) {
s32 iVar4 = 0;
iVar14 = iVar13;
while (0 < iVar14) {
CBaseFile* tfile2 = cmds_array[iVar4 / 4]->m_pBaseFile;
iVar9 = iVar9 + 1;
s32 uVar11;
if (read_rates_array[iVar4 / 4] == 0 &&
(uVar11 = tfile2->m_nNumPages, (int)uVar11 < remaining_pages_array[iVar4 / 4]) &&
((int)uVar11 < tfile2->m_Buffer.m_nMaxNumPages)) {
tfile2->m_nNumPages = uVar11 + 1;
iVar14 = iVar14 + -1;
}
if (cmds_total <= iVar9)
break;
iVar4 = iVar9 * 4;
}
}
been_a_while = iVar13 == iVar14;
iVar13 = iVar14;
if (been_a_while) {
iVar8 = iVar8 + iVar14;
iVar13 = 0;
}
}
while (0 < iVar8) {
iVar13 = 0;
iVar9 = iVar8;
if (0 < cmds_total) {
s32 iVar14 = 0;
iVar9 = iVar8;
while (0 < iVar9) {
CBaseFile* tfile4 = cmds_array[iVar14 / 4]->m_pBaseFile;
iVar13 = iVar13 + 1;
s32 uVar11;
if (((0 < read_rates_array[iVar14 / 4]) &&
(uVar11 = tfile4->m_nNumPages, (int)uVar11 < remaining_pages_array[iVar14 / 4])) &&
((int)uVar11 < tfile4->m_Buffer.m_nMaxNumPages)) {
tfile4->m_nNumPages = uVar11 + 1;
iVar9 = iVar9 + -1;
}
if (cmds_total <= iVar13)
break;
iVar14 = iVar13 * 4;
}
}
been_a_while = iVar8 == iVar9;
iVar8 = iVar9;
if (been_a_while) {
iVar8 = 0;
}
}
iVar13 = 0;
if (0 < cmds_total) {
iVar8 = 0;
do {
CBaseFile* tfile3 = cmds_array[iVar8 / 4]->m_pBaseFile;
iVar13 = iVar13 + 1;
s32 uVar11 = tfile3->m_nNumPages;
// lg::warn("mystery values: {} - {} = {} (from {})", num_pages_array[iVar8 / 4],
// uVar11,
// num_pages_array[iVar8 / 4] - uVar11,
// cmds_array[iVar8 / 4]->m_pBaseFile->m_FileDef->name.data);
if (((int)uVar11 < num_pages_array[iVar8 / 4]) &&
(iVar8 = tfile3->RecoverPages(num_pages_array[iVar8 / 4] - uVar11), 0 < iVar8)) {
get_page_manager()->GarbageCollectPageList(tfile3->m_Buffer.m_pPageList);
}
iVar8 = iVar13 * 4;
} while (iVar13 < cmds_total);
}
}
return selected_cmd;
}
int ProcessMessageData(ISO_Hdr* tgt_msg) {
EIsoStatus stat;
s32 ret = 1;
LAB_000088a4:
s32 num_remaining = gPriStack[0].count + -1;
if (num_remaining < 0) {
return ret;
}
auto* cmd_array = gPriStack[0].cmds + num_remaining;
ISO_Hdr* cmd = nullptr;
do {
EIsoStatus (*cb)(ISO_Hdr*) = nullptr;
cmd = *cmd_array;
if (cmd && cmd->active_a) {
stat = cmd->status;
if (stat == EIsoStatus::OK_2) {
auto* file = cmd->m_pBaseFile;
auto* buffer = &file->m_Buffer;
if (!file) {
buffer = nullptr;
}
if ((buffer && (buffer->m_eBufferType != CBuffer::BufferType::EBT_FREE)) &&
(cb = cmd->callback, cb != ProcessVAGData)) {
stat = (cb)(cmd);
}
}
if (stat != EIsoStatus::OK_2)
break;
cmd->status = EIsoStatus::OK_2;
}
num_remaining = num_remaining + -1;
cmd_array = cmd_array + -1;
if (num_remaining < 0) {
return ret;
}
} while (true);
if (cmd == tgt_msg) {
ret = 0;
}
ReleaseMessage(cmd);
if (stat != EIsoStatus::IDLE_1) {
cmd->status = stat;
ReturnMessage(cmd);
}
goto LAB_000088a4;
}
void ReturnMessage(ISO_Hdr* msg) {
if (msg->mbox_reply == 0) {
if (msg->thread_to_wake == 0) {
FreeVAGCommand((ISO_VAGCommand*)msg);
} else {
WakeupThread(msg->thread_to_wake);
}
} else {
SendMbx(msg->mbox_reply, msg);
}
}
void ReleaseMessage(ISO_Hdr* msg) {
if (GetThreadId() == g_nISOThreadID) {
auto* file = msg->m_pBaseFile;
if (file && file->m_Status != EIsoStatus::NONE_0) {
file->Close();
}
UnqueueMessage(msg);
} else {
if (msg->msg_type != ISO_Hdr::MsgType::ABADBABE) {
set_active_a(msg, 0);
set_active_c(msg, 0);
msg->msg_type = ISO_Hdr::MsgType::ABADBABE;
SendMbx(g_nISOMbx, msg);
}
}
}
ISO_VAGCommand* GetVAGCommand() {
int iVar1;
u32 uVar2;
u32 uVar3;
do {
while (vag_cmd_cnt == 0x1f) {
DelayThread(1000);
}
do {
iVar1 = WaitSema(g_VagCmdSema);
uVar2 = 0;
} while (iVar1 != 0);
do {
uVar3 = uVar2 + 1;
if (((vag_cmd_used >> (uVar2 & 0x1f) ^ 1U) & 1) != 0) {
vag_cmd_cnt = vag_cmd_cnt + 1;
vag_cmd_used = vag_cmd_used | 1 << (uVar2 & 0x1f);
if (max_vag_cmd_cnt < vag_cmd_cnt) {
max_vag_cmd_cnt = vag_cmd_cnt;
}
SignalSema(g_VagCmdSema);
return vag_cmds + uVar2;
}
uVar2 = uVar3;
} while ((int)uVar3 < 0x1f);
SignalSema(g_VagCmdSema);
} while (true);
}
void FreeVAGCommand(ISO_VAGCommand* param_1) {
u32 uVar1;
int iVar2;
uVar1 = param_1 - vag_cmds;
ASSERT(uVar1 < 16);
if ((uVar1 < 0x1f) && (((vag_cmd_used >> (uVar1 & 0x1f) ^ 1U) & 1) == 0)) {
do {
iVar2 = WaitSema(g_VagCmdSema);
} while (iVar2 != 0);
vag_cmd_cnt = vag_cmd_cnt - 1;
vag_cmd_used = vag_cmd_used & ~(1 << (uVar1 & 0x1f));
SignalSema(g_VagCmdSema);
}
}
} // namespace jak3
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "common/common_types.h"
namespace jak3 {
void jak3_overlord_init_globals_iso_queue();
struct ISO_Hdr;
struct ISO_VAGCommand;
void ReleaseMessage(ISO_Hdr* msg);
void FreeVagCmd(ISO_VAGCommand* msg);
int QueueMessage(ISO_Hdr* msg, int pri);
int UnqueueMessage(ISO_Hdr* msg);
void ReturnMessage(ISO_Hdr* msg);
void InitBuffers();
bool DgoCmdWaiting();
ISO_Hdr* GetMessage();
int ProcessMessageData(ISO_Hdr* msg);
void FreeVAGCommand(ISO_VAGCommand* msg);
ISO_VAGCommand* GetVAGCommand();
struct PriStackEntry {
ISO_Hdr* cmds[8];
int count = 0;
};
extern u32 g_auTrapSRAM[6];
extern u32 g_auStrmSRAM[6];
extern s32 g_nPriQueueSema;
extern PriStackEntry gPriStack[2];
} // namespace jak3
Loaded 100 of 606 files, more files were not shown because too many files have changed in this diff. Show more