Skip to main content


2:45am is the perfect time to try to reverse engineer a PS2 game by strategically breaking the filesystem, right?
in reply to Foone🏳️‍⚧️

well I can confirm that if you break the directory names for CHARAL and CHARAS in Robot Alchemic Drive, it gets to the first scene where it's supposed to display them and just softlocks forever
in reply to Foone🏳️‍⚧️

hmm. a switch with 7 subfunctions, two of which have already been identified as LoadLargeChara and LoadSmallChara.

So... generic image loader, or dialogue script evaluator?

in reply to Foone🏳️‍⚧️

the load image looks like:
cVar1 = (**(code **)(*(int *)(param_1 + 0xda8) + 0xc))
(param_1 + 0xda4,param_1 + 0x624,*(undefined1 *)(param_1 + 0xe1c),0);

so we've got C++ with vtables, yay

in reply to Foone🏳️‍⚧️

vtables are contentious in reverse engineering. they can often reveal a lot of structure which is very useful to a reverse engineer.

but they also make everything way more complicated which makes my head hurt

in reply to Foone🏳️‍⚧️

okay thankfully they're using one of those vtable implementations where the first slot points to a string that describes the vtable

so I've located C_Scenario and it seems to have a virtual member function that evaluates individual opcodes

in reply to Foone🏳️‍⚧️

Tomoe_Large_Normal.FACE

well, it's actually 友絵_大_通常

but here's the fun part: it's shift-jis! nooooooo

in reply to Foone🏳️‍⚧️

found some text! it's encoded as shift-jis full width characters, then rendered as not-full-width characters
in reply to Foone🏳️‍⚧️

it's also compressed somehow, it doesn't appear in the ISO, I've only spotted it in RAM
in reply to Foone🏳️‍⚧️

and I can set a breakpoint on the buffer in ram to see when it changes, which gives me the address of... memcpy
in reply to Foone🏳️‍⚧️

BaB::C_Buffer<BaB::C_String *>

YEP. it's a C_Buffer that's templated on C_Strings

in reply to Foone🏳️‍⚧️

from looking at constants in the binary, it looks like their library supports tags like: WAITKEYP, WAITKEY, CLEAR, COLOR, and WAITTIME
in reply to Foone🏳️‍⚧️

extracted some script from RAM:

v1-3-1\x00Hello.<RET>We\u3000bring\u3000you\u3000breaking\u3000news.\x00
v1-3-2\x00It’s...\x00

in reply to Foone🏳️‍⚧️

so each line of script has a name at the beginning, then a nul, then the text, then another nul

so like, the first line of text in the game is v1-3-1, then v1-3-2 through v-1-3-10

in reply to Foone🏳️‍⚧️

and they use <RET> for newlines for some reason. why not just encode them as \n or \r or both?
in reply to Foone🏳️‍⚧️

all this indirection makes it hard to figure out where the compression/encryption code actually is
This entry was edited (1 week ago)
in reply to Foone🏳️‍⚧️

so I have matched up some code from the CONTROL.DY file with the extracted RAM

the snippet:
"tialize\00\xFD\xF8\x97\x09main"

matches up with:
"tialize\0\f8\0\0\0\0\0\x01\0\0\0\0\0\0main"

so... some kind of RLE?

This entry was edited (1 week ago)
in reply to Foone🏳️‍⚧️

okay so it turns out I already found the function that decodes the text:
00159350 TextDecode

So I know where to focus my efforts sometime when it's not 5am

in reply to Foone🏳️‍⚧️

it appears it decodes the text in 16bit chunks, which is gonna be FUN to emulate
in reply to Foone🏳️‍⚧️

so I know which function it is that loads data from a VFS, if I can figure out how to extract the file directory from the VFS (either from the file or the in-ram representation), I can automate calling it for each file and saving the result, so I can extract the whole filesystem
in reply to Foone🏳️‍⚧️

wake up next day, go to doctor, get back, relax into bed and check project status:

reverse a decompression algorithm

well you know I'm still recovering from surgery, I should take it easy, maybe I'll just play Civilization V for a couple hours

in reply to Foone🏳️‍⚧️

there's no part of reverse engineering I hate more than fucking COMPRESSION ALGORITHMS
in reply to Foone🏳️‍⚧️

okay so this compression algorithm first starts by erasing memory in 64-byte chunks, using 8 64bit writes each iteration

erasing a total of... 4k? I think? it counts weird

