don't click here

SADX/SA1 Hacking/Modding

Discussion in 'Engineering & Reverse Engineering' started by MainMemory, Aug 15, 2009.

  1. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    I guess so, because it works when I do it.
     
  2. Shoemanbundy

    Shoemanbundy

    Researcher
    1,094
    30
    28
    Chicago, Illinois
    selling shoes
    I got it now. I wasn't saving the exe :v: The fact it says it extended it after you choose to do so gave me the impression it's done and that's it, only occurred to me to actually look at the exe size to see if it's actually being extended, which it wasn't.
     
  3. Ch1pper

    Ch1pper

    Fighting the Battle of Who Could Care Less Member
    840
    102
    43
    Life.
    Curiousness:

    I nabbed the EU version on eBay a few years ago. Playing through it, I was surprised that when I wound up in the Past, that percussive track used at the echidna temple was noticeably faster compared to the tempo of the Mystic Ruins theme. I've assumed it was something to do with the "NTSC stuff is faster on PAL to compensate" business that would have been needed for the original console version.

    I've been curious to find a US Version of whatever file (forget the name) that contains the Past track to see if the conversion theory is the case.

    Any guesses or confirmation?
     
  4. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
  5. Ch1pper

    Ch1pper

    Fighting the Battle of Who Could Care Less Member
    840
    102
    43
    Life.
    Ah, much thanks.

    Now to reinstall the game after a fresh reboot. :v:

    Update: Well, they're one in the same. Sorry for the waste of time. I guess the speed difference just isn't that noticeable until the Mystic Ruins theme gets pounded into your head from continuous game playing.
     
  6. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Updated obj2nj, SADXMDL and SADXLVL, they stored colors from obj imports backwards before.
    I also ported a feature from SADXMDL to SADXLVL: clicking on the color preview for diffuse or specular in the Material Editor displays the Microsoft color picker dialog.
    And I got tired of SADXLVL having version numbers that are just made up, so now releases with new features are +1, and releases with only bugfixes are +0.1. I'm starting with 34.0, based on the version history from the wiki page, and adding 5 for the releases before that aren't listed.
     
  7. 1)It looks like SADXLVL is incompatible with SADX MIP pack. :( Previously after selecting a level to edit I every time got a an "example of object doesn't contain a reference to an object" error:
    [​IMG]
    but some guy give me advice to use non-mipmap textures, and almost everything worked fine since that.
    2)How can I edit water and sea bottom in SADXLVL?
    3)There are some strange level part I cannot do anything with. I can't see it in SADXLVL. Looks like, as Sonic's blue half-opaque shield, it doesn't have any texture at all... EDIT: OK, I can delete it with along with some near level parts...
    [​IMG]
     
  8. Dude

    Dude

    Tech Member
    3,138
    0
    16
    Southbridge, MA
    Random VR/AR trash
    This is known, and is actually the reason 'clear level' function breaks emerald coast. The only suggestion I can give to you is to rip the level using another tool, find those objects and clear them out using a quick little hex hack or something (like zeroing the mesh and material numbers).
     
  9. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Water is usually an invisible flat model with the Water flag (0x00000002) set, and everything between the model and the next solid model/object is underwater. You'll see them if you press 'B' or use the dropdown in the view menu. The part you see ingame is usually an object loaded by the level.
     
  10. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    SADXsnd is very limited. It can only extract/pack an entire archive at once, and it's a command line program.
    So I looked at the source, and wrote SADXsndSharp in C# 2008.
    With it, you can view, add, remove, replace and extract any files from a DAT archive by right clicking on it in the list view.
    [​IMG]
    Credit to Tux/SANiK for SADXsnd.
     
  11. FraGag

    FraGag

    Tech Member
    For those of you who don't check the Community SVN Project forum often, I'll let you know that I've committed Sonic Adventure Tools and the decompilation of CHRMODELS.DLL from SADX PC.

    As a courtesy, I'm providing you with a build of the "new" CHRMODELS.DLL. It should be stable, but if you encounter any problems with it, go to the Sonic Adventure Tools topic and post about it there (how to reproduce, what happens).
     
  12. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    And related to that, I'm giving you two things:
    SAM Ripper converts a model tree and animations into a .sam file.

    SADXMDL2, which edits .sam files. It has the same features as SADXMDL, but can also add and remove models in the tree, and can export to C files for compiling with the MSVC project on the SVN. However, it does not yet export animations, so if you add/remove models, you'll have to edit the C files for the animations manually.
     
  13. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Anybody with a 64-bit OS should redownload, it doesn't crash anymore.
     
  14. I've got a little problem with SADXMDL and SADXMDL2. Both of them don't show the models when I load them. It's just black.
     
  15. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Unfortunately, I have no idea what could be causing that.

    Here's something found by Highter from TiggoMods (don't go there): Metal Sonic's voice files!
    In system\sounddata\voice_us\wma,
    2044.wma = Y_MS_CLEAR - Level clear
    2045.wma = Y_MS_DAMAGE - Hurt
    2046.wma = Y_MS_DEAD - Death
    2047.wma = Y_MS_SELECT - Character select
    2048.wma = Y_MS_START - Unused?

    Note: the uppercase part in the middle is the GC filename from EVENT_ADX_E.AFS.
     
  16. Aquaslash

    Aquaslash

    <The Has-been Legend> Moderator
  17. SANiK

    SANiK

    Tech Member
    413
    0
    16
    MainMemory sent me the decompress routine that the SADX installer supposedly uses.

    So here is a simple program that does the decompression. Rename the input file to in.dll.
    Code (Text):
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4.  
    5. #define __ //For posting on forum. Do a CTRL+F to get rid of it.
    6.  
    7. void DecompressBuffer(unsigned char *DecompressedBuffer, unsigned char *CompressedBuffer /*Starting at + 20*/, int DecompressedSize)
    8. {
    9. __ unsigned char *CompressedBufferPointer = CompressedBuffer;
    10. __ unsigned char *DecompressedBufferPointer = DecompressedBuffer;
    11.  
    12. __ //Create sliding dictionary buffer and clear first 4078 bytes of dictionary buffer to 0
    13. __ unsigned char *SlidingDictionary = malloc(4096);
    14. __ memset(SlidingDictionary, 0, 4096);
    15.  
    16. __ //Set an offset to the dictionary insertion point
    17. __ unsigned int DictionaryInsertionOffset = 4078;
    18.  
    19. __ //Decompression command
    20. __ unsigned char CommandCounter = 0;
    21. __ unsigned char DecompressCommand = 0;
    22.  
    23. __ while(DecompressedSize)
    24. __ {
    25. __ __ //Is the decompress counter zero? Load the command
    26. __ __ if(CommandCounter == 0)
    27. __ __ {
    28. __ __ __ CommandCounter = 8; //Each command has 8 sub commands, one bit per command
    29. __ __ __ DecompressCommand = *CompressedBufferPointer++;
    30. __ __ }
    31.  
    32. __ __ //Each command is a byte and is actually composed of 8 sub commands
    33. __ __ //A bit of 1 means to copy the byte exactly as it is, and add it to the dictionary
    34. __ __ //A bit of 0 means to load a special encoded format describing a repetition.
    35. __ __ if(DecompressCommand & 1)
    36. __ __ {
    37. __ __ __ //Copy the byte exactly over
    38. __ __ __ unsigned char RawByte = *CompressedBufferPointer++;
    39. __ __ __ *DecompressedBufferPointer++ = RawByte;
    40.  
    41. __ __ __ //Add the byte to the dictionary
    42. __ __ __ SlidingDictionary[DictionaryInsertionOffset] = RawByte;
    43. __ __ __ DictionaryInsertionOffset = (DictionaryInsertionOffset + 1) & 0xFFF; //Slide the dictionary
    44.  
    45. __ __ __ DecompressedSize--;
    46. __ __ }
    47. __ __ else
    48. __ __ {
    49. __ __ __ //The sub command tells us there is a repetition
    50. __ __ __ //unsigned short RepetitionCode=(CompressedBufferPointer[1] << 8) | CompressedBufferPointer[0];
    51. __ __ __ unsigned char CurrentByte = CompressedBufferPointer[0];
    52. __ __ __ unsigned char NextByte = CompressedBufferPointer[1]; //Lower nibble is the repetition count or RunLength
    53. __ __ __ CompressedBufferPointer+=2;
    54.  
    55. __ __ __ //Calculate the offset of the byte to use in the sliding dictionary
    56. __ __ __ int DictionaryOffset = ((NextByte & 0xF0) << 4) | CurrentByte;
    57.  
    58. __ __ __ //It is not really run length compression, but instead it is a dictionary based method
    59. __ __ __ //I just ran out of ideas what to name the variables
    60. __ __ __ int RunCounter = 0;
    61. __ __ __ int RunLength = (NextByte & 0xF) + 3; //Compression defines a repetition to be at minimum three bytes
    62.  
    63. __ __ __ while ( RunCounter < RunLength)
    64. __ __ __ {
    65. __ __ __ __ unsigned char RawByte = SlidingDictionary[((RunCounter + DictionaryOffset) & 0xFFF)];
    66. __ __ __ __ *DecompressedBufferPointer++ = RawByte;
    67.  
    68. __ __ __ __ DecompressedSize--;
    69. __ __ __ __ if(DecompressedSize <= 0)
    70. __ __ __ __ {
    71. __ __ __ __ __ free(SlidingDictionary); //Terminate
    72. __ __ __ __ __ return;
    73. __ __ __ __ }
    74.  
    75. __ __ __ __ //Add the byte to the dictionary
    76. __ __ __ __ SlidingDictionary[DictionaryInsertionOffset] = RawByte;
    77. __ __ __ __ DictionaryInsertionOffset = (DictionaryInsertionOffset + 1) & 0xFFF; //Slide the dictionary
    78.  
    79. __ __ __ __ RunCounter++;
    80. __ __ __ }
    81. __ __ }
    82.  
    83. __ __ //Rotate the sub command
    84. __ __ CommandCounter--;
    85. __ __ DecompressCommand >>= 1;
    86. __ }
    87.  
    88. __ free(SlidingDictionary);
    89. __ return;
    90. }
    91.  
    92. int isFileCompressed(unsigned char *CompressedBuffer)
    93. {
    94. __ return !strcmp(CompressedBuffer, "compress v1.0");
    95. }
    96.  
    97. void ProcessBuffer(unsigned char *Name, unsigned char *CompressedBuffer, int CompressedSize)
    98. {
    99. __ if ( CompressedBuffer )
    100. __ {
    101. __ __ if ( isFileCompressed(CompressedBuffer) )
    102. __ __ {
    103. __ __ __ int DecompressedSize = *(unsigned int*)(&CompressedBuffer[16]);
    104. __ __ __ unsigned char *DecompressedBuffer = malloc(DecompressedSize);
    105. __ __ __ if ( DecompressedBuffer )
    106. __ __ __ {
    107. __ __ __ __ //Xor Decrypt the whole buffer
    108. __ __ __ __ unsigned char XorEncryptionValue = CompressedBuffer[15];
    109.  
    110. __ __ __ __ unsigned int I=0;
    111. __ __ __ __ while(I < CompressedSize)
    112. __ __ __ __ {
    113. __ __ __ __ __ CompressedBuffer[I] ^= XorEncryptionValue;
    114. __ __ __ __ __ I++;
    115. __ __ __ __ }
    116.  
    117. __ __ __ __ //Decompress the whole buffer
    118. __ __ __ __ DecompressBuffer(DecompressedBuffer, &CompressedBuffer[20], DecompressedSize);
    119.  
    120. __ __ __ __ //Switch the buffers around so the decompressed one gets saved instead
    121. __ __ __ __ CompressedBuffer=DecompressedBuffer;
    122. __ __ __ __ CompressedSize=DecompressedSize;
    123. __ __ __ }
    124. __ __ }
    125.  
    126. __ __ //Save file
    127. __ __ FILE *fout=fopen(Name, "wb");
    128. __ __ fwrite(CompressedBuffer, CompressedSize, 1, fout);
    129. __ __ fclose(fout);
    130. __ }
    131. }
    132.  
    133. int main()
    134. {
    135. __ FILE *fin=fopen("in.dll", "rb");
    136.  
    137. __ fseek(fin, 0, SEEK_END);
    138. __ int CompressedSize=ftell(fin);
    139.  
    140. __ fseek(fin, 0, SEEK_SET);
    141.  
    142. __ void *CompressedBuffer=malloc(CompressedSize);
    143. __ fread(CompressedBuffer, CompressedSize, 1, fin);
    144.  
    145. __ fclose(fin);
    146.  
    147. __ ProcessBuffer("out.dll",CompressedBuffer, CompressedSize);
    148.  
    149. __ return -1;
    150. }
    The file format is as so:
    At 0, there is a 14 byte null terminated string, "compress v1.0", no quotations.
    14 is 0.
    15 is a byte that is the xor encryption password.
    Like MainMemory said, at 16 there is a dword denoting the decompressed file size.

    At 20 is where the data starts.

    To read the data you must xor the whole file or at least the data and on portion with the xor encryption password, byte by byte. C xor operator is ^.

    The data consists of packets. The first byte of each packet is a byte command.
    Each packet, excluding the command byte, can be divided into eight uneven sections. Each bit of the command denotes what operation to do for each of the sections.

    The first lower bit of the command byte corresponds to the first section after the command byte.
    The second lower bit corresponds to the section after the first section, etc.

    If the command bit is 1 for the section, it means the section is only one byte, and so it gets copied to the decompression file.
    It also gets put into a sliding dictionary.

    If the command bit is a 0 for the section, it means the section is two bytes, and the two bytes describe an address in the sliding dictionary and the number of bytes to copy to the decompressed file from the sliding dictionary. The format of the two bytes is as so: 0xLL 0xHK
    Address In Sliding Dictionary where to start copying from = 0xHLL
    Number of bytes to copy from sliding dictionary = 3 + 0xK

    When copying from the sliding dictionary to the decompressed file, you must do the copy byte by byte because each copy also re-adds the value to the sliding dictionary. So it has a feedback loop that would desynchronize the sliding dictionary if one did a batch copy in one shot.

    The sliding table is 4096 bytes in size, and each byte is initialized to 0 at start up.

    The sliding table's insertion pointer is at 4078 on startup and for each byte insertion it increments by one. If it is >= 4096, it rolls over to 0. (This is what the & 0xFFF does).
     
  18. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Thanks for that SANiK, now SADXsndSharp can decompress files from system.dat on disc 1. Compressed files show up with blue names, and they're decompressed automatically on extracting. Turns out that everything but the MPG files are compressed.
     
  19. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    I've located the code that sets the checksum for Chao save files, and converted it to C# (source included). If you run this program after editing the file, SADX won't crash. The save file has the same layout as it does in RAM.
     
  20. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Sorry for the triple post, but I've now got the checksum code for the normal save files, and converted it to C#. So now we can edit the save files. I don't know what's what in the save file yet, but filling it with 00 locks everything, even Sonic.

    Edit: SCHG:Sonic Adventure DX: PC/Save Files