Jump to content

Pokemon Super Mystery Dungeon And PMD:GTI Research And Utilities


Recommended Posts

So basically, they print comments into their debug stream ?

Idk if its of any interest to you, but I ran strings on it a while ago(its not all that great), and it returned a lot of file paths and c++ symbols names for some reasons :

https://dl.dropboxusercontent.com/u/13343993/my_pmd_research_files/PSMD/PSMD_codebin_strings.txt

There's even a few mention of python core, and various libraries and even "jenkins" which is an automated build server thing. There's a lot of junk characters in there though. And japanese characters probably got ignored.

Link to comment
Share on other sites

strings(1) will, by default, only search for ASCII/ISO8859-charset strings, but you can pass a different encoding to it through the -e/--encoding= argument (e.g. "-e l" for UTF16LE; see man page for more info).

Usually, when the path to a source file ends up in the output binary, it is for an assert-like functionality (note that assert() is actually a macro, so the exact implementation is compiler-dependant).

Working on the disassembly, I've found what seems to be the core function which dereferences UUIDs to their strings (const wchar_t *sub_1C42CC(_DWORD uuid)); this might hold at least a few details about some of the escapes, but I'm not holding my breath for that.

I've located the list of Pokémon names and categories in the .data section, as well as a couple more tables which I haven't fully identified (and so they aren't in the offset list). The category list almost certainly is keyed by a value in the Pokémon data file, so that might help the understanding a bit.

As a side note, does anyone know for sure what register holds the reason code when executing SVC 0x3C (Break)?

Link to comment
Share on other sites

strings(1) will, by default, only search for ASCII/ISO8859-charset strings, but you can pass a different encoding to it through the -e/--encoding= argument (e.g. "-e l" for UTF16LE; see man page for more info).

Usually, when the path to a source file ends up in the output binary, it is for an assert-like functionality (note that assert() is actually a macro, so the exact implementation is compiler-dependant).

Working on the disassembly, I've found what seems to be the core function which dereferences UUIDs to their strings (const wchar_t *sub_1C42CC(_DWORD uuid)); this might hold at least a few details about some of the escapes, but I'm not holding my breath for that.

I've located the list of Pokémon names and categories in the .data section, as well as a couple more tables which I haven't fully identified (and so they aren't in the offset list). The category list almost certainly is keyed by a value in the Pokémon data file, so that might help the understanding a bit.

I'm using sysinternal's build of string. Which I'm not even sure is related. And yeah, I set it up for unicode strings.

If its just your standard C assert, the string is kinda superfluous, no? If its their own take on assert, it has to dumps the strings somewhere if its kept as-is after optimization and etc, no? That's just what I was saying. I know for sure that there is a debug stream that's disabled in the release build. The script has a ton of reference to "DebugPrint" and it has 2 lua functions for checking whether its a release build or not. If those strings are still there, maybe there's a way to restore the functionality?

Also, did you know the 3ds ran windows :P

8108364:Win32 function returned an error

Also are you sure your really found a list of pokemon names and categories ? Are they just the starter names ?

(I don't have a lisp interpreter installed and etc. It would be a bit more practical to see some values.)

As a side note, does anyone know for sure what register holds the reason code when executing SVC 0x3C (Break)?

I don't think I've seen that info anywhere, but those place might have it:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360f/index.html

https://www.3dbrew.org/wiki/Hardware

Link to comment
Share on other sites

So I went farther and got a large number of additional tables identified.

I have a feeling that something, somewhere, was compiled with MSVC++, partially because of that string, and partially because I saw some structures that looked similar to the VC++ implementation of typeinfo. If that is the case, it will have a number of type names leaked in a way that I can pair them with function calls.

I know that those two lists I found in the previous post's commit are the names and categories for all the species, both real (including Mega forms) and fake (being plot-controlled non-PkMn "opponents"). However, I don't have code written up to dump them (at least not in the repo) - the specific file (data/offsets.dat) is just a list of the offsets (into code.bin) along with the names I've given them. It doesn't take too long to write up code, however, so give me a moment...

EDIT: Here's the output, spoilers!

Link to comment
Share on other sites

Keep in mind that these string references are to features which are the least likely to change -- the availible Pokémon, their abilities, the moves and items, etc. All of the storyline stuff is scripted through the Lua interface, with strings referred to through their UUIDs. Anyway, if something which has a reference through the binary were to change, a recompile would probably be needed anyway (usually to change a hardcoded e.g. number of Pokémon, though I can not confirm this yet).

Link to comment
Share on other sites

  • 3 weeks later...
I looked at the save file, and it has a zlib header, but using .Net's DeflateStream and DotNetZip, I was unable to inflate the save. Something about the block size not being right.

I'm going to leave this here for any aspiring save researchers. Further discussion should occur in the Save R&D forum or via PM.

I was able to decompress each save file using .Net's System.IO.Compression.DeflateStream. The trick is to only give the deflate stream the data after the two byte header, because the header is part of the zlib spec and not part of the deflate algorithm.

Link to comment
Share on other sites

  • 3 weeks later...

Little update.

I've been trying for a while to use a weakness in the lua 5.1 engine the game uses to run arbitrary code from the game's binary. There's a known and fairly simple way to escape the lua sandbox, and run C functions, by editing lua C closures, using a few tricks, to place a pointer to the desired function, and run the closure.

https://gist.github.com/corsix/6575486

This in itself is probably not really exploitable for things like hacking the console and etc, but it would possibly allow us to make a layer to access extra functions in the game via the script engine. Because so far, after messing around it a lot, I can safely say that, you can't do much with the scripts only. Changing pokemon stats ability and etc is all handled in the game's binary, and so is loading levels and etc.. But if we could wrap calls to some of the functions handling that, we could perhaps write a lua function library that modders could use to easily gain that control over the game!

However, I haven't been able to use the modified exploit successfully this far. But I'm fairly sure its only that I'm doing something wrong, as my test to make it work on my computer also failed.

I'm guessing I might be putting the wrong offset in the pointer, and ending up trying to execute non-executable memory, crashing the game. That, or the assert check that the exploit publisher assume has been disabled, is actually enabled in PSMD. If its truly an assert check causing this, I managed to find the assert function in the binary using the demo version of IDA, and it would be trivial to nop it out.

I however managed later on to get the code to execute a bit further, by specifying a more accurate address offset, based on the position of the LuaB_auxwrap function in memory.

It seems the LuaB_auxwrap function is located here in the US rom's binary : 0x68237C

Here are the address of the lua wrapper functions for both the SysMsg function variants:

* SysMsg1 0x82AC4 <- Probably the one that takes a string to print it.

* SysMsg2 0x42200 <- This might be the one taking a string hash.

So, in order to run the SysMsg function, you need to calculate the difference between the SysMsg function's offset and the LuaB_auxwrap function:

0x68237C - 0x82AC4 = 0x5FF8B8

Then you turn that into a negative value, so its subtracted from the LuaB_auxwrap function.

0x5FF8B8 => -6289592

I managed to figure this out by looking at the lua 5.1 sources, and by tracking down the individual functions using the function loading the lua function table in the binary. That very long function is located at 0x12FAC in the US codebin. (Or it should if I translated the offset correctly ^^; )

Here's an example of how each function is registered: (Note that I had to turn the codebin into an elf to use the IDA demo on it, so the offsets are translated by 0x100000, because the first 0x100000 bytes of the elf are reserved(?). )

.text:00115758                 LDR     R2, =sub_18BA88 ; Load from Memory           //Func ptr to CreatePageMenu fun
.text:0011575C                 ADR     R1, aCreatepagemenu ; "CreatePageMenu"
.text:00115760                 MOV     R0, R4          ; Rd = Op2
.text:00115764                 BL      sub_1D8BE0      ; Branch with Link           //Call Register Function function
.text:00115768                 LDR     R2, =sub_14C83C ; Load from Memory
.text:0011576C                 ADR     R1, aCreatepagemenu ; "CreatePageMenu"
.text:00115770                 MOV     R0, R4          ; Rd = Op2
.text:00115774                 BL      sub_1D8BE0      ; Branch with Link           //Call Register Function function
.text:00115778                 LDR     R2, =sub_1615AC ; Load from Memory
.text:0011577C                 ADR     R1, aCreatepageme_0 ; "CreatePageMenuBigStack"
.text:00115780                 MOV     R0, R4          ; Rd = Op2
.text:00115784                 BL      sub_1D8BE0      ; Branch with Link           //Call Register Function function
.text:00115788                 LDR     R2, =sub_15A180 ; Load from Memory

Basically, R2 is assigned the funtion ptr of the function whose string's address is then put into R1. IDA makes this all seems pretty and simple, but there are some cross-references to handle here.

Anyways, knowing this should make it a bit easier to read.

Below is the functions needed by the exploit. I changed and tried a lot of different things over time, and so its pretty dirty and etc. But it goes all the way up to actually the calling of the modified coroutine before crashing. Also, note that, apparently after calling the inner() function, the lua stack is messed up and that literals won't work anymore. Which is why I did that stupid thing with the 10 variables for the values 1 to 10. But it might not even be needed. As long as you find a way to pass the variable amount of args to the function we're trying to call through the exploit.

I copied as closely as possible the code the PSMD devs were using to pass an arbitrary number of argument to a lua functions in the "flowsys" class, just to be sure it would work with their setup. But that didn't seem to help.

I strongly recommend reading this : https://gist.github.com/corsix/6575486

to understand what's going on, and before trying to make changes.

But in short, all numbers in lua 5.1 are 64bits doubles. And its possible to convert them into raw tables of characters/bytes. So by finding a way to turn the C structs used by lua into several lua numbers, you can access them as raw bytes. All lua variable are in fact a C struct with type information and data on the variable. Which means, its trivial in theory to turn a function closure into a number for instance, if you see where this is going.

Upvalues, aka variables shared between function and sub-function scope, are re-used in lua. Which means if you re-define the upvalue later on, some of the bits of the struct will not have been cleared and still contained the old value! So its possible to access normally inaccessible variable's values that way, or sort of cast them into raw bytes if you want. That's what they demonstrate in one of their example. They turn a raw c pointer from a "closure" struct into a lua number, and do some bit manipulation to extract the value from the double, and they now have the raw pointer address!

Then, you can use the string manipulation capabilities of lua to merge and edit those bytes, to assemble a replacement C struct which you can then inject back into the lua state and stack considering what we know.

Or at least, that's how I understood it.. ^^;


--[[
      Code adapted from : https://gist.github.com/corsix/6575486
]]--
asnum = loadstring((string.dump(function(x)
 for i = x, x, 0 do
   return i
 end
end):gsub("\96%z%z\128", "\22\0\0\128")))

function ub4(x) -- Convert little endian uint32_t to char[4]
     local ub1 = {[0] = -- Convert uint8_t to char[1]
       "\0", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\8", "\9", "\10", "\11", "\12", "\13", "\14",
       "\15", "\16", "\17", "\18", "\19", "\20", "\21", "\22", "\23", "\24", "\25", "\26", "\27", "\28",
       "\29", "\30", "\31", "\32", "\33", "\34", "\35", "\36", "\37", "\38", "\39", "\40", "\41", "\42",
       "\43", "\44", "\45", "\46", "\47", "\48", "\49", "\50", "\51", "\52", "\53", "\54", "\55", "\56",
       "\57", "\58", "\59", "\60", "\61", "\62", "\63", "\64", "\65", "\66", "\67", "\68", "\69", "\70",
       "\71", "\72", "\73", "\74", "\75", "\76", "\77", "\78", "\79", "\80", "\81", "\82", "\83", "\84",
       "\85", "\86", "\87", "\88", "\89", "\90", "\91", "\92", "\93", "\94", "\95", "\96", "\97", "\98",
       "\99", "\100", "\101", "\102", "\103", "\104", "\105", "\106", "\107", "\108", "\109", "\110", "\111",
       "\112", "\113", "\114", "\115", "\116", "\117", "\118", "\119", "\120", "\121", "\122", "\123", "\124",
       "\125", "\126", "\127", "\128", "\129", "\130", "\131", "\132", "\133", "\134", "\135", "\136", "\137",
       "\138", "\139", "\140", "\141", "\142", "\143", "\144", "\145", "\146", "\147", "\148", "\149", "\150",
       "\151", "\152", "\153", "\154", "\155", "\156", "\157", "\158", "\159", "\160", "\161", "\162", "\163",
       "\164", "\165", "\166", "\167", "\168", "\169", "\170", "\171", "\172", "\173", "\174", "\175", "\176",
       "\177", "\178", "\179", "\180", "\181", "\182", "\183", "\184", "\185", "\186", "\187", "\188", "\189",
       "\190", "\191", "\192", "\193", "\194", "\195", "\196", "\197", "\198", "\199", "\200", "\201", "\202",
       "\203", "\204", "\205", "\206", "\207", "\208", "\209", "\210", "\211", "\212", "\213", "\214", "\215",
       "\216", "\217", "\218", "\219", "\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227", "\228",
       "\229", "\230", "\231", "\232", "\233", "\234", "\235", "\236", "\237", "\238", "\239", "\240", "\241",
       "\242", "\243", "\244", "\245", "\246", "\247", "\248", "\249", "\250", "\251", "\252", "\253", "\254",
       "\255"}
 local b0 = x % 256; x = (x - b0) / 256
 local b1 = x % 256; x = (x - b1) / 256
 local b2 = x % 256; x = (x - b2) / 256
 local b3 = x % 256
 --return string.char(b0, b1, b2, b3)
 return ub1[b0] .. ub1[b1] .. ub1[b2] .. ub1[b3]
end

function f2ii(x) -- Convert double to uint32_t[2]
 if x == 0 then return 0, 0 end
 if x < 0 then x = -x end

 local e_lo, e_hi, e, m = -1075, 1023
 while true do -- this loop is math.frexp
   e = (e_lo + e_hi)
   e = (e - (e % 2)) / 2
   m = x / 2^e
   if m < 0.5 then e_hi = e elseif 1 <= m then e_lo = e else break end
 end

 if e+1023 <= 1 then
   m = m * 2^(e+1074)
   e = 0
 else
   m = (m - 0.5) * 2^53
   e = e + 1022
 end

 local lo = m % 2^32
 m = (m - lo) / 2^32
 local hi = m + e * 2^20
 return lo, hi
end

function ii2f(lo, hi) -- Convert uint32_t[2] to double
 local m = hi % 2^20
 local e = (hi - m) / 2^20
 m = m * 2^32 + lo

 if e ~= 0 then
   m = m + 2^52
 else
   e = 1
 end
 return m * 2^(e-1075)
end

-- Extra params are passed to the function
-- offsetfun is the offset from the LuaB_auxwrap function of the C function to execute
function RunCode( offsetfun, ... )
 WINDOW:SysMsg("Got into RunCode!")

 local funtorun = loadstring(string.dump(function(funaddress, ... )
   WINDOW:SysMsg("Began loadstring function!")
   local magic = nil
   local argTbl = {...}

   local function middle()
     WINDOW:SysMsg("Got into middle!")
     local print, asnum, f2ii = print, asnum, f2ii
     local co, upval
     local function inner()
       WINDOW:SysMsg("Got into Inner!")
       do local l0 = 2^52 local l1, l2, l3, l4, l5, l6, l7 = l0, l0, l0, l0, l0, l0, l0 end
       co = coroutine.wrap(ub4) -- create a CClosure
       upval = 2^52 .."next".."t".."m".."pa".. ub4(asnum(co) - 2^52 + 12)
       local upval_ptr = ub4(asnum(upval) - 2^52 + 16 + 20)
       magic = upval_ptr .. upval_ptr
       WINDOW:SysMsg("Finished Inner!")
       WINDOW:CloseMessage()
     end
     --local dll_name = "my_dll.dll"
     --local function_name = "entry_point"
     local delta = funaddress 
     WINDOW:SysMsg("Setup Inner!")
     local argNum = #argTbl
     local zeroargs = 0
     local onearg = 1
     local twoargs = 2
     local threeargs = 3
     local fourargs = 4
     local fiveargs = 5
     local sixargs = 6
     local sevenargs = 7
     local eightargs = 8
     local nineargs = 9
     local tenargs = 10
     WINDOW:SysMsg("Running Inner!") 

     inner()
     local env, f = f2ii(asnum(magic))
     f = f + delta
     magic = ii2f(env, f)

     if argNum == zeroargs then
       co() -- calls
     elseif argNum == onearg then
       co(argTbl[onearg]) -- calls
     elseif argNum == twoargs then
       co(argTbl[onearg], argTbl[twoargs])
     elseif argNum == threeargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs])
     elseif argNum == fourargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs])
     elseif argNum == fiveargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs], argTbl[fiveargs])
     elseif argNum == sixargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs], argTbl[fiveargs], argTbl[sixargs])
     elseif argNum == sevenargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs], argTbl[fiveargs], argTbl[sixargs], argTbl[sevenargs])
     elseif argNum == eightargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs], argTbl[fiveargs], argTbl[sixargs], argTbl[sevenargs], argTbl[eightargs])
     elseif argNum == nineargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs], argTbl[fiveargs], argTbl[sixargs], argTbl[sevenargs], argTbl[eightargs], argTbl[nineargs])
     elseif argNum == tenargs then
       co(argTbl[onearg], argTbl[twoargs], argTbl[threeargs], argTbl[fourargs], argTbl[fiveargs], argTbl[sixargs], argTbl[sevenargs], argTbl[eightargs], argTbl[nineargs], argTbl[tenargs])
     end
   end
   middle()
 end):gsub("(\100%z%z%z)....", "%1\0\0\0\1", 1))

 WINDOW:SysMsg("Setup function, now running !")
 funtorun( offsetfun, ... )
 WINDOW:SysMsg("Done running function !")