in reply to Foone🏳️‍⚧️

okay decompression starts at the 4th byte, and it turns out the first DWORD is the decompression size
in reply to Foone🏳️‍⚧️

on the PS2 the scratchpad RAM is RAM that's accessed as normal but only exists inside the CPU. It's got 16 entire kilobytes of it
in reply to Foone🏳️‍⚧️

this would be a lot easier to understand if it wasn't trying to decode 8 bytes at a time
in reply to Foone🏳️‍⚧️

okay I have manually extracted a 285 byte compressed chunk that decompresses to 421 bytes

so I can test the decompression routine I'm gonna have to write

in reply to Foone🏳️‍⚧️

okay I can decode most of a directory listing. now I gotta figure out how different directories are linked together, since this VFS format doesn't have one big index, it's a bunch of hierarchical directories
in reply to Foone🏳️‍⚧️

got it. I can list all the files in all the subdirs. It only supports a top dir + a subdir, you can't nest deeper than that.

There's a total of 1971 files in CONTROL.DY

in reply to Foone🏳️‍⚧️

there's two VFS files (CONTROl.DY and REMOTE.DAN) and they use DIFFERENT DIRECTORY FORMATS

why? because FUCK ME that's why

in reply to Foone🏳️‍⚧️

I think DAN is mostly (or completely?) audio files, and it doesn't look like they compressed any of them.

at least not using their homegrown compression, they're probably some other audio compression format

in reply to Foone🏳️‍⚧️

so what I think is the dialogue files are these genericly named files like:
V58\10c-1

no extension. They start with "BA", a file length, and 2k of zeroes

in reply to Foone🏳️‍⚧️

okay it's not 100% alright (it's clipping in some places) but using Tenacy and importing with VOX ADPCM, offset of 2080, and sample rate of 22050 gives me reasonable-sounding audio
in reply to Foone🏳️‍⚧️

so I can find and extract all the dialogue (not compressed, I have a close enough code that works), and I can find all the other datafiles but not decompress them yet
in reply to Foone🏳️‍⚧️

maybe I can procrastinate on the decompression routine by extracting all the audio, converting it to WAV, and then running it through a voice recognition model
in reply to Foone🏳️‍⚧️

I think my problem with compression is that my ADHD means I am not good at holding a bunch of small numbers in mind. I can work around this limitation in most of my programming/reversing, but compression is inevitably a bunch of small numbers getting masked and bitshifted and indexed
in reply to Foone🏳️‍⚧️

okays so broad strokes of the compression:
it has a 4k scratchpad circular buffer, which it seems to keep updated on each iteration.
it maintains two key variables, which I've named (somewhat arbitrarily) sliding_mask and control.
in reply to Foone🏳️‍⚧️

each iteration through the loop, control is set to sliding mask, and at the end of the loop sliding mask is shifted right by 1.

in the loop, there's a first check:
if sliding mask & 256 == 0, we set control to the 4 bytes off the input pointer (which I call "main pointer" for reasons).
then the sliding mask is set to control OR'd with 0xFF00, and we advance the main pointer one byte

in reply to Foone🏳️‍⚧️

then there's the big check:
does control & 1 == 0?

if so, we go into the RLE logic, which is Complicated

if not, we copy 1 byte from input to output, advance the pointer, and loop

in reply to Foone🏳️‍⚧️

in the RLE logic, control is set to the 4 bytes at the main pointer, OR'd with the second byte anded with 0xF0 and shifted left 4 bits.
then we set write_len to the second byte of main_ptr ANDed with 0xF, and advance the main pointer by 2.
in reply to Foone🏳️‍⚧️

and in the RLE logic, we are copying from the scratchpad to output, using the write_len (plus 2), at offset control
in reply to Foone🏳️‍⚧️

like I've said before, compression isn't my strong point, but this seems like a simpler form of an LZ compression?

just instead of a dictionary, they're using a circular buffer, and they've skipped any huffman coding steps

in reply to Foone🏳️‍⚧️

normally I'd say this was probably hacked in at the last moment to save on space, but this is a 500mb PS2 game, they had space to spare.
in reply to Foone🏳️‍⚧️

yeah and if they didn't compress anything, they'd have wasted about 98 megabytes.

Load speed, maybe? I'm playing on an emulator off an SSD but this might have been a nightmare on a real PS2, especially since they load a lot of files in no particular order and at CD speeds

