Bloodstained: Curse of the Moon 1 & 2 (and other Inti Creates) stuff

Do you know a tool, link or website for working on a specific game files or to help game research? Let's collect them here!
xttl
Posts: 4
Joined: Sun May 02, 2021 6:04 am

Bloodstained: Curse of the Moon 1 & 2 (and other Inti Creates) stuff

Post by xttl »

I changed the name of the program to inti_encdec since it also works for other Inti Creates games. The version in the zip can now decode/encode larger files which are found in games other than COTM1/COTM2. It can also now decode/encode the system and game save data files in COTM1 and COTM2! (but probably not any of the other games, the save keys are different between COTM1 and COTM2 so probably the other games all use their own keys too).

I have also added file lists for other games to the zip (thanks RandomTBush @ xentax) plus added the missing files to the COTM1/COTM2 lists


(also just posted this on xentax)

Hello everyone, I managed to find out very little information about these games from the interwebs, so maybe someone will find this useful.

Here's a tool for decrypting/encrypting (if you can really call it encryption, it's not using AES or any other real encryption algorithm) datafiles from COTM1 and COTM2. Not all files encrypted, and I think some are compressed in addition to being encrypted. It's a very simple single file C program, should compile and work fine with gcc, clang, msvc, pretty much anything.

It may also work for some other Inti Creates games, but I haven't tried.

I have no idea where the game code reads or calculates the keys for the "encryption algorithm", but it seems there are four different possible keys used with the same decryption code, one per data type ("obj", "bft", "set" or "scroll").

The same keys seem to work for both COTM1 and COTM2 on all platforms.

Also, in the PC versions of both games, the filenames have been obfuscated changing each file's name to the MD5 hash of the actual filename. It is possible to recover the original filenames by logging them as the game accesses them (by eg. hacking the binary to report them via OutputDebugStringA), or using file listings from console versions which don't have obfuscated filenames. Note that for some filenames, you'll need to add a platform specific "Win/" directory prefix before hashing the filename (on Switch, these files would be in an actual "NX/" subdirectory inside the romfs, for some reason there is no equivalent subdirectory inside the PS4 version's package).

Using these methods, I have recovered original filenames for all but two of the files in the the DataHash directory on PC for COTM2. (I played through the game multiple times taking different routes, but the logger didn't catch anything which would match those two when MD5 hashed, unfortunately it's not really feasible to brute force the remaining ones, at least not with only a single albeit reasonably powerful GPU, the filenames could be up to 20-30 characters or even more, and may contain at least lowercase and capital letters, numbers, underscores and a single dot.)

For COTM1, I have recovered 261 of 296 filenames based on file listings from console versions. I'll need to play through it a few times with a logger patched one day...

Next up is going to be figuring out the actual map and background graphics formats. I want to write a program which can show the maps. :-)

Btw. if you want to look at them, use the "set" key for map*.stb files and "scroll" key for map*.scb files. *.osb files use the "obj" key.

The zip includes the encdec.c program and MD5 filename hash lists for COTM1 and COTM2.

inti_encdec_v2d.zip


oh yeah, if you rename map00.stb to map01.stb (look at the hash lists), you'll get a debug map (in both games) when you start the game! nothing that interesting or useful there, though.

edit: fixed the argv enum I added just before upload...

edit2: figured out how the keys are calculated, but i'm too tired to update the zip

Code: Select all

#define WTFSTRING "90210"
#define BASEKEY 0xA1B34F58CAD705B2LL

uint64_t type2key (char *type)
{
  uint64_t key;
  int i, l;
  char buf[12];
 
  strcpy(buf,type);
  strcat(buf,WTFSTRING);
 
  l = strlen(buf);
 
  key = BASEKEY;
 
  for (i=0; i<l; i++)
  {
    key += buf[i];
    key *= 141;
  }
 
  return key;
}


gives correct keys for inputs "obj", "bft", "set" or "scroll"
(also, the bft key seems to be for the font data files)