end

And here's a little loop to pop up a simple menu to chose something to do, I use it to launch the exploit test, and various random junk. I usually put it in one of the callbacks in "script\event\other\seikakushindan\seikakushindan.lua" (That's the script that runs each "scenes" of the personality test. Each scene has its own callback function. And if you do something that crashes a scene, it will just go to the next scene. Unless you screw up things too badly and it kicks you to the main screen, or just straight up freeze ^^; )

 repeat
   WINDOW:CloseMessage()
   WINDOW:SysMsg("Do _ what _ ?(Menu#1)")
   WINDOW:SelectStart()
   WINDOW:SelectChain("Done", 0)
   WINDOW:SelectChain("Test asnum", 1)
   WINDOW:SelectChain("Test launching", 2)
   WINDOW:DefaultCursor(0)
   local id = WINDOW:SelectEnd(MENU_SELECT_MODE.DISABLE_CANCEL)

   if id == 1 then
     WINDOW:SysMsg("Testing asnum...")

     do local dummy = 2^52 end
     local f = function() end      
     WINDOW:SysMsg( tostring(asnum(f)- 2^52) )

   elseif id == 2 then
     WINDOW:SysMsg("Testing running a C function...")
     local funaddress = -6005916  -- !!! difference between the LuaB_auxwrap function and the SysMsg Function !!!
     WINDOW:SysMsg("Asnumaddr:" .. tostring(funaddress))
     RunCode( funaddress, "GOTTAGOFAST" )
     WINDOW:SysMsg("Success!")
 end

 until id == 0

 WINDOW:SysMsg("--- End checks ---")
 WINDOW:CloseMessage()