in reply to Foone🏳️‍⚧️

anyway the main complication in the RLE branch is that it tries to do 8 bytes at a time. I don't know if this was done manually or if it was a compiler trying to inline a memcpy
in reply to Foone🏳️‍⚧️

gonna try and punt and just copy-paste the ghidra code into a C file and see if I can hack it into working without having to fully understand it
in reply to Foone🏳️‍⚧️

of course before I can even get into the decompression I run into 32bit/64bit compatibility problems with just erasing the scratchpad
in reply to Foone🏳️‍⚧️

I changed the type of scratchpad to a byte* instead of a void*, which removed all the int casts but did cause ghidra to unroll setting 64 bytes at once
in reply to Foone🏳️‍⚧️

yay I love code I barely understand working. now to un-hardcode a few things and then try running it on all couple thousand files
in reply to Foone🏳️‍⚧️

int main(int argc,char *argv){

I'm not sure what I hate more:
that this compiles or that I typed it without realizing the obvious mistake

in reply to Foone🏳️‍⚧️

okay I've got it running. extracted all of CONTROL.DY (1,971 files), and REMOTE.DAN (6,182 files)
in reply to Foone🏳️‍⚧️

remaining stuff to do:
1. extract the scripts from the mission briefings / other text files?
2. decode all the voicelines into WAVs
2b. optionally autotranscribe them
3. decode the font? I'm looking at the files and it's mostly obvious, but it may be WAY easier to use my Automatic Visual Font Extraction scheme on it
4. find all the character portraits and extract those
5. find a .JP ISO and re-run all this to see if there's more left in that version
This entry was edited (1 week ago)
in reply to Foone🏳️‍⚧️

huh. it uses a scripting system called "BAT" (not a real batch file, surely?) which seems to be a list of commands.Example file, SIREN.BAT:

openmap map\map01

CameraPos 0 700 0 700 3.14
object object\Siren.vob
objectpad 4
objectteam 1

CameraPos 0 625 0 800 0
probject object\player1.vob object\Siren.vob 0 0
objectteam 0

CameraPos 0 700 0 700 0
console 0
FadeIn 2

in reply to Foone🏳️‍⚧️

the game also definitely has the ability to load a USB keyboard driver (they stuffed the driver inside CONTROL.DY for some reason) but I don't know if that means it actually responds to any keyboard keys.
in reply to Foone🏳️‍⚧️

the MAP folder inside CONTROL.DY has MAP01 through MAP09, MAPBLK, and EFFECTTEST

I'm guessing EFFECTTEST is unused

in reply to Foone🏳️‍⚧️

the first file under OBJECT in CONTROL.DY is "AISAMPLE.VOB"

figures. even in 2002, you can't escape AI slop!

in reply to Foone🏳️‍⚧️

ah-ha! so Robot Alchemic Drive was made by Sandlot in 2002.
In 2003 they released the first of the Earth Defense Force series, Simple 2000 Series Vol. 31: The Chikyū Bōeigun/Monster Attack.

and I just checked: that game uses the same VFS format for its datafiles!

in reply to Foone🏳️‍⚧️

I love hacking one game and finding out I hacked several other games I didn't know about in the process
in reply to Foone🏳️‍⚧️

okay so extracting the script is going to be hard. they are stored inside IEX files, which I think contain binary code as well. So I can't easily statically figure out where the shift-JIS script is, even if it's mostly identical between missions
in reply to Foone🏳️‍⚧️

oh interesting! there's a prototype, which uses a different naming scheme for the two VFS files:
it calls them RAD.EE and RAD.IOP

That's Emotion Engine (CPU/GPU) and I/O Processor.

And that split makes sense for the CONTROL.DY/REMOTE.DAN too: CONTROL is files the CPU/GPU needs, like levels and textures and subtitles, while REMOTE is sound files

in reply to Foone🏳️‍⚧️

which might explain why they're not compressing the REMOTE/IOP files: without a CPU to decompress it, they're just DMAing straight from the DVD into IOP memory
in reply to Foone🏳️‍⚧️

interesting: one of the modules it loads is called ISFSOUND and contains the string "ISF_SOUND_DRIVER_BY_BaB_2001/2002"

"BA" is the magic number for the voice files

in reply to Foone🏳️‍⚧️

my kingdom for debuggers that let me set them to logging only and define what gets logged. I'm so tired of stepping through a bunch of OpenFile breakpoints, and having to click three times on each one to look up what file its trying to read
This entry was edited (1 week ago)

Cy reshared this.

in reply to Foone🏳️‍⚧️

bunch of functions in a raw that are all mysteriously no-ops

somebody used a define to turn off a logging function, I bet

in reply to Foone🏳️‍⚧️

NOPs were sometimes used in assembly as a hacky and precise time delay as executing a nop took a clock pulse.
in reply to Foone🏳️‍⚧️

I finally got the emulator in the right place to inject a different character name, typed in the 40-digit hex number, and hit resume, and waited... and the whole emulator crashed
in reply to Foone🏳️‍⚧️

this would be a lot easier if I understood japanese and my hex editor understood shift-jis
in reply to Foone🏳️‍⚧️

don't get me wrong, I totally understand why my hex editor doesn't understand shift-jis. it'd be a terrible feature to support such an insane text encoding that no one should use
in reply to Foone🏳️‍⚧️

The three versions of the 3 filenames I'm looking at, in shift-jis hex, translated name, original name.

(I need to make a program to compile this shit for me, manually assembling this is a pain!)

in reply to Foone🏳️‍⚧️

also I keep having to retype manually 36-character hex filenames. and not only is that boring and error prone, but remember, I'm me: I immediately start ADHDing off onto the idea of building a keyboard to automate this somehow
in reply to Foone🏳️‍⚧️

if you ask a foone to type in a 36-character string, she'll start thinking about building a keyboard by digit 8, open an IDE at digit 16, and by digit 24 she's got a microcontroller connected over USB
in reply to Foone🏳️‍⚧️

ugh. this is just an artifact of how I'm generating the indexes in program A and printing filenames in program B and there's too much normalization between them
in reply to Foone🏳️‍⚧️

either I need to encode an intermediate file in shift-jis, or refactor program A to output json

the latter is unfun work compared to the ease of the former...
but the former should be considered a crime in many countries. for the good of the internet, do not create more shift-jis files

in reply to Foone🏳️‍⚧️

okay look I don't want everyone to think I took an hour and a half to add CSV export to my scripts and import it into google sheets and make it translate them all

there was some Life Events that took me away from video game hacking for an hour. I don't want you to think I'm losing my touch here

in reply to Foone🏳️‍⚧️

so now I've got a list of every original filename's shift-jis encoded filename (for injection into the game at runtime, to make it load different images), the original kanji, and the translated version. cool.
in reply to Foone🏳️‍⚧️

hmm. I can't replace files on the disk (yet) because I don't have ability to rebuild the archives. and I can't just hex edit the ISO because they're compressed. ugh. I think I'm going to have to do everything in-ram, which is a pain
in reply to Foone🏳️‍⚧️

well I got it to load the wrong level by modifying data while it's loading, but all I got out of it was a mostly blank window and a softlock:
in reply to Foone🏳️‍⚧️

but before it ran the (dialogue) script, I saw where my character was located, and it was in the wrong level. Neat. Progress, even if short-lived
in reply to Foone🏳️‍⚧️

the bare minimum test when doing this kind of reverse engineering: CAN YOU BREAK IT?

in this case, yes, yes I can. The portrait hasn't loaded properly, because I scrambled the filenames. That proves I'm poking in the right place!

in reply to Foone🏳️‍⚧️

ah-ha! the first portrait shown in the second tutorial is Kaoruko News_Small_Tension.

now I can inject over it!

in reply to Foone🏳️‍⚧️

it's the same length, which is important because I don't think I can pad this string. if the string gets shorter, it breaks everything after it
in reply to Foone🏳️‍⚧️

now I just need to automate this. it took about an hour to figure out how to do this, now to do it 203 times in a row
in reply to Foone🏳️‍⚧️

I skipped the keyboard and just edited the savestate as a test.

FUN FACT: this is what happens if you fuck up the filename list! it loads the filename you told it to, then hangs.

This is faster than letting the level finish loading, so now I'm going to crash it on purpose

This entry was edited (6 days ago)
in reply to Foone🏳️‍⚧️

dang it. it doesn't always crash properly. it might crash improperly.

I'm gonna need to carefully crash it.

in reply to Charlie Stross

@cstross "supervillain" is one of the more fun moods to be in while programming.

am I hacking this game the "right" way? absolutely not. I'm hacking it the way that's FUN TO CODE, and damn the "rules"

in reply to Foone🏳️‍⚧️

It's one of the more fun moods to be in when writing fiction, too! (Unless you're straitjacketed by the need to write an MFA-respectable Literary Novel for a publisher you conned into paying you a seven digit advance two years ago and who are now waiting on your doorstep with a baseball bat, tapping their toes impatiently.)
in reply to Foone🏳️‍⚧️

