Author
|
Topic: [PSX] Any Suikoden II technical experts hanging around here? (Read 1 times)
|
FaustWolf
Guest
|
|
« on: June 24, 2009, 03:15:07 pm » |
|
I've recently become interested in investigating and documenting Suikoden II's sprite assembly, event code, dialogue, etc; basically all its file formats. However, I'm not sure what's been investigated already; I can't find a whole lot of technical discussion on this game anywhere, and given its rarity I fear there may not have been very much technical investigation at all up to this point. Assuming that's the case, I guess the graphics compression algorithm would be as good a place to start as any. Does anyone here who's worked with PSX-era Konami games feel that there's a particular compression routine or at least a general species of compression routine Konami typically used for its sprite-based graphics? For example, some basic knowledge of LZSS sliding window compression gets you surprisingly far in Square Enix PSX-era games. It'd be awesome if Konami used LZSS too, but I'm assuming there's other efficient techniques out there so I'm not holding out too much hope for that. Er, I still need to actually run Suikoden II through a graphics viewer to see if the graphics even *are* compressed, but I'm assuming that they are until I get some time to investigate. If they aren't and the sprites and tilesets are literally just hanging out right in the open, I'll laugh at myself with you all. EDIT -- NOTE TO SELF: Graphics most likely compressed. Next step, compare uncompressed sprites and portraits in VRAM to files in the 120_Play folder and see if any data in game memory dump can be matched to suspected compressed files in that directory. EDIT -- NOTE TO SELF: Fantastic! Experiment proved that the character sprites (as well as portraits!) are contained in the 120_Play folder! Sprite assembly and animation appears to be self-contained, otherwise replacing Nanami with Ada in that folder would have produced a screwed-up Ada. Also note -- Suikoden II keeps its sprite data in VRAM a deceptively long time; I had to manually switch Nanami out of the party to refresh her sprite data. Otherwise it was kept unchanged in the savestate until that point, despite going in and out of battle, in and out of towns, etc. Only things that apparently got messed up during the simple switcharoo experiment: #1. When the swapped character sprite dodges, the game seems to freeze consistently. Ouch. Something's up there, maybe a quirk in the animation or sprite assembly data. #2. While sprite palettes are contained in the character's graphics pack, it would appear that the portrait palette is not. #3. Furthermore, the swapped character's portrait appears at the end of battle (when experience, etc., is tallied) but seems garbled in the party formation menu. Oddly enough, when the swapped character trades places with an arbitrary normal character, the swapped character copies the normal character's portrait. Quite odd. Now on to VRAM examination and comparison with the source file, and then victory! :crazy: If anyone else wants to take a look at the Suikoden II sprite graphics pack format (again, palettes definitely and sprite assembly most likely included), Nanami's at 0xB1DC000 in a 2048 byte/sector ISO. Ada (arbitrary pick since I wouldn't have to repointer if I used a smaller graphics pack, and I don't know for sure how pointers in Suikoden II work yet) is at 0xB0C2800 in a 2048 byte/sector ISO. I strongly suspect compression still, because Nanami's graphics pack looked like a palette followed by compressed gobbledegook in tilemolester. I am so freaking glad that I know what "LBA" means after chatting with more experienced modders some time ago. IsoBuster tells which sector the files start on as long as it can sense a proper table of contents.
|
|
« Last Edit: June 30, 2009, 02:38:04 pm by Tauwasser »
|
|
|
|
David Holmes
Guest
|
|
« Reply #1 on: June 26, 2009, 01:22:51 am » |
|
I haven't looked at Suikoden 2 in much depth (only glanced at dialog, which was uncompressed; easy enough), but I am somewhat interested in it for a possible future project. Do you have a specific goal in mind, or are you just taking it apart for fun?
|
|
|
|
FaustWolf
Guest
|
|
« Reply #2 on: June 26, 2009, 09:36:40 am » |
|
EDIT: Okay, I've finally got (hopefully) the Rosetta Stone of Suikoden II graphics compression. http://www.sendspace.com/file/yjrg02Inside the zip are the following files: Nan 120 Play: Nanami's graphics pack straight from a 2048 byte/sector ISO. Contains (probably uncompressed) palettes and most definitely compressed sprite and portrait data. Looks like it starts with a header table -- I'm most interested in the data starting at 0x31E4; 0x3D74/0x3D94 because judging from the dumped RAM, the sprites were decompressed from data around one of these addresses. NanamiSpriteRAW PERFECT: Uncompressed spritesheet strung together from VRAM data; Microsoft Excel is the most underrated hacking tool evar. :thumbsup: There's probably a bit of extraneous data hanging around either at the beginning of this file or the end, but I wanted to make sure that I caught the whole sprite sheet I saw in VRAM. Format is 4bpp-reversed, 2D. 16-color palette. NanamiPal.zst: The sprite sheet's native palette in a format that Tilemolester can understand. Creaothceann's PVV3 is the second-most underrated hacking tool evar. :beer: RAM Dump: I might be using the term "RAM" wrong here; it's whatever leaps out of pSX when you tell it to "dump memory." It contains large pieces of the compressed Nanami graphics pack, as well as several of the graphics pack's uncompressed palettes. Compare 0x3204~0x3B30 of Nan 120 Play to 0x1428E4~0x143210 of this file; same with 0x3DA0~4950 of Nan 120 Play and 0x143478ish~0x144030 of this file. VRAM Dump: Not a huge deal now that I've teased the uncompressed sprite sheet out of it, but you can throw it into PVV3 and take a look at the state of the game's visuals when I made the RAM/Memory dump. The goal now is to see if there's some recognizable compression method at work in the Nan 120 Play file by comparing sections of it to NanamiSpriteRAW PERFECT. I won't be able to get back to this for a couple weeks to take a good look, so I wanted to dump what I've observed and what I've got here in the meantime in case others are interested. EDIT: Actually, now I'm more interested in the earlier part of Nan 120 Play, from around 0x690 on. While this portion of the file doesn't seem to appear in RAM, it's always possible I didn't dump enough RAM, and there's some short byte strings common to both the compressed file and the uncompressed file here. Going to the uncompressed NanamiSpriteRAW PERFECT and zeroing in on some nonzero byte strings should give you search results in the 120 Nan Play file.
Earlier in the day... My goal is to document as much of Suikoden II's file format as possible, and hopefully someone with programming knowledge will take interest in writing an editor. I just fired up the game again after getting inspired to embark on a possible Suikoden fan project down the road myself, and I was just blown away by the technical quality of the game (far superior to the original Suikoden). It'd be great if there were an entire community devoted to Suikoden modding, but alas, I think it's going to be a long, winding road indeed. Lacking uber capabilities, I succeeded in brute force location of Nanami's uncompressed sprite sheet in VRAM. Problem is, VRAM is viewed two-dimensionally and the bytes would have to be presented in a width that TileMolester just can't reach for them to align correctly -- agh! I got around that by doing some crazy ninja stuff like transferring a section of VRAM to a new image and switching between 8bpp and 4bpp encoding, then widening, to gradually separate Nanami's data from the other VRAM data interleaved with it, and got the uncompressed sprite sheet. Now the problem is, I just realized Tilemolester corrupted the raw data slightly, possibly due to Tilemolester's penchant for assigning the same color to different slots in its default sample palette. Arrrgh! When the sprite sheet distilled from VRAM is compared to the original VRAM data, there's a couple places where 0x00s have been converted into 0x20s, for example. That'll play hell with figuring out the decompression algorithm, because I'm trying to do it by brute force comparison of the compressed and uncompressed data. Since I don't want to piece together the sprite sheet by plucking its disaligned segments straight from VRAM (it seems putting Humpty Dumpty together again would be easier), I'm going to have another go at distilling the sprite sheet via Tilemolester and try making sure there aren't duplicate colors on Tilemolester's default sample palette when I copy the image from one window to another this time around. This would all be so much easier if Tilemolester could just widen images to the width that PSX VRAM uses. Anyone know why Tilemolester is so limited? Is there any other raw image viewer that can exceed Tilemolester's max image width? Again, the current goal is to figure out the graphics decompression routine used in Suikoden II by comparing an uncompressed spritesheet with its compressed counterpart, Nanami being the arbitrary example. I know this is extremely frowned upon by experienced modders (I should learn how to read PSX ASM sometime and just find the decompression routine), but so far I'm one-for-one using this method. Things are looking up because there's only one section of Nanami's sprite file stored in RAM, or whatever the output is when you tell pSX to dump "Memory." My hope is I'll be able to compare that section of RAM with the uncompressed sprite sheet and figure out how the decompression transformed the compressed file into the uncompressed version. If I can get an accurate uncompressed sprite sheet, I'll post that and the part of the compressed graphics file stored in RAM so others more familiar with various compression algorithms might be able to provide some insight. EDIT: Crap, just wasted an entire morning to realize that Tilemolester just isn't cut out for this -- there's too much data corruption when transferring data from one image to another; things like 0x00s turning into 0x0Cs or 0x00s turning into 0x20s. I'll have to use the Humpty Dumpty method after all. Maybe it won't be so bad...now I wish I had a hex editor that could display greater than 256 columns at a time though. Then I'd be able to dump it into a spreadsheet and copy the properly aligned image data into a new hex file. It's so frustrating capturing raw data from VRAM in PSX games!
|
|
« Last Edit: June 26, 2009, 07:12:40 pm by FaustWolf »
|
|
|
|
Tauwasser
Guest
|
|
« Reply #3 on: June 26, 2009, 06:58:01 pm » |
|
EDIT: Okay, I've finally got (hopefully) the Rosetta Stone of Suikoden II graphics compression. NanamiSpriteRAW PERFECT: Uncompressed spritesheet strung together from VRAM data; Microsoft Excel is the most underrated hacking tool evar. :thumbsup: There's probably a bit of extraneous data hanging around either at the beginning of this file or the end, but I wanted to make sure that I caught the whole sprite sheet I saw in VRAM. Format is 4bpp-reversed, 2D. 16-color palette.
Looks much better in 2D 8bpp linear with 31 x 16. There sure are a lot of redundant tables in your compressed data. 3D74 seems to be a header referencing other compressed data. 3D94 is the actual compressed data as far as I could tell. Most of the pointers in the first header will only point to pointers which point to other stuff. There seems so be some indexing logic involved or it just plain knows how much data to read for every address each pointer points to. It also seems like it is actually laid out to do some palette sharing and covers width/height information as it covers multiple pointers for all parts it references which then reference a reference pointer to word-sized tuples containing width and height (or some other tuple) boundaries it seems. It would surely work with some tinkering to get the info out of there. However, it might be nice to have the asm routine that reads those files so you know 100%. cYa, Tauwasser
|
|
|
|
FaustWolf
Guest
|
|
« Reply #4 on: June 26, 2009, 07:10:47 pm » |
|
Tauwasser, this is what I'm getting for NanamiSpriteRAW PERFECT, 4bpp-reversed, 2D and importing NanamiPal.zst for the palette in TileMolester: That should be absolutely correct. If that's not what you got with a 4bpp-reversed 2D codec, I might have included the wrong file in the zip or something. Let me know if that's not what you get with those settings and I'll have to reupload. In any case, thanks for the bump! Now I can do this without my post above becoming obscenely cluttered: EDIT: I made some time to compare unique runs between Nan 120 Play and NanamiSpriteRAW PERFECT. During random investigation, I found unique byte runs shared between the two files at the following addresses: 0x67D9~0x67E2 (Uncompressed) & 0xED3~0xEDC (Compressed); and 0x68A5~0x68AC (Uncompressed) & 0xEE1~0xEE8 (Compressed). The files pass my ad hoc "test for sliding window compression," so it's possible we're dealing with LZ-family compression. However, I haven't been able to conclusively determine that Suikoden II uses sliding window/LZ compression for its graphics; I just haven't been able to disprove it. And now I'm interested in the fact that David's found library-based compression in the Suikogaidens. Care to share any info about that algorithm you saw in the Suikogaiden text files, David Holmes? I'm really, totally, a noobie at modding still, so I'm kind of feeling my way around. Nanami's sprite sheet is written to either address 0x440 in VRAM or address 0xC40 if she's in the top-left party formation slot if I remember correctly, so if someone wants to convert that into a RAM address and run a breakpoint to find the decompression routine, I sure won't complain. My main hangup right now is that I know nothing about PSX ASM, so I haven't bothered to try finding and interpreting the actual decompression code. EDIT: Oooh, just saw all your advice Tauwasser! Thanks! I'll have to ponder on this. EDIT: And just to be clear, I've switched my own investigation from the later addresses in the compressed file like 0x3D74 and 0x3D94 in favor of earlier addresses in the 0x690~0x2C90 range, because only in the earlier portion of the compressed file am I finding any data shared between it and the uncompressed file. Nanami's got a ton of sprites overall, so the data in VRAM covers only a small portion of that (in battle, about 1/2 of her whole battle sheet is in VRAM at any one time). If the game decompresses the entire sprite collection and then plucks a portion of that for VRAM at any one time, that's probably a further roadblock in this effort.
|
|
« Last Edit: June 26, 2009, 07:47:19 pm by FaustWolf »
|
|
|
|
Gemini
Guest
|
|
« Reply #5 on: June 26, 2009, 08:07:11 pm » |
|
The files pass my ad hoc "test for sliding window compression," so it's possible we're dealing with LZ-family compression. Given my past experience with Konami's crap, I wouldn't exclude an enhanced RLE optimized on purpose for pixel art (saves more than any lz algorithm). Nanami's sprite sheet is written to either address 0x440 in VRAM or address 0xC40 if she's in the top-left party formation slot if I remember correctly, so if someone wants to convert that into a RAM address and run a breakpoint to find the decompression routine, I sure won't complain. Psx VRAM doesn't work with offsets, but with coordinates. It's not mapped anywhere as an offset and it can't be accessed with simple reads and writes. If you're looking for the decompression routine look at RAM and see when those frames are decompressed, not when the decompression is already complete and they are ready to be sent to VRAM.
|
|
|
|
Tauwasser
Guest
|
|
« Reply #6 on: June 26, 2009, 10:15:24 pm » |
|
Sorry for the misinfo with the codec, you're right. I was just ignorant that stuff would be significantly wider than 30ish, so I concluded that the codec had to be a power of 2bpp higher. You're right tho. Still, 62 x 16 tiles is pretty bad. I took a look at your data offsets (good job finding it, I looked into it too, but couldn't find stuff that quickly). So basically you have to check out the "header" type thing which will point to pointers to compressed data. The first 5 byte seem to be the size, though it could be first four = size and after that = type or something. The compression method is a mystery. It might actually be some some hybrid thing that can use lzxx and some other method. I found that most stuff that happens to be identical is preceded by binary 11000XXX where X + 8 is the number of bytes that follow uncompressed. However, it will jump/reuse really big stuff in between by seemingly only having 5 bytes or so in its stream - yet it will sometimes only reuse only 3 bytes with the same amount of bytes (in compressed stream) that it can use for hundreds of bytes... This is rather intriguing, keep at it cYa, Tauwasser
|
|
|
|
FaustWolf
Guest
|
|
« Reply #7 on: June 26, 2009, 10:42:16 pm » |
|
June 27 Edit: Here's an updated file investigation zip. The file that's most important here is NanamiSpriteRAW ONESHEET, which conforms to the 32x16 dimensions that Tauwasser will no doubt find more palatable and consists of one-half of the sprite sheet I pulled out of VRAM (it's the left half of NanamiSpriteRAW PERFECT when viewed 2 dimensionally, basically). Both ONESHEET and PERFECT are included in the zip because I'm not sure which reflects the true methodology for sprite storage used in Suikoden II. I thought we'd better have both on hand by the time we start thinking about how the sliding window works, because that depends on previously decompressed data IIRC. ...if we're dealing with LZ-family compression, that is. http://www.sendspace.com/file/9lzral
Yeah, from what I've read Konami's really fond of odd compression methods. The only other Konami game I've seen a decompression algorithm for is the Super Castlevania IV, which RH.net has stored; that looked like a sliding window technique, but that was 5+ years prior to Suikoden II. Do we have any other examples on hand? Square Enix seemed to use a hybrid of LZSS and RLE for its Front Mission 2 portraits, so I wouldn't be surprised if someone at Konami thought it would be cool to borrow that or implement some other hybrid technique. Tauwasser, I think that Suikoden II may be loading two separate sprite sheets side-by-side in VRAM for each character's in-battle sprite, which may account for the odd sprite sheet dimensions compared to other games. Huge thanks for the "pointers," especially for pointing out the binary breakdown of what may be the "control bytes." Looks like we can definitely pull this off with some time. Gemini, do you know of any literature on "optimized" RLE I could read up on? Seems like RLE would be the proper thing to use on the large runs of 00 bytes in the image data, if there's any RLE in the file at all. Otherwise I'm suspecting some kind of straight lz-family compression, but I admit it doesn't seem as straightforward to me as the Front Mission 2 portraits were. EDIT: So far I've got the following possibilities for sample control bytes in the compressed file Nan 120 Play (if this is some kind of LZS or hybrid scheme): 0xC1 writes the next 9 bytes as plain uncompressed data 0x80 writes the next 7 bytes as plain uncompressed data The 0x80 observation plays hell with Tauwasser's formula (0x80, decomposed to 10000000, should produce an 8 byte run according to the formula I think? But then again, that starts with a bin10 and not a bin11). There's one run after an 0x80 I found that looked like it could be 8 bytes, but most of the uncompressed runs following a 0x80 are 7 bytes in length from what I've seen so far. Guess I'll try to find more control bytes like these to see if some general pattern will reveal itself; then I'll take another crack at the header structure. EDIT: After seeing some alarming breaks in logic (later addresses in the compressed file producing earlier sections of the uncompressed VRAM output), I'm attaching a spreadsheet showing how the bytes look in 2D space, just like TileMolester reads them. If you pay close attention to the bytes, you can actually see Nanami's sprites in the spreadsheet as if it were ASCII art; call it Hex art I suppose. The pertinent sheet in the Excel workbook is Sheet 2, so be sure to look at that tab. http://www.sendspace.com/file/98j2lcI think "jumps" that seem illogical in the way I describe above can be explained with the possibility that what appeared in VRAM was in fact two sprite sheets arranged side-by-side. A later section of the compressed file would naturally produce an earlier section of the VRAM output if that section was from a completely different file stored just after the first. I hope I'm describing this effectively; it's a really weird concept, but hopefully the visual will help. Shouldn't be too much of a problem from the standpoint of figuring out some of the general decompression rules for now, but I'll try cropping the NanamiSpriteRAW PERFECT file to one sprite sheet later and see if the "jumps" and some other oddities disappear. Guess the file wasn't so perfect after all.
|
|
« Last Edit: June 27, 2009, 07:13:09 pm by FaustWolf »
|
|
|
|
CaseCrash
Guest
|
|
« Reply #8 on: June 27, 2009, 04:03:20 pm » |
|
Sorry for not adding anything to the discussion, but this is one of the awesomest, most interesting and informing threads I've seen in awhile. Watching someone figuring things out and how they did it and the help of other people is encouraging. Thanks guys. :beer:
</AfternoonTVSpecialMode>
|
|
|
|
FaustWolf
Guest
|
|
« Reply #9 on: June 27, 2009, 06:23:11 pm » |
|
So glad someone bumped it at least -- my posts are getting utterly cluttered with all the edits. If this thread seems more informative than others, it's because I know just enough to get info out of those more experienced than me. I still haven't figured out if Suikoden II is loading one large sprite sheet or two smaller sprite sheets side-by-side. I do, however, strongly suspect sliding window compression. Take a look at this experiment: This is (an attempt at) the breakdown for the uncompressed range 0x68E~0x705 in ONESHEET and 0xCA6~D95 in PERFECT. Regardless of whether it's ONESHEET or PERFECT that's being decompressed from the file, whichever range is correct would correspond to the range 0x711~0x725 in Nan 120 Play. I was surprised that neither really fared that much better than the other, since I thought it would be obvious after this experiment which hypothetical storage method was correct. Uncompressed runs that would appear in the sliding window (part of the file already uncompressed earlier) are grouped by color, with red bytes impossible to derive from the sliding window. In other words, all the red bytes appear in the given order for the first time in the file, and would have to be stored directly as such in the compressed file. An exception to this color rule are the 0x20 bytes at the end of each sample, since the previous run uncompressed from the sliding window could be shortened and some 00s placed in front of the 0x20 for another run successfully plucked from the window. Note that in the sixth row there's a choppy series of short red-colored byte runs that could actually be lengthened to include their adjacent light blue runs and then they'd conform to unique runs in the compressed file. I used light blue to highlight runs that could be derived solely from a sliding window, but are too short to really be efficient. What's really kooky -- and this is what Tauwasser was referring to as "jumps" I think -- is that there's absolutely no way the range 0x711~0x725 in the compressed file can write most of the red bytes; however, later ranges in the compressed file very explicitly contain these very same runs. For now I'm going to hazard a guess that we could be dealing with some kind of "jumping LZS," where the control bytes can give one of the following instructions: a.) Write the following # bytes directly to the file. b.) Jump # bytes backward in the sliding window and write # bytes starting from that address. c.) Jump # bytes backward in the compressed file and follow whatever instructions are there. d.) Jump # bytes foreward in the compressed file and follow whatever instructions are there. That's really...not what I've ever seen before, but considering how strange jumps within the compressed file would handily explain the output shown in the experiment pictured earlier in this post, I think that's almost what it has to be. Next step is to examine the spatial relationship between the range 0x711~0x725 in the compressed file (again, Nan 120 Play) to the later unique run containing the instruction 0xC1 and the literal bytes... BA CC AC 02 A2 2B 44 22 A2 I think that will appear in somewhere in the x1000ish address range of the compressed file IIRC, I'll need to find it again. We might be able to determine if there's a jump-internally instruction in the 0x711~0x725 range based on that, because this is the first appearance of BA CC AC 02 A2 2B 44 22 A2 in the uncompressed data regardless of which storage method is being used for the spritesheets.
|
|
|
|
Tauwasser
Guest
|
|
« Reply #10 on: June 27, 2009, 07:14:10 pm » |
|
Well, I went on and made some compares. I compare the very first data header's files and the output starting at the first sprite. You'll get the idea what I mean with funny jumps. I made pdf files from datafile1.hex (the first compressed datastream referenced) and the whole spritestuff you provided (in *PERFECT data file). Also, you could check out if any of the other three data files (which get all referenced by header the first pointer @ 0x000C -> header @0x31E4 -> pointer list @ 0x2C6C-0x2C7B -> 4 * datafileN.hex). Contain any unique stuff that is between stuff currently marked... I doubt it though. I think this would be the whole first "animation frameset" for whatever Nanami is doing in the first 4 animations (like...umm... jumping? Maybe?). I actually got the feeling that the whole compression is related to the tuple pairs that get referenced right before the pointers get referenced or something. Maybe it's some sort of dictionary stuff. Like, get repetitions that are used very often so you don't have to waste 2..5 bytes on sliding windows, but rather have one-byte opcodes for those... or something. This is really strange. It might be unfeasible to try to decode it by hand (which I've done for a few algorithms by now, but none as strange as this one seemingly). Also, I actually had the idea that the opcodes might also reference other parts of the compressed stream (like, read next x instructions from cur_pos + NNNN). However, that seemed not to be a consistent case. I also thought about bit streaming compression stuff, but it seemed nonsensical when you can find whole strings of literals encoded in the stream which could also be encoded shorter using bitstreaming (there are some 00 encoded literally as well as some stuff that could be accessed through a sliding window setup much shorter). Anyway, here are the extracted/marked files, so grab 'em while they're hot.cYa, Tauwasser
|
|
|
|
FaustWolf
Guest
|
|
« Reply #11 on: June 27, 2009, 08:05:23 pm » |
|
EDIT: Nanami's sprite palette is stored uncompressed in two locations in the Nan 120 Play file (the source file with the compressed data).
First instance of the sprite palette: 0x2C7C~0x2C9C Second instance of the sprite palette: 0x3B38~0x3B58
That clears up two small sections of the file, and maybe it will lead us to some guesses regarding the data surrounding the palettes. However, I'm really wondering why the palette would be stored just *after* the four contiguous files (some of which are) apparently decompressed in PERFECT, as opposed to just before. Usually palettes go just before image data in the source file, so this is a new one for me.
Also, I see that the pattern for writing literal data from the compressed file to the uncompressed file is holding: 0xC1: command for writing the next 9 literal bytes 0xC2: command for writing the next 10 literal bytes 0xC3: command for writing the next 11 literal bytes
I'll see how long this pattern carries on. At least we know the algorithm operates on some kind of control byte scheme.
Oh, baby, those are some nice PDFs Tauwasser! Thanks again for the ideas and the header observations, because I couldn't make sense of those until you pointed them out.
I'm glad that things are stored literally as opposed to bitstreaming, mainly because I'm totally inexperienced with bitstreaming and bitwise operations. "";D The Front Mission 2 portraits (I keep falling back on that game because it's my only experience with decompression routines) also had data stored literally as hex bytes or could be interpreted that way, so I wonder if it's a requirement of Playstation architecture?
EDIT: Aha, I see where the individual compressed files begin now. I can only find 6 files as far as each one identifies itself by signaling its own compressed length in bytes. In Nan 120 Play, the six files housed within start at:
File 1: 0x694 File 2: 0x109C File 3: 0x1D20 File 4: 0x23E4 (Break with special stuff) File 5: 0x3204 (header @0x31E4, which is in the first overall file header) (Break with special stuff) File 6: 0x3D94
The viewer can get a feel for the file structure by looking at the file lengths given at those addresses; the first four files that Tauwasser explicitly separated into the .hex files seem to flow contiguously, then there's some kind of break between that bloc of files and the 5th file, and between that and the 6th file. There are probably pointer tables within those breaks as pointed out by Tauwasser.
Based on the experiment I conducted to identify this file (Nan 120 Play) as containing Nanami's battle graphics in the first place, I also found that the graphics pack must contain her portrait and palette data as well. I'll have to look for her palette in VRAM and see if it's uncompressed in the graphics pack so that we can get that part out of the way at least.
These six files have to account for all of Nanami's battle sprites, her portrait, and maybe something else. I'm under the impression for now that these files in the game's 120 Play folder contain only in-battle graphics and not overworld or field graphics. Mainly because Nanami's file would be ridiculously larger than Ada's (another character's file I gave in a zip a couple posts up as a comparison) if it contained field graphics as well, whereas they're actually comparable in length, Nanami's being larger for the fact that she's got more battle sprites than the other character.
|
|
« Last Edit: June 28, 2009, 01:23:34 am by FaustWolf »
|
|
|
|
Tauwasser
Guest
|
|
« Reply #12 on: June 28, 2009, 08:19:11 pm » |
|
Ok, i tinkered with the header and that's what came to pass: [Number of Struct1 elements (2B)][Number of Struct2 elements (2B)][Number of GFX (1B)][21][00][00][Pointer1 (4B)][Pointer2 (4B)][Pointer GFX (4B)][Pointer PAL (4B)][Pointer Struct1 (4B)][Pointer Struct2 (4B)]
That's the main header. I haven't found out what the 210000 would be, but whatever. Maybe it's some kind of code which parts of the header are to be used as it seems kind of extensible to me. Pointer1 and Pointer2 are either pointers to pointer lists and therefore point to 0x00000000 (as in list is empty), or they are set to 00 by the bytes after [21] or something... Either way, no way to know what they do until we see an example. Pointer GFX is a pointer to a list of pointers which holds number of GFX pointers. Pointer to pal seems to be limited to point to one palette. Or maybe it's the [2 1] or something. Struct1 and Struct2 are somewhat weird. Struct1: It's 14 bytes per entry: [Unknown (4B)][Some counter (4B)][Pointer1 (4B)][Unknown (4B)][Pointer2 (4B)] Pointer1 seems to always point to 4 bytes, whereas pointer2 points to an extensible structure (or maybe one of the values determines its length). Struct2: It's 8 byter per entry: [Number of entries (4B)][Pointer to entries (4B)]. Each entry is 8 byte, all unknown in function. That's all for Nan 120 Play. If you got more examples, do upload them. cYa, Tauwasser
|
|
|
|
FaustWolf
Guest
|
|
« Reply #13 on: June 28, 2009, 09:24:37 pm » |
|
Whoa, thanks for figuring out the main header! :woot!: Here's some more examples of the graphics pack files if you'd be interested in taking a look at them and comparing the overall file structure. I think I'm making some progress on the decompression algorithm, or at least its literal writing behavior. I'm still stepping through a comparison experiment to look at the decompression, but I should have the results up as image files tomorrow. Looks like the byte 0x34 at the end of a compressed string might be a "jump internal" command that tells the decompression algorithm to skip ahead in the compressed file and begin writing from there, but I can't find how it controls the length of address skip. Based on what I've seen, I definitely think library-based compression is a possibility, because I just can't get the sliding window to give me proper results with the byte values available in the compressed stream. However, treating the uncompressed file as if a sliding window were at work still accurately identifies which byte strings are compressed and which are literal in the compressed file, so I'm unsure what to think. In any case, I'll see what the rest of you think once I've got the sample decompressed strings up. There could be some bitstream operations at work that I'm just not picking up on.
|
|
|
|
Tauwasser
Guest
|
|
« Reply #14 on: June 28, 2009, 09:32:41 pm » |
|
Wow, I actually think I finally understood how it all works That is, the concept, not the encoding format. Basically, all four first frames can be derived from datafile1.hex alone. The other data pointed to seem to be some vectors telling it exactly how to do the vram wrapping or something (guess based upon adressing involved, goes in jumps from 0x00, 0x20, 0x40 etc... seems to me like it's actually wrapping 20 tiles per frame around and anding absolute with 0x3FFF... just look at the data that struct1 pointer2 is referencing). Please take a look at this. Basically, it's a comparison over all frames for an animation (in any animation, nothing is going to change much, right?). So it takes the bits that correspond most in all the frames and encodes these literally. Anything else is encoded in some scheme before the data it seems. It now must know which bits to set and reset based on which frame is being produced. With this scheme all "animated" pixels can be compressed as difference matchings to any "base" frame (which does not correspond to any frame actually produced!). I hope this info sheds some light. And yes, it's mindfuck. However, with that knowledge, it seems straightforward to reverse the algorithm now... I actually have some stuff worked out, but I'm not sure about that yet, so I won't post it now. That, and I'm tired.... cYa, Tauwasser
|
|
|
|
|