So yeah, I'm posting this hoping someone might get it to work ! As I've been trying since before January to actively get this to work. However, my hacking skills aren't all that great I'm afraid ^^;

I also kept this secret until the european release, because if it actually allow us some control on the game, and that the european release had been delayed for that, I have felt a bit bad about it tbh.. ^^;

But yeah, its probably just about figuring out a delta between LuaB_auxwrap and the desired function. You need to use a delta, because the starting address of a program on the 3ds is not always the same. And well, you need to execute something in an executable memory page. I suggest looking at the recent presentation Smea gave on December 27 2015. It gives you a good idea of what the 3ds does to keep people from executing whatever they want on it. Basically, NX bit / Data Execution Prevention.

If anyone has anything to say about this I'd really appreciate any help I could get ^^; I'm pretty sure the problem is something really dumb/obvious, as it always is with me.. xD

Link to comment
Share on other sites

  • 4 weeks later...

Ok, I might be dumb but i cannot figure out how to map pokemon to their portraits. We know how to output the portraits, but all of the data in face_graphic.bin has around 10,000+ files of junk that just appear to be the same backgrounds (for the portraits) repeated over and over again, I was thinking if I could figure out how to get the portraits for X pokemon, then i could just output the non-junk files, but I can't find anything super obvious in any of the .bin files.