I think between when I wrote this code and now, some library I depend on changed in some way that's non-obvious.

either that or I'm going mad.

in reply to ⚧Kunikleto Celeste⚧

@celestestormysea youtube.com/watch?v=8ZcqaolcjU…

Let's get mischievous and polyamorous

in reply to Foone🏳️‍⚧️

ah-ha! I accidentally programmed my "Waveshare RP2040 One" as a "Waveshare RP2040 Plus"
in reply to Foone🏳️‍⚧️

I should look up the 2bit hack that wrote this keyboard library

unfortunately it's GPL'd to some clown named "Pagliacci"

in reply to Foone🏳️‍⚧️

MOTHERFUCKER CHANGED IN SEPTEMBER:

github.com/earlephilhower/Keyb…

my code is failing because it doesn't call HID_Keyboard::begin() , but the last time I used this code, HID_Keyboard::begin() didn't exist

in reply to Foone🏳️‍⚧️

SHE LIVES

man this library is a nightmare to get working every time I change computers. I need to shiny it up and release it

I've been thinking that above paragraph since 2019

in reply to Foone🏳️‍⚧️

for not the first time and most certainly not the last time, my ass has stopped a program from working
in reply to Foone🏳️‍⚧️

I hit GO on the keyboard-automation program, and let go of the mouse to wait and observe.