edit3: the compression seems to be just regular zlib compression with a 4-byte header (int32) telling how much space to reserve for decompressed output.

yes!
curse.png

curse2.png


I still don't know what the huge 0-byte fills at the end of those scroll/font datafiles are for though (nor the actual header & stage tile layout format)

I have tested decoding & re-encoding the map01 files with it and the game still loads it fine. :) Next up will be figuring out the actual map formats...
Last edited by xttl on Mon Jul 05, 2021 11:34 am, edited 2 times in total.
xttl
Posts: 4
Joined: Sun May 02, 2021 6:04 am

Re: Bloodstained: Curse of the Moon 1 & 2 stuff

Post by xttl »

"major" update to the zip in first post:

  • Turns out, the key is NOT supposed to be reset every 384kB, large files (which some Inti games other than COTM1/COTM2 have) can now be unpacked and repacked successfully. Thanks to RandomTBush @ xentax for testing this.
  • Added system and game save data decryption/encryption for COTM1 and COTM2. The same code (actually it's just the same obfuscation applied twice with different keys) could also work for other Inti games, but game specific keys/keystrings are required.
  • Added three new possible asset types/keys to the list "txt20170401", "snd90210", "json180601", cannot be tested with any files from COTM1/COTM2. (unless with something from inside one of the large archive files..?)

(the save data may well be checksummed in addition to the obfuscation, I haven't tried to edit it yet)

I may need to update the tool to use memorymapped files, compress/decompress in chunks and/or encode/decode inside the same buffer to reduce memory use a bit if there are compatible games with even larger files out there...
xttl
Posts: 4
Joined: Sun May 02, 2021 6:04 am

Re: Bloodstained: Curse of the Moon 1 & 2 stuff

Post by xttl »

Haven't forgot about this, just have had other things to do (and some difficulties doing much of anything at all...)

No progress whatsoever to report on the level formats, but here is a tool (source included) which can dump and rebuild TTB files in COTM1 and COTM2 (possibly other Inti games too) which contain all the in-game text. Refer to the file lists included to see which data files (under Data/ or DataHash/) are the TTB files.
inti_textconv.zip

First, convert the TTB file to a plain text file like this:

Code: Select all

textconv d <ttbfile> <textfile>

eg. to convert PauseMenu.ttb:

Code: Select all

textconv d 208b1e0c2edfd32ad57b9ff7e03d64b7 PauseMenu.txt

then edit the file with any text editor which supports UTF-8, and convert it back to a TTB like this:

Code: Select all

textconv e PauseMenu.txt 208b1e0c2edfd32ad57b9ff7e03d64b7


Do not try to change the TXT file structure (don't touch any of the numbers and don't try to split the text for a TTB record to multiple lines, don't add empty lines, etc.).

With this tool you could, for example, translate the games to any language. The text length CAN be changed! (though you should probably stay within a reasonable range of the original text to avoid any possible problems with text positioning and dialog box size, also pay attention to the "\n" newlines and line lengths)

If you need characters which are not included in the original font, then you'll need to edit the BMPFont/BMPFont2 files (as raw graphics since there is no special tool for this yet...). I could help with this is required.

The game uses UTF-8 encoding internally for both the English and Japanese text. It seems SOME (kana) but not all (kanji) Japanese characters work even if the game is set to English mode. Also, all English/latin alphabet characters seem to still work when the game is in Japanese mode. If you are willing to break Japanese support, this can be used to add any diacritics required by your target language.

There are other unused characters in the font too (eg. PlayStation and Switch button graphics, on PC it always uses the Xbox graphics) that may be possible to use for this purpose, but I don't have the character values used for these.

Note that parts of the original English text use the double-width "ideographic space" unicode character (U+3000) instead of a regular space (U+0020), especially text that is displayed using the smaller font (in eg. PauseMenu.ttb). Do not try to change these to regular spaces (or you'll get no space at all when the text is displayed).

If the original text uses C-style formatting specifiers (if you see a percent sign somewhere), do not change the amount or order of those formatting specifiers. See MainGame.ttb from COTM1 or COTM2 for examples of this.

Any more questions, ask.

edit: seems some versions of Windows notepad may add garbage bytes (BOM) to the beginning when a text file is (re)saved as UTF-8 which confused the "parser" in textconv, updated the zip with a fixed version

edit2: whoops, encoding was not scrambling the last four bytes of the output ttb file, fixed
xttl
Posts: 4
Joined: Sun May 02, 2021 6:04 am

Re: Bloodstained: Curse of the Moon 1 & 2 (and other Inti Creates) stuff

Post by xttl »

I finally updated the zip in the first post with the newest version of the program on this forum too. Among other things, it now supports decoding and encoding the JSON files and savegames from Dragon Marked for Death PC. The order of command line arguments has also changed. DMFD PC save files use unique scrambling passwords based on the player's SteamID, so you'll need to input it when you are using the save3 filetype.

Also, here's the zip password for the "disc" file (which is really a zip) from Mega Man Legacy Collection 2 since I couldn't find it published anywhere on Internet (at least before I just posted it on Xentax, maybe I just failed at searching):

Code: Select all

00000000  9f 82 90 ac e0 e7 83 47  3f 94 fc 90 dd 88 bc 9d  |.......G?.......|
00000010  dc 90 63 e0 e3 90 c8 9e  52 92 ad 94 42 8b 50 9f  |..c.....R...B.P.|
00000020  9e 81 cc 9c 91 94 e0 e8  eb 8a f8 8d 61 8e 87 34  |............a..4|
00000030  96 79 e9 71 e7 cd 33 e6  43 96 82 9a ba 9d 91 e1  |.y.q..3.C.......|
00000040  fb e3 ef 9f 67 e0 df 77  81 40 2e 84 9f 8b d4 9b  |....g..w.@......|
00000050  a9 95 98 8f ee e3 9f 88  a9 90 98 9c ad 95 d7 e3  |................|
00000060  4d 96 f0 e7 74 e4 40 ea  46 9c aa 81 93 e2 58 92  |M...t.@.F.....X.|
00000070  98 e7 ef 8b d9 8d 8c 8f  dd 8c 40 8b f6 e2 cf e2  |..........@.....|
00000080  e3 9f 66 99 60 96 a8 83  7e 83 83 e6 e9 92 73 e2  |..f.`...~.....s.|
00000090  c4 90 a0 95 90 e0 c4 e4  ce 9e fb 9d 62 96 65 e7  |............b.e.|
000000a0  c0 81 4b e5 6f 92 b4 8c  47 9f 53 8b 99 82 64 8d  |..K.o...G.S...d.|
000000b0  b4 e0 a2 81 e1 99 56 e9  6d e6 e6 8c cb e4 91 e6  |......V.m.......|
000000c0  e4 96 e1 81 6f 83 90 91  9b 8d 42 8a 86 c6 e4 69  |....o.....B....i|
000000d0  e8 8c 90 72 8e 86 9a b0  e4 95 9d 8d 8f bd 8a ca  |...r............|
000000e0  84 5f 93 e7 89 b5 94 7c  95 63 95 62 e7 56 e6 80  |._.....|.c.b.V..|
000000f0  e7 a9 9b eb e1 5a 8d be  9d d7 9c 52 e9 c5        |.....Z.....R..|


Since it's a long (254 byte) bunch of garbage with lots of non-ASCII characters, for your convenience I also made a modified version of the miniunz program (from here) which can read zip passwords from files, here:
mmlc2_unzip.zip


You can extract the files from MMLC2 using it like this (bin file is included in attachment):

Code: Select all

miniunz -s mmlc2pass.bin -x <path_to_discfile> [-d targetdir]


Btw. if you want to replace files in the zip (to mod the game), it is not necessary to password protect the replacement files (so not having a modified minizip program as well shouldn't be a problem)