Someone posted what looks like what i'm wanting here: https://projectpokemon.org/forums/showthread.php?p=211133#post211133 , but i'm not sure what files were used to create the data in the pastebin

Link to comment
Share on other sites

Ok, I might be dumb but i cannot figure out how to map pokemon to their portraits. We know how to output the portraits, but all of the data in face_graphic.bin has around 10,000+ files of junk that just appear to be the same backgrounds (for the portraits) repeated over and over again, I was thinking if I could figure out how to get the portraits for X pokemon, then i could just output the non-junk files, but I can't find anything super obvious in any of the .bin files.

Someone posted what looks like what i'm wanting here: https://projectpokemon.org/forums/showthread.php?p=211133#post211133 , but i'm not sure what files were used to create the data in the pastebin

From what I can tell, each file in the FARC archive has an associated hash, instead of filename. The game refers to these files by a filename, which is hashed, and the game uses that hash to find the correct entry. The problem with the portraits is that we don't know the original filenames or the hashing algorithm, at least for the time being. Unless we find out exactly what]/i] the game hashes in order to find the appropriate portrait, we won't ever know how to find specific files. If we don't know the hashing algorithm, then it will be impractical to find which is which for all entries. If we have both, then for some unused files (like the ones in image_2d) we will never know the original filenames (although in the future this may be a good way to weed out the garbage files).