unfortunately since I'm still slightly bedridden, I'm in a recliner, and my mousepad is curving over one round arm. So the mouse slipped down, then hit my ass, which made it register a left click, switching away from my PS2 emulator and into File Explorer

in reply to Foone🏳️‍⚧️

autoextracted my first portrait! this is Android_Small_Normal aka アンドロイド_小_通常
in reply to Foone🏳️‍⚧️

yep it worked on 6 of em. Next step, do all 203 of them, at something like 20 seconds an image

See you in an hour and a bit?

(hopefully. otherwise I'll post DAMN IT IT BROKE)

in reply to Foone🏳️‍⚧️

so that's #4 done.

digipres.club/@foone/115760308…


remaining stuff to do:
1. extract the scripts from the mission briefings / other text files?
2. decode all the voicelines into WAVs
2b. optionally autotranscribe them
3. decode the font? I'm looking at the files and it's mostly obvious, but it may be WAY easier to use my Automatic Visual Font Extraction scheme on it
4. find all the character portraits and extract those
5. find a .JP ISO and re-run all this to see if there's more left in that version

#4
in reply to Foone🏳️‍⚧️

working on the backgrounds now. There's only 22 of them, so this hopefully will go quicker?
in reply to Foone🏳️‍⚧️

okay this game 100% has a console in it, I'm seeing tons of references to it.

I wonder if the code to render and interact with it is still here

in reply to Foone🏳️‍⚧️

I don't understand enough about how the PS2 (not PS/2) keyboard works to figure out where these keystrokes are ending up
in reply to Foone🏳️‍⚧️

I found a class called clKksCallback and another called clKksItouCallback. I typed "Itou" into google translate, only to learn it means "Ito"

useful

in reply to Foone🏳️‍⚧️

also I just realized this game lists two programmers: Toshio Noguchi and Yoshihiro Itou

I think Itou just named their callbacks

in reply to Foone🏳️‍⚧️

arg. I finally got my injection code to properly pull out the backgrounds... but they are coming as multiple separate textures, thanks to how they're loaded.
and I don't have the ability to put them back together. I'm gonna have to do something weirder
in reply to Foone🏳️‍⚧️

this is what it looks like. maybe if I can just get her to disappear, I'll just screenshot them out?
in reply to Foone🏳️‍⚧️

okay I've extracted them all, with caveats:

the first two (these BNN ones) are too connected to separate them out, apparently. so I have one screenshot for two filenames

Also, that screen is animated. and I can't really handle that: but it's the only one, so I'm just gonna let it be

in reply to Foone🏳️‍⚧️

actually I guess I can manually jigsaw all these 256x256 chunks together, since I know what the full size works like
in reply to Foone🏳️‍⚧️

nah, quality is virtually identical: 640x460 vs 640x448

so other than removing the animated part there'd be no point

in reply to Foone🏳️‍⚧️

okay so I have all the small portraits and the backgrounds.

next up, the large portraits!

in reply to Foone🏳️‍⚧️

some fun filenames:

Mika 6 years ago, Big Guts
Yamano Dai looks like he's about to cry
Shadow of a fallen mother_Large
The cameraman was delighted
The cameraman panics
The cameraman is very surprised

in reply to Foone🏳️‍⚧️

The animated background:
90 frames of balls

turns out it loops perfectly!

EDIT: Apparently WebP doesn't work here. GIF it is then

This entry was edited (1 day ago)
in reply to Foone🏳️‍⚧️

so the CONTROL.DY file contains two font files:
FONT.P2F
FONTE.P2F

Which one is the dialogue font? NEITHER APPARENTLY

in reply to Foone🏳️‍⚧️

never use an obvious file when you can instead secretly encode another font somewhere else
in reply to Foone🏳️‍⚧️

yeah it seems to be loading (/generating?) 16x16 textures for each letter, then scaling them to 32x32 at runtime.
in reply to Foone🏳️‍⚧️

so it actually is using CONTROL.DY/SYS/FONTE.P2F, it's just scaled so oddly that I didn't recognize it at first
in reply to Foone🏳️‍⚧️

the file doesn't look TOO hard to hack, but I'm not gonna. I'm just going to make it render every character in Latin1/fullwidth latin-1
in reply to Foone🏳️‍⚧️

okay I have text injection. repeatable and automatable.

Also it turns out those special tags, like WAITKEY? only work if you spell them in full-width.
So "<WAITKEY>", not "<WAITKEY>"

in reply to Foone🏳️‍⚧️

ugh. not all fullwidth characters can be encoded into shift-jis, of course.

the whole point of fullwidth in unicode is to enable roundtripping with encodings like shift-jis, but there's 4 codepoints in the U+FF01 to U+FF5E range that aren't encodable in shift-his!

in reply to Foone🏳️‍⚧️

so does the game use non-fullwidth single quotes? I'm gonna have to find some dialogue that uses one
in reply to Foone🏳️‍⚧️

weird dialogue scripting engine: it seems to stop paying attention to tags if it hits a missing character.

I'm rendering "|A|<WAITKEY>" and it pauses, because of the WAITKEY. But if instead of A it's a character that isn't in the font, it renders a blank and then treats <WAITKEY> as plain text, so it doesn't pause, and my screenshotter goes off at the wrong time

in reply to Foone🏳️‍⚧️

okay I have grabbed three unicode blocks:
* general punctuation
* latin-1
* full-width

I haven't found a way to find out what characters are in the font, so that should be good coverage for non-japanese text.
I've just extracted the characters, I'm going to need to spend a while massaging the image->font import script into doing what I need with this image pile

in reply to Foone🏳️‍⚧️

I've been tearing apart Kerbal Space Program's UI the last few days and having similar reactions (several of the button sprites are 64x64 scaled up to 69x69)
in reply to Foone🏳️‍⚧️

I learned from the pixel art folks that it works better on social media (or anywhere that pre-processes uploads) when you upscale by 3 or 4x first.
in reply to Foone🏳️‍⚧️

Oof, I'm expecting compression algorithms to be mostly "I am very smart" piles of abject insanity.
Please promise me you'll take regular sanity breaks.
in reply to disorderlyf

@disorderlyf no news would actually be a comforting change, these days. seems there's a bit too much news
in reply to Foone🏳️‍⚧️

I don't know which one it is but it bothers me so much how this RE tool litters the output with pointer casts rather than inferring correct types for its synthesized locals from the effective type rules.
in reply to Foone🏳️‍⚧️

“Advanced paging architecture“

But that’s just a level above the actual partition.

So just fat32?

in reply to zero-escape

@zero well it's a combination of the PS2 DVD filesystem and the VFS the game implements
in reply to Foone🏳️‍⚧️

can you extract the english dub voicelines? I always wanted a collection of them but my reverse fu is very weak
in reply to Ash

@ash not yet. I need to understand the VFS files better for that, but it's on the TODO
@Ash
in reply to Foone🏳️‍⚧️

please let the world know if the Nanao offing themselves route thing the one gamefaq about this game talks about is true or just a hoax. We could never get it and it sounds the most L is real thing ever.