Author
|
Topic: [PSX] Any Suikoden II technical experts hanging around here? (Read 2 times)
|
FaustWolf
Guest
|
 |
« Reply #15 on: June 29, 2009, 12:01:42 pm » |
|
Whoa, awesomeness! That is a different method of storing sprites. Could this mean that the graphics pack isn't storing all frames, but rather a base frame and then vectors for each animation, and then puts the base frame and vector frame info together to produce other sprites in VRAM? If so, ouch. Reminds me of compressed video streams or something. If you want to see a reference for the sprites in motion to help with theorizing, this is a good example. Also Tauwasser, here's some decompression examples using three short compressed runs that I chose arbitrarily. Non-red colors represent compressed runs and red represents literal runs shared between the compressed and uncompressed byte runs. I tried to match up the compressed runs with their decompressed couterparts as much as possible, but since I'm still not sure how the control bytes are working (and whether the algorithm even needs control bytes all the time) I made some "distribution uncertain" notes below the runs whose division is in question. I believe the byte 0x34 at the end of the first two examples represents a jump forward in the file, because there are literals further on in the compressed file that match the last byte runs marked red. The third example ends with a different command apparently (0xB4), and I'm not sure what it does. I've marked the next few bytes in the third example purple because I'm not sure where they're coming from. It seems they would have to be derived from separate, short literal runs (of one to two bytes each) from various places in the compressed file. Example 1 Just realized I made a mistake in labeling here. The run labeled FB 0E F5 16 6C should be two colors, since it's two separate uncompressed runs and I'm not sure how the distribution is working there. Example 2 Example 3 I was thinking about just tossing the third example since it was so different from the others (I was trying to find compressed runs as similar as possible to reveal the meaning of control bytes and other commands that might be embedded), but then I noticed there's a fantastic example of the decompression routine either adding a previous run to its sliding window or to its library right before our very eyes -- see how the 1C 8B 11 B9 1C 4B DD 00 00 00 00 00 00 00 00 A0 CB run gets incorporated into a later run in that example's uncompressed data? Treating the uncompressed runs as if they were produced by a sliding window showed that these runs do indeed appear further back in the decompressed file whereas the literals do not. However, I've been completely unable to find a relationship between the values in the compressed runs and the production of their uncompressed counterparts via sliding window. The distances the routine would have to go back in the decompressed file-in-progress just aren't matching with any manipulation of the compressed bytes that I can think of. It's possible that I've got the storage method completely wrong -- maybe the sprites are produced one at a time for example, as opposed to in sheets, or maybe the vector nature of storage is throwing me off. Otherwise it might be a library at work, which I need to start reading up on because I'm not sure how the technique differs from sliding windows yet could produce pretty much identical results. There could also be bitwise operations at work that I'm not picking up on for lack of experience.
|
|
« Last Edit: June 29, 2009, 07:28:26 pm by FaustWolf »
|
|
|
|
Next gen Cowboy
Guest
|
 |
« Reply #16 on: June 29, 2009, 12:28:58 pm » |
|
My apologies for chiming in now, but if it is stored like a compressed video stream, than it would not be the first time Konami games have messed with people. This seems to be a near-daily occurrence for them; Kitsune hates them, and we had a fellow working on something else a while back, the credits were stored differently (and I mean completely differently) than anything else. Do not let them fool you one bit =p
|
|
|
|
Tauwasser
Guest
|
 |