Link to comment
Share on other sites

  • 3 weeks later...
https://smd.salthax.org/

I feel like some people might be interested in this here. Also, since I have a good chunk of the save loading documented I think I'm going to take a look at how that checksum for SMD files is calculated so some actual save editing can be done.

Here's what I know about the checksum so far. Hope it helps:

-PSMD saves (with the possible exception of dungeon) are compressed with zlib. .Net's DeflateStream can decompress them in you trim the first two bytes.

-GTI saves use the same engine, and as far as I'm aware, all points after this one apply to both games.

-The checksum is an 8-bit sum of all the 1 bits in the file

-Because the file size in bits varies, the checksum is almost never byte aligned, and will have a different bit shifting depending on the data that comes beforehand

-There are often a few 0 bits after the checksum so the file size is a certain number of bytes (since most file systems require this)

-Before the checksum, there's a byte containing only one 1 bit. I think there's a constant number of 0s between that and the checksum, but I'm not sure. It's held true in the few GTI saves I've seen.

-game_data has something else done to the checksum, possibly dependant on the file size. Small edits can be accomplished by adding or subtracting the difference of 1 bits added or removed.

Overall this is a very annoying file format to deal with. However, if you manage to figure out game_data's extra checksum step, I might be able to edit held items. I figured it out in GTI, but lost my notes, and haven't looked at it since.

[Edit] I typed this up shortly after I woke up, so I didn't make the connection that it was you I PMed all this to earlier. Oh, well.

Edited by evandixon
Link to comment
Share on other sites

https://smd.salthax.org/

I feel like some people might be interested in this here. Also, since I have a good chunk of the save loading documented I think I'm going to take a look at how that checksum for SMD files is calculated so some actual save editing can be done.

That's some nice work! xD

I wish I was able to figure out this kind of things. xD

I've been trying to get a sandbox escape exploit for the Lua 5.1 script engine the game uses to work, but I keep triggering an assert or something, and I don't really know why.. It might be the heap corrupting or something..

But, I've bee doing that mostly to get full control over the game, since most of the useful functions aren't exposed by lua, and executing arbitrary code within the game is always nice for modding and maybe more xD

If you ever feel like taking a look at that, it would be really appreciated to see what someone that knows more about this stuff would have to say about it. ^^;

I also know that the game can save dungeon replays somewhere. (You have to use the meowth theater) I think on the SDCard maybe? So, there might be some potential to exploit the game further this way maybe?

Link to comment
Share on other sites

  • 3 months later...

I don't know if any of this can be useful or if it was already known, but I'll post it anyway.

I've made a list of the Pokémon species IDs in Gates to Infinity. The only places I've seen them being used are the RAM and menu_ground_chara_select.lua, but I assume there's more than that. The same order seems to be used in pokemon_data_info.bin, which is similar to the one found in Super documented earlier in the thread. http://pastebin.com/iahAZvA8