« Reply #17 on: June 30, 2009, 04:00:09 am » |
|
Yeah... Can't really tell that much (since I don't have the iso and emulator or any savestates around lol)... Anyway, it seems to me that the 7X in the beginning are each producing X + 1 times 0x00. The byte before them might be some kind of "case selector". Meaning 0xFF 0x7F means in any case put 0x10 times 0x00 and so on. You should be able to test this by just writing 0x00 instead of 0xFF. Then all the sprites produced from this one file should be 0x10 bytes smaller, meaning the sprite would go one pixel up and the colors would go screwy. In this manner you can test the other codes. It seems it also has some other ways of determining stuff since 7X is not always preceded by a bit mask where it would need to be according to this logic (there is one right after the 0xDD that is used in frame 1 and 3. In frame 1 it is preceded by 0x6F times 0x00, in 3 by 0x9E times 0x00. It should be somehow producable out of the 7X preceding the DD in the stream -- which I haven't figured out yet. The 0xCB in there is throwing me off as it could be some other special byte etc. Also, this would mean that there are 8 pictures producible out of this stream instead of just 4. Yeah, it pretty much will eat up much time to figure this out by hand (and is pretty pointless without having the data files and a decent debugger), but you should be able to eventually. It might however be significantly easier to pick up on debugging and just tracing till you find the decompression routine and take a look at that until you understand the decompression algorithm, so you can build your own program for getting the pics at least out of the files. EDIT: All files and pdf. Hex files should be in correct order so you don't have to guess anymore. Here.cYa, Tauwasser
|
|
|
|
FaustWolf
Guest
|
 |
« Reply #18 on: June 30, 2009, 01:55:53 pm » |
|
Some experimental results just in. Firstly I changed the very first byte that I expected served as the first literal, at address 0x698 in Nanami's file. I figured that if the compression algorithm was using a dynamic dictionary, it would add the 0x00 in as the first dictionary value, but now I'm not sure:  Basically changed that very first value to a couple test values, and found that the exact test value made no difference; 0x02 and 0xDD in place of the 0x00 both seemed to pull in an adjacent chunk of VRAM...!? Could this be some kind of reaction the game engine just does when it doesn't know what's going on? At first I thought it might be a command to locate and pull tiles from within VRAM, but it looks like the game is just reacting allergically to an alteration of the first byte. If I switch Nanami around in this experiment, she'll always pull an adjacent section of VRAM just like this. The second experiment, which you suggested Tauwasser, seemed to have more promising results as far as revealing some secrets:  Looks like 0xFF might be a dictionary value standing for a run of 0x00s, and when that's changed, it throws off the rest of the sheet for which that dictionary value serves as the first building block. The major thing I'm getting out of this is that the affected section of VRAM is indeed the first uncompressed sprite sheet, so we know the storage method finally! :beer: Even ONESHEET was too wide. The reason why the the change didn't propagate further is probably that there are separations between the first four compressed runs -- in fact, that 0x00 byte I changed could be a terminator instruction, because I remember seeing that between the compressed sprite sheet files. Now that I know how the sprites are stored I can finally, finally get to work on determining dictionary values with confidence that I'm looking at the proper decompressed output. If you're getting anything else out of these experimental results Tauwasser, let me know. I'm not sure whether the second experiment conforms to your theoretical results, for example. Also, I've been leaning in the direction of dictionary compression now, but I'm wondering if the dictionary would be static and stored somewhere or dynamic and built by the algorithm during the compression phase? It seems to me that a dynamic dictionary built on-the-fly would have to start with a literal, and this obviously isn't unless I'm misinterpreting the first experiment. In any case, now that I know how the output is segmented I can give the sliding window compression possibility one last shot with some comparisons. If it's not sliding window, dictionary would be the logical alternative I guess, but I'm really just basing that on David Holmes' observation on page 1.
|
|
« Last Edit: July 02, 2009, 04:59:17 pm by FaustWolf »
|
|
|
|
Tauwasser
Guest
|
 |
« Reply #19 on: July 03, 2009, 11:23:47 am » |
|
Hmm, that should not have happened at all. It seems however, that all 8 sprites are retrieved from the first file alone... Quite amazing actually if you think about it being only ~0xB00 bytes long (and each of those sprites is 0x400).
Can't really tell you much about it. If it's not what I guessed, it might as well be a 16bit marker for something. The very first byte might be a compression method or some other sort of identifier. You should have tested with 0x01, 0xFF etc... Not just random numbers outside of every bounds.
cYa,
Tauwasser
|
|
|
|
FaustWolf
Guest
|
 |
« Reply #20 on: July 03, 2009, 01:47:01 pm » |
|
Update: I think another opcode is identifiable: 0x01 plus a multiple of four writes (Byte Value - 1)/4 bytes. For example, the value 0x7D writes (0x7D - 1)/4 = 1F bytes! This accounts for why 1F bytes were taken out of the uncompressed stream in one of the experiments when 0x7D was zero'd, and why 19 bytes were taken out of the uncompressed stream when 0x65 [(0x65 - 1)/4 = 19] was zero'd. The opcode is apparently in the format xx yy, where xx is the aforementioned multiple of four-plus-one and yy is an address-based director that tells xx where to start ripping its bytes from. Experiments suggest the following: 7D 8A @ 6BB writes 1F bytes from 0x1AC. 69 90 @ 6BD writes 1A bytes from 0x1B5. 71 8E @ 6C3 writes 1C bytes from 0x1B0. I thought at first that it would make the most sense for yy to be anchored at either the next highest or the next lowest 256-byte boundary in the decompressed output, but now I'm not sure. That would hold for the first two examples the change between 8A and 90 equals the change between 1AC and 1B5, but the third example throws that trend off. I might be making a mistake in my source address observations, so I need to do more experiments to make sure I'm absolutely right on the 0x1AC, 0x1B5 and 0x1B0 observations. Note that these addresses could actually be 0x2C; 0x35; and 0x30 because I just found out that I may have included an extra 0x180 bytes' worth of 00s at the beginning of the 128x128 pixel sprite sheet. Each run seems to have an exogenously determined minimum length, which seems to interrupt the multiple-of-four runs. For the bright green and sky blue runs the minimum length is 6 bytes, but for the pale green run the minimum length is 5 bytes. Zero them all out and you'll get the minimum length. The multiple-of-four-plus-one opcode is confirmed to work for all values up to 0x7D, but it obviously cuts out at a certain value since 0xC1 is also a multiple of four plus 1, and it does something very different. (EDIT: Actually, I've found at least one exception so far. Rats.) EDIT: Actually, it's also possible the yy references for the model opcode in question are relative to the start or end of the previous uncompressed run or otherwise something that changes from one run to the next. That might explain the change discrepancy between the first & second examples and the third.
In any case, using the 128x128 pixel sheet that got screwed up during the second experiment definitely eliminates the odd jumps we were seeing before (so 0x34 isn't a jump command or anything). In other words, the 0xC1 commands are all following in order now when compared to the output. There's a ton of literals strewn throughout though, in addition to the ones generated by the 0xC1 commands. If anyone's interested in checking out patterns with me, these files should provide more accurate comparisons than what we've been doing until now. I should have done an experiment like that to determine how the sprite sheets were stored a long time ago, agh. The spreadsheet contains color-coded run matches and literal strings for a portion of the compressed and uncompressed files. I thought at first that it would be easy to identify library entries marked 0x02 and 0x20 since they appear in a lot of compressed runs, but now I'm thinking that they're just literals too. One thing that's interesting is that the compressed file gets into an "add 0x22 to the next entry" at the very beginning. Values like 0x7C21, 0x7C43, 0x7C65, etc., one after another. Not sure what it means yet.
Aha! New experimental results in:  This is what happened to the data when I changed the 0x7D @0x6B4 in Nan 120 to a 0x00 value. The green section is shortened by 0x1F bytes in total (meaning the 0x7D was responsible for writing either 0x1F or 0x20 bytes depending on whether the 00 in its place now is treated as a literal). More importantly, it's now become clear what the compressed runs are doing. It doesn't look like a dictionary after all -- all the compressed runs just seem to be acting on past data with the exception of the 0x2E 0x1E compressed run in yellow, which is actively writing 00 bytes or else it would have propagated the errors introduced previously. Note that the errors do get propagated throughout the sprite sheet, so runs further on must be reaching back into the garbled up runs seen in the example. I guess this rules out dictionary-based compression, or at least reduces the range of values in the compressed runs that could be associated with dicionary-based compression. It seems more like some version of LZSS from hell now.
More experimental results in. The first compressed green run shows the nature of each experiment (just zeroing out certain bytes in that compressed run):    Fascinatingly, there seems to be no purpose to the second and fourth bytes in that first compressed run, or at least they have the effect of producing one zero each. Also, zeroing the second and fourth bytes one at a time had absolutely no effect on the file. Also, it seems the compressed green run can produce six zeros with just four...somehow. EDIT: Updated the experimental results with an address grid of the decompressed output. The algorithm can't be copying previous runs relationally, otherwise the 0xD0 21 bytes wouldn't be corrupting the sky blue uncompressed run in some results but not others. Whether the 0xD0 21 is copied into the sky blue uncompressed run depends completely on the raw address at which 0xD0 21 appears, it would seem... Never seen anything like this before, that's for sure.
|
|
« Last Edit: July 05, 2009, 11:37:10 am by FaustWolf »
|
|
|
|
Gemini
Guest
|
 |
« Reply #21 on: August 18, 2009, 03:26:53 pm » |
|
Seems like this is the right place to post these Action Replay codes: 3006AE62 0007 D010E8C8 0052 3010E8C8 0053 What do they do? They enable you to use McDohl directly from the party switch menu, instead of going the long way by visiting his house in Gregminster. The codes are pretty simple to explain. The first one enables a flag used by the party screen to tell if a character has joined you, but McDohl's is always zeroed even if you actually recruited him. The second and third ones alter this instruction in the code: 8010E8C8: slti $v0, $s1, 0x52 Which translates to a for like this: for(int s1=0; s1<0x52; s1++) But McDohl's ID is 0x52, so we need to make it check for him too by changing the value to 0x53. There you go, a nice working selection screen:  PS: Codes work on every version out there.
|
|
|
|
FaustWolf
Guest
|
 |
« Reply #22 on: August 21, 2009, 01:38:54 pm » |
|
Maybe this would be a good time to start accumulating RAM address functions for Suikoden II, and make a sort of Memory Map.
Is 8010E8C8 a RAM address that specifies which character takes the last place in the inactive party menu, or how is this working exactly? Did the forced change from a value o 0x52 to 0x53 cause the game to overwrite another character with Tir McDohl?
I'm obviously behind on figuring out the graphics decompression routine, but I plan to keep working on it. It seems to be analogous to LZSS and I think I've got one of the most important opcodes partially figured out as explained in my previous post (Tauwasser figured out the opcodes for literal strings too, so that's two down already), but there's still some kinks that need to be figured out through sheer experimentation.
I guess part of me is also waiting to see if Konami's "unnamed PS3 RPG" is a main series Suikoden, since a continuation of the main series might kill interest in modding the already-existing games.
|
|
|
|
Gemini
Guest
|
 |
« Reply #23 on: August 21, 2009, 02:00:44 pm » |
|
Is 8010E8C8 a RAM address that specifies which character takes the last place in the inactive party menu, or how is this working exactly? That's just a value in a table used by the game to determine the status of a particular character (7 means the character has been recruited and is still alive, but sometimes this value is 6). I have no clue how big the table is, but it's probably around 115 values and starts from 8010E861. Did the forced change from a value o 0x52 to 0x53 cause the game to overwrite another character with Tir McDohl? Nope. The whole cast is still there.
|
|
|
|
Velsper
Guest
|
 |
« Reply #24 on: October 08, 2009, 12:28:38 pm » |
|
Might be a tad late, but you didn't happen to come across the script while you were tinkering around?
|
|
|
|
FaustWolf
Guest
|
 |
« Reply #25 on: October 09, 2009, 09:31:27 pm » |
|
As a matter of fact, yes! I wanted to dump the Suikoden II script since I haven't seen a full version out there, but ultimately went with the Dragon Force II translation project instead. Here's what you do: launch the game and look at the first line of story text. Then get David Holmes' FAQ: http://www.gamefaqs.com/console/psx/file/198844/7234And run a search for that line of text, converting the line from the English text you see on screen to the relative alphabet (remember to include the relative alphabet's space characters and capitals, etc). Searching for that in a hex editor will give you the general neighborhood of the file, and you can see which file(s) the text is stored in exactly by looking at the ISO in ISOBuster or other program that can read the game's CD file table. Converting the script will probably be as easy as setting up a VLOOKUP table in Microsoft Excel and looking up each relative alphabet value in that. Otherwise, I'm not sure if anyone's written a dumper for it. I don't think it's compressed in any way, since I was able to find specific lines of story dialogue in the ISO with a hex editor just using the alphabet.
|
|
|
|
Velsper
Guest
|
 |
« Reply #26 on: October 10, 2009, 04:54:27 am » |
|
Interesting. I did like you said and I've managed to locate the script to the VA0X.bin files. Thanks! It's pretty garbled though but at least I know where it is. Too bad my attempts at changing the script around a bit just caused the game to crash though. EDIT: Aha... turns out UltraISO just wasn't up to the job. Using CDmage instead I was able to get the game to change Nanami to Jackie in the beginning. EDIT: Working out better than I expected. Now I just need to figure out how to put in names that are longer than the original without crashing the game  
|
|
« Last Edit: October 10, 2009, 05:44:55 pm by Velsper »
|
|
|
|
ffgriever
Guest
|
 |
« Reply #27 on: October 11, 2009, 12:37:02 pm » |
|
I don't know if it will be of any help (sorry, too much to read for me today, tired as hell), but one year ago I helped a little in one Suikoden II related project. One tool was decompressor and compressor. Can't find the code for compressor, but I posted the source for decompressor on one of the forums. Here is the copy Sorry, but the names are quite weird, plus it may seem a little bit chaotic at first (it's manually decompiled mips assembly). It worked fine with graphics and most other things. I also should have somewhere tools needed for translations (game text script editor). You just have to realize, that in most cases SUI2 files are not "normal" files. They're more like dlls. Loaded into memory, all have their own executable code and such. When the location or whatever is done, they're unloaded and replaced by others. This makes some things much harder than the usual files (it's much harder, because sometimes you have to relocate entire code sections, all reads, writes, jumps, etc... ). //size_pac - compressed file length //packed - compressed data buffer //unpacked - uncompressed data buffer //decompressor stub translated into c++ directly from asm (original file SLUS_009.58) //decompression function is placed at 0x80075F58 in memory (main executable section)
void decompress(u32 size_pac, u8* packed, u8* unpacked) { u8 frame[1024]; u8 ctrl; u32 tempCtrl; u8* end = packed + size_pac; u32 frameOffset = 0x3de; u8* currFrame = &frame[frameOffset]; u32 remain; u32* tempFrame = (u32*)frame; while ((u32)tempFrame < (u32)currFrame) { *tempFrame = 0; tempFrame++; } while (1) { ctrl = *packed; packed++; if (packed >= end) { return; } tempCtrl = ctrl | 0xff00;
do { if (tempCtrl & 1) { s8 tempCtrl2 = *packed; packed++; s32 cnt; if ((u8)tempCtrl2 < 0x80) { remain = *packed; packed++;
u8 temOff = (tempCtrl2 & 3); tempCtrl2 = (tempCtrl2 >> 2) + 2;
remain |= (temOff << 8); cnt = 0; if (tempCtrl2 >= 0) { do { currFrame = &frame[frameOffset]; frameOffset++; frameOffset &= 0x3ff;
u8* tempFrame = &frame[(remain + cnt) & 0x3ff]; ctrl = *tempFrame; *unpacked = ctrl; unpacked++; *currFrame = ctrl; cnt++; } while (cnt <= tempCtrl2); } } else if ((u8)tempCtrl2 < 0xc0) { remain = tempCtrl2 & 0xF; tempCtrl2 = ((tempCtrl2 & 0x30) >> 4) + 1; cnt = 0; if (tempCtrl2 >= 0) { do { u32 tempFrameOffset = frameOffset - remain; currFrame = &frame[frameOffset]; frameOffset++; frameOffset &= 0x3ff; tempFrameOffset &= 0x3ff; u8* tempFrame = &frame[tempFrameOffset]; ctrl = *tempFrame; *unpacked = ctrl; unpacked++; *currFrame = ctrl; cnt++; } while (cnt <= tempCtrl2); } } else { tempCtrl2 = (tempCtrl2 & 0x3f)+7; cnt = 0; if (tempCtrl2 >= 0) { do { ctrl = *packed; packed++; currFrame = &frame[frameOffset]; *unpacked = ctrl; unpacked++; *currFrame = ctrl; frameOffset++; if (frameOffset >= 0x400) { frameOffset = 0; } cnt++; } while (cnt <= tempCtrl2); } } } else { ctrl = *packed; packed++; if (packed >= end) { if (packed == end) { *unpacked = ctrl; } return; }
*unpacked = ctrl; currFrame = &frame[frameOffset]; *currFrame = ctrl; frameOffset++; unpacked++; if (frameOffset >= 0x400) { frameOffset = 0; } } tempCtrl >>= 1; } while (tempCtrl & 0x100); } }
|
|
« Last Edit: October 11, 2009, 12:43:46 pm by ffgriever »
|
|
|
|
Velsper
Guest
|
 |
« Reply #28 on: October 11, 2009, 01:53:14 pm » |
|
I also should have somewhere tools needed for translations (game text script editor).
You just have to realize, that in most cases SUI2 files are not "normal" files. Well, as far as I can tell it's impossible for me to recompile the game after fixing the scripts. All methods I tried result in an endless black screen. Not to say it's impossible, just that I don't have the skills needed.
|
|
|
|
ffgriever
Guest
|
 |
« Reply #29 on: October 11, 2009, 02:02:17 pm » |
|
Nah, my editor worked just fine. It has been used in two translation projects.
|
|
|
|
|