I've looked at dungeon/random_parts.bin some time ago. It has a simple structure, figuring out how it works isn't hard, but I have yet to see anyone talking about it.

random_parts.ent is a list of pointers to the start of each SIR0 in random_parts.bin. Starting from byte 0x10 of random_parts.bin there are 0xA4 bytes of data, then 0xC of zeros, and then it repeats. Every 0xA4 section represents a part of the map formed by 9x9 tiles, two bytes for each tile. The game rotates and arranges the parts togheter in a way that makes sense. Each DLC dungeon also has its own random_parts.bin file, which should mean they can have their own layouts. random_parts.bin only controls a tile's behaviour and the map. The model doesn't change, I assume there's a part of the file that says what model each 0xA4 section should use. [Edit: That's not the case, the file only contains the various 9x9 blocks] At 0x1134 there are 0x16c zeros, at 0x13F4 there are 0xBC zeros. http://pastebin.com/K4V0i4Kk

The file can also be found in Super, but I don't think the game uses it [Edit: It is in some dungeons, like Heart Lake for instance, and it's probably used in RTDX as well]. I tried it in GtI and it only works for the first few dungeons (later ones just crash the game), probably because it's shorter than the original.

Edited by Celeste
Link to comment
Share on other sites

so far is not posted in here :P, today got much free time and poking around PMDS Font. is using pretty simple format where img file is cte format (LA4 as texture format), and dic file as pointing font image on font sheet image just like bitmap font.

lcfont28_08_08_img_cte.png
Fot_Tsku_ARd_Gothic_Std_B_14_img_cte.png

here for dic format on PMDS font :

 

HeaderMagicHeader - 4 byte - KAND 0x4B414E44u1          - 4 byte - always 0x0CharacterCount       - 4 byte - u2          - 4 byte - always 0x0Character Data (20 byte)Character Code (Unicode) - 2 bytePosition X               - 2 bytePosition Y               - 2 byteLine Feed Width          - 2 byteLine Feed Height (?)     - 2 byteRight Space Width        - 2 byteRight Space Height (?)   - 2 byteGlyph Width              - 2 byteGlyph Heigth             - 2 byte?                        - 2 byte

 

  • Amazed 1
Link to comment
Share on other sites

Question: Has anyone figured out where the files for the Pokemon's portraits and animations/models are stored? If so, has anyone figured out what format they're in, and/or how to create a file that will follow that format? I've seen a few pictures of custom-starter PSMD runs bandied about, and I'm rather curious to see if I can resurrect my Eevee/Riolu team from Sky. If this is a stupid question, then I'm sorry, I'm rather new to the forum, and while I don't know much about the game code I'm fairly sure I could do up the portraits and the models if given a format and size to save them in for someone to input.

Link to comment
Share on other sites

Question: Has anyone figured out where the files for the Pokemon's portraits and animations/models are stored? If so, has anyone figured out what format they're in, and/or how to create a file that will follow that format? I've seen a few pictures of custom-starter PSMD runs bandied about, and I'm rather curious to see if I can resurrect my Eevee/Riolu team from Sky. If this is a stupid question, then I'm sorry, I'm rather new to the forum, and while I don't know much about the game code I'm fairly sure I could do up the portraits and the models if given a format and size to save them in for someone to input.

We know where it's stored, and how to extract/repack the file. Unfortunately, we don't know how the game selects a file.

My working theory:

1. Script requests a portrait.

2. An unknown filename is generated based on the Pokemon and the emotion.

3. Unknown magic happens that turns this unknown filename into a hash.

4. The hash is used to find the appropriate image.

Step 2 above is unknown for portraits, but known for other things like the background images of the main menu. Until someone figures out step 3, it would be impractical to attempt to add portraits through trial and error.

Link to comment
Share on other sites

We know where it's stored, and how to extract/repack the file. Unfortunately, we don't know how the game selects a file.

My working theory:

1. Script requests a portrait.

2. An unknown filename is generated based on the Pokemon and the emotion.

3. Unknown magic happens that turns this unknown filename into a hash.

4. The hash is used to find the appropriate image.

Step 2 above is unknown for portraits, but known for other things like the background images of the main menu. Until someone figures out step 3, it would be impractical to attempt to add portraits through trial and error.

I have found something that may be relevant. This is a picture from someone's starter-edited game.

[ATTACH=CONFIG]13640[/ATTACH]

As you can see, the portrait *background* exists, meaning that it calls that from somewhere, but there's no actual Wooper portrait, basic or otherwise. This means one of two things:

 

  • The game looks up the background first, and then the portrait for that emotion separately, superimposing them

  • The game tries to look up the emotion portrait, fails, and uses the default as an error handler

 

I haven't a clue which, but can anyone smarter than me tell which it is or if I'm on the wrong track entirely?

Link to comment
Share on other sites

I have found something that may be relevant. This is a picture from someone's starter-edited game.

[ATTACH=CONFIG]13640[/ATTACH]

As you can see, the portrait *background* exists, meaning that it calls that from somewhere, but there's no actual Wooper portrait, basic or otherwise. This means one of two things:

 

  • The game looks up the background first, and then the portrait for that emotion separately, superimposing them

  • The game tries to look up the emotion portrait, fails, and uses the default as an error handler

 

I haven't a clue which, but can anyone smarter than me tell which it is or if I'm on the wrong track entirely?

Looking in the contents of the portrait file, there a bazillion different instances of those backgrounds. My guess is that portraits exist for ALL Pokémon, but the majority of Pokémon have backgrounds like that for certain emotions. It's theoretically possible to edit the portraits right now, but without knowing which "shocked" background corresponds to which Pokémon, it's not practical to try right now.

Hopefully someone will eventually figure out the hashing algorithm used on filenames in FARC files.

Link to comment
Share on other sites

I have found something that may be relevant. This is a picture from someone's starter-edited game.

[ATTACH=CONFIG]13640[/ATTACH]

As you can see, the portrait *background* exists, meaning that it calls that from somewhere, but there's no actual Wooper portrait, basic or otherwise. This means one of two things:

 

  • The game looks up the background first, and then the portrait for that emotion separately, superimposing them

  • The game tries to look up the emotion portrait, fails, and uses the default as an error handler

 

I haven't a clue which, but can anyone smarter than me tell which it is or if I'm on the wrong track entirely?

How did they manage to edit their starter, I wonder?

Link to comment
Share on other sites

I have found something that may be relevant. This is a picture from someone's starter-edited game.

[ATTACH=CONFIG]13640[/ATTACH]

As you can see, the portrait *background* exists, meaning that it calls that from somewhere, but there's no actual Wooper portrait, basic or otherwise. This means one of two things:

 

  • The game looks up the background first, and then the portrait for that emotion separately, superimposing them

  • The game tries to look up the emotion portrait, fails, and uses the default as an error handler

 

I haven't a clue which, but can anyone smarter than me tell which it is or if I'm on the wrong track entirely?

I just now realized that's a screenshot of GTI and not PSMD. For GTI, everything I said about PSMD portraits holds true, except that the FARC files store file names, which means editing them is not only possible, there are already tools that can almost edit them now. I say almost because I just tried one out on portraits and things crashed, and it's been a while since I edited 2d backgrounds. Perhaps this should be my next project?

Modified background from months ago editing a similar file type:

HULnkrY.jpg

How did they manage to edit their starter, I wonder?

The two ways I'm aware of are via RAM hacks (like NTR plugins or Gateway cheats), or via ROM editing.

Link to comment
Share on other sites

@SirLoin4:

In this case it was save game editing. But, there are several ways. Like editing the scripts. I made a script edit a while ago and hacking in a little menu to pick both starters via pokemon id. The way the game works prevented it from working properly though. Btu with some asm edits it could probably be made to work.

Also, I'm considering getting back into PSMD soon, or at least partially, since citra can emulate it enough now that I can use their debugger. But right now I'm struggling with motivation, especially with the script editor for PMD2, since there has been less interest than expected, and I have to test everything myself. (Also, I have no ideas how to make automated tests for something like that ^^; )

EDIT:

Actually evan is right, its a ram edit, but they saved the game afterwards and shared it.

Link to comment
Share on other sites

  • 2 weeks later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...