Jump to content

Recommended Posts

Posted

Does anyone know how the save files of the first Pokemon Ranger game are encrypted? Or at least if there is any kind of encryption that has been commonly used for other NDS games which might have been used for Pokemon Ranger as well?
Shadows of Almia and Guardian Signs are not encrypted and I don't know any other NDS Pokemon game that has a fully encrypted save file.

Any idea about this matter would be appreciated, thanks.

  • 3 months later...
Posted (edited)

The thing is we have a Japanese save file with all 3 DLC packages WMission (Deoxys Mission & Manaphy Mission), Celebi Mission & Mew Mission. Thanks to this website just btw: https://ux.getuploader.com/savedate/download/226.

The Mew Mission was extreme rare and I have been looking for it for quite a while. (Years!)
It was distributed in partnership with ANA and you were only able to get it on respective airport terminals for the most part. Now the save file with all missions got finally leaked this year so we are damn lucky! The problem is the missions are already played and we would like to have a blank save file with the untouched special missions.

One idea from my side would be to maybe edit everything from memory and then save the game. It's complicated too though and regardless of which way we go some research might be required, to learn what activated the missions ect... since there was no option to delete the save file but a new game option, I kinda wonder how fresh a newly started game is? At least the missions won't be removed from what I remember?

Edited by ajxpk
  • 2 years later...
Posted (edited)

Meanwhile I did some reverse engineering and I found the encryption/decryption algorithm, it's this function...

 unsigned int __fastcall PokemonRangerCrypt(int a1, unsigned int s_rand, int size, _DWORD *dest)
{
  unsigned int i; // r5
  unsigned int result; // r0
  unsigned int rand; // ST04_4
  unsigned int h_rand; // r0

  for(i = 0; i < size/4;)
  {
    rand = 1566083941 * s_rand;                 // the initial seed is located before the decrypted/encrypted data
    h_rand = rand;
    s_rand = 1566083941 * rand;
    *dest ^= h_rand & 0xFFFF0000 | (s_rand >> 16);
    ++dest;                                     // this increments by 4 (u32 data)
    ++i;
  }
  return result;
}


The initial seed for this comes right before the encrypted data itself... for example, the first two chunks include a header with the string "PKR-059 a00 ".
Right after that (loc 0xC) there is the initial seed which becomes the first "s_rand" value, the encrypted data follows (at loc 0x10). The encrypted chunk size for the first chunk is 496 bytes, the 2nd chunk starts at location 0x200, size of this chunk is still unknown. 

We can't just loop through this thing from start to end and have to figure out where each chunk starts and hopefully their seeds are stored there as well, but I think this should be easy from here now and tests should reveal that... It's still unknown how the initial seed was originally calculated... (could be a checksum?). At least the two first chunks are sharing the same seed. It looks like there could be different initial seeds for other chunks, so if something doesn't decrypt properly watch out.

Edited by ajxpk
Posted

@ajxpk nice work! Thank you very much!

Spoiler

I was able to figure out the offsets of the other chunks/seeds.






Seed	Start	Length
0x00C	0x010	0x1F0
0x20C	0x210	0x1F0
0x400	0x404	0x2620
0x2B00	0x2B04	0x2620
0x5200	0x5204	0x30E8
0x8300	0x8304	0x30E8
0xB400	0xB404	0x30E8
0xE500	0xE504	0x30E8
0x11600	0x11604	0xE7FC
0x1FE00	0x1FE04	0x67FC
0x26600 0x26604	0x4FFC
0x2B600	0x2B604	0x57FC

The checksum of a chunk is a simple CRC16 right after the seed (first 4 bytes of the encrypted data).

Ok, forget what I wrote previously.

Only the first two chunks are static they contain the information about the other chunks.

Save Info Chunk (512 bytes)
0x000-0x00B	PKR-059 a00 
0x00C		seed
0x010		CRC16 over the next 492 bytes
0x014		time stamp
0x018		time stamp (see https://projectpokemon.org/home/forums/topic/45846-pokemon-ranger-save-file-encryption/?tab=comments#comment-265265)
0x01C		always 0x000000A0
0x020-0x1FF	chunk info entries

Chunk Info Entry (48 bytes)
0x00 file name
0x1E number of copies
0x20 offset
0x24 data size
0x28 reserved space

Each of the other chunks has a 24 byte header.

Chunk
0x00 seed
0x04 CRC16 over the chunk data starting at 0x18
0x08 unknown
0x0C unknown
0x10 save counter
0x14 always 0x00000001
0x18-0x?? chunk data

 

A small tool to decrypt/encrypt and fix checksums for Ranger saves (use with caution!): https://github.com/Bl4ckSh4rk/rangercrypt/releases/latest

  • Like 1
Posted (edited)

Good work!

Now I finally understand why there were 3 functions for decryption. One was for the first two chunks with the save info, the second is a hardcoded decryption function with a * 5 loop to decrypt the first 20 bytes of a chunk, (I guess just for save file checks) and the third one for the rest of the non-static chunks.

This is the directory list struct based on my understanding.
(Updated! @BlackShark Sorry for the confusion, I think I mixed the numbers up a little, the struct with the save info is complete now...)

Savedata List
0x00 File name (28 chars)
0x1C Unknown (u16)
0x1E Mirror (u16)
0x20 Address (u32)
0x24 Data Size (u32)
0x28 Sector Size (u32)
0x2C Unknown (u32)

 

file name		type	data size	sector size
siskin.tsd		2	0x260C		0x4E00
wren.tsd		2	0x30D4		0x6200
hazel.tsd		2	0x30D4		0x6200
set_delivery001.dat	1	0xE7E8		0xE800
set_delivery003.dat	1	0x67E8		0x6800
set_delivery002.dat	1	0x57E8		0x5800
set_delivery004.dat	1	0x4FE8		0x5000


The most interesting one is siskin.tsd, which starts with the setting data, it also includes the ranger id (offset 0x10) and data related to the special missions, like the "new mission" flag (offset 0xA) and manaphy egg information at offset 0x8 (not received yet/received/sent) and the time info when it was obtained year/month/day (starts at offset 0xC). There's also the title and desciption strings, wren.tsd apparently includes player info... the name is at offset 0xE for example. Last but not least, hazel.tsd is used for quick save/save states... 

And then we have the Special Mission .dat files... the international versions have those localized included in the directory data\mission.


Edit:
I extracted the special mission .dat files and attached them to this post
In addition I attached a thingy table which I made in case you want to look for strings.

Pokemon Ranger Special Missions (JP).zip Pokemon Ranger Character Encoding (JP).tbl

I still don't know how we can utilize the .dat files at this point... they seem to include all the data that is needed for the missions, including the title and description, but just using them with a ZP3J cartridge doesn't seem to work, I still get this screen when I boot the game with it...

ZPJ3.png.e27eea5bbefc86dd37c5098283bff231.png

I will have to find out in what kind of form it wants to receive the data. I already located the functions (in version 1.1), the Ranger Net jump table function for Slot-2 is in overlay_0009.bin (loc 0x020FC628), there's the call to a familiar function that gets the cartridge data at 0x0209A558 (it's almost identical to the one in Diamond & Pearl ect.). It seems everything is ok, At least it gets the size information from 0x8100000 and the data starting from 0x8100010. Just for some reasons it returns false when the data is checked. Maybe because of failed validation checks. Now if I force it to return true I get this btw...

ZPJ3.png.084c7e0657ebcbf552769fe57532ccb8.png


I found some pictures online related to the download process...
2131.jpg.85cda84afca279c39af6296abbf186d8.jpg
2133.jpg.c7a404588e0df2da2f90fdeb1c643ef4.jpg
2134.jpg.bda64e9f514969ef76700a24728f9b58.jpg
Source: https://www.inside-games.jp/article/2006/08/12/18877.html

Edited by ajxpk
Posted
2 hours ago, ajxpk said:

I attached the extracted .dat files. Still don't know how we can utilize them...
In addition I attached a thingy table I made in case you want to look for strings.

With a modified siskin.tsd the missions will show up ingame. Idk where the data comes from, it probably gets written there during the download process.

Now I need to find a save file that doesn't have any missions yet to test if the injected misiions are properly playable.

Pokemon Ranger (JP).sav

Posted (edited)

Recently I found out that when you do something like writing on these encrypted save file chunks the game will delete them.
When you for example overwrite the encrypted "PKR-059 a00 " save block (or its seed) it erases the whole save file. And that's for example how you can delete the save game but still keep the special missions... I think the same will also work vice versa, so you can just destroy the data/do anything that the sum check fails. 

The field at offset 0x1C is a u32 value and always 0xA, it's set when the game creates the header. Can be used to check whether a save file is already decrypted/encrypted. Also... Offset 0x10 of these other save file headers could be some kind of save count. Looks like the 2nd is really a backup save, its chunk is created when you save for the 2nd time (before that it only has these blocks only have the save file header).

Edit:
Meanwhile I located the functions for checking the agb ("beacon") & mission data. The first 128 bytes starting from offset 0x08100010 is an array which is swapped for some kind of cryptostuff which reminds me on RC4, but tbh I don't know much about this and it looks tough to reverse engineer so I think I will leave it there. If I understand it correctly it's in fact very similar to what I've seen in the localized versions of Diamond & Pearl etc. where it does a signature verification with 128 bytes from 0x08020000, it looks almost identical (just the key is different from what I've seen). In addition there are SHA1 checks for the mission data starting at offset 0x08100090, apparently the total size checked is the size of the full data -191 bytes. I didn't even go further (it's possible that the data is compressed), this whole thing is more complicated than I thought, a lot research would be necessary to make it work...

If someone is interested in it... the initiate BL to the subroutines for the SHA1 and the cryptostuff is at offset 0x020B0284. The function that makes the .dat save file block and updates the main save (PKR) is at 0x02001D80. I think the best approach would be to figure out how saving works in detail, which would have to be done either way as we need to know more about these other values from the header...

Edit 2:
Btw. Meanwhile I know where the Special Mission data struct from siskin.tsd is coming from, they were installed from the .dat files...
This also exists in the localized versions, the data is for accessing the missions via Ranger Net.  The D00X struct is always 100 bytes copied straight from the .dat or maybe it's coming from another payload, we might never know unless someone wants to give it a try and reserve engineer it. Missions are sorted by their IDs, regardless in which order they were downloaded. The data includes information like mission ID, lengths (u16) for description and client name and strings for title, description and the name of the client.
 

set_delivery001.dat

Mission: D001 (0x50D0)
Description offset: 16 chars
Client name offset: 97 chars
Title: "だいじなタマゴを とりもどせ!" (0xD490)
Description:
"シンバラきょうじゅが てにいれた
マナフィの タマゴを
ゴーゴーだんに うばわれてしまった!
かれらに あくようされる まえに
だいじな タマゴを とりもどせ!" (0xD4B0)
Client: "シンバラ" (0xD554)


set_delivery003.dat

Mission: D002 (0x1D10)
Description offset: 16 chars
Client name offset: 111 chars
Title: "デオキシスと わかりあえるか?" (0x5874)
Description:
"なんらかの りゆうで こうげきてきに
なった デオキシスが はっけんされた。
まわりの ポケモンや にんげんに
ひがいが でるまえに デオキシスを
せいじょうな じょうたいに もどすのだ!" (0x5894)
Client: "ハヤテ" (0x5954)


set_delivery002.dat

Mission: D003 (0x1BC0)
Description offset: 13 chars
Client name offset: 107 chars
Title: "セレビィを すくいだせ!" (0x43A4)
Description:
"ライラのもりに あらわれた セレビィを
ゴーゴーだんの れんちゅうが
ねらっているという じょうほうが ある。
セレビィを ぶじに ほごするために
かれらよりも さきに キャプチャせよ!" (0x43C0)
Client name: "ハヤテ" (0x447C)


set_delivery004.dat

Mission: D004 (0x1520)
Description offset: 15 chars
Client name offset: 115 chars
Title: "まぼろしのミュウを さがせ!" (0x397C)
Description:
"まぼろしのポケモンと いわれている
ミュウが ジャングルで もくげきされた。
シンバラきょうじゅの けんきゅうの ため
ひとりの レンジャーの めいよの ため
ぜひ ミュウを キャプチャしてほしい!" (0x399C)
Client name: "シンバラ" (0x3A64)
Edited by ajxpk
Posted (edited)

It turns out that the PKR-059 a00's header fields at offset 0x14 and 0x18 are time stamps representing minute, second, hour (u32) and year, month, day (u16).
The .tsd and .dat save headers have their time stamps too at field offset 0x8 (minute, second, hour) & 0xC (year, month, day). 

date(u16) format = year, month, day
time(u32) format = minute, second, hour

PKR-059 a00
date = Previous date from header offset 0x18 (default value = 0xFFFF)
time = Previous time from header offset 0x14 (default value = 0xFFFFFFFF)

.tsd & .dat
Whatever is currently on the stack (gets XORed all the time from what I've seen so far)

*date = *date & 0xFF80 | year & 0x7F;
*date = *date & 0xF87F | ((month & 0xF) << 7);
*date = *date & 0x7FF | ((day & 0x1F) << 11);
*time = *time & 0xFFE0FFFF | ((hour & 0x1F) << 16);
*time = *time & 0xFFFFFFC0 | minute & 0x3F;
*time = *time & 0xFFFFF03F | ((second & 0x3F) << 6);

Field at 0x1A (u16) (PKR-059 a00 ) is a count. It's defaults at 0 in increments when the file is created and each time the file is overwritten until it maxes out at 0xFFFF (it doesn't overflow). With siskin.tsd, wren.tsd, hazel.tsd, set_delivery001.dat, set_delivery003.dat, set_delivery002.dat & set_delivery004.dat set it reaches 8 and will never be updated from here. Field 0x1C is always 10, the value is used as the maximum of files that can be saved and to calculate the reserved space (0xA * 0x30 + 0x20 = 0x200).

.tsd & .dat files have a save count too at 0x10 (u32), which serves to select the current mirror, the top .tsd file being saved first takes the odd and the 2nd takes the even numbers. It maxes out at 0xFFFFFFFF without overflowing, there is an additional count for the block at 0xE (u32), which increments with each time the block is saved. It maxes out at 0xFFFF (again, no overflow).

Field 0x14 in .tsd & .dat is used for enabling/disabling a save block, mainly used for the backup where the first time these save files are created an empty disabled backup block is created. Hazel.tsd (used for save states) makes use of this commonly as save states are deleted when they're used.

PKR Main Save File header
Adrs	Description		Type
0x00	"PKR-059 "		str
0x07	"a00 "			str
0x0C	Seed			u32
0x10	CRC16			u32
0x14	Time			u32
0x18	Date	 		u16
0x1A	Count			u16
0x1C	Save Data Table Max	u16
0x1E	Unknown			u16

 

Save Data header (.tsd & .dat)
Adrs	Description		Type
0x00	Seed			u32
0x04	CRC16			u32
0x08	Time			u32	
0x0C	Date			u16
0x0E	Block Count		u16
0x10	Global Count		u32
0x14	Enable Flag		u16
0x16	Unknown			u16


Btw. the seeds for the encryption is created by via timer 0 (REG_TM0D) + a counter of its overflow (REG_TM0CNT = TIMER_ON | TM_IRQ | TM_FREQ_64).

(REG_TM0D | (TM_IRQ_count << 16)) + 1
Edited by ajxpk
  • 4 months later...
Posted
On 3/8/2021 at 10:15 PM, BlackShark said:

A small tool to decrypt/encrypt and fix checksums for Ranger saves (use with caution!): https://github.com/Bl4ckSh4rk/rangercrypt/releases/latest

Hey there so I was just trying to use this but when I drag my (german) ranger save onto it (open the save with that) it just quickly opens the command thingy and immediately closes it again without having any effect from what I can tell... I'm not really familiar with programming or anything like that; do you know what I might be doing wrong?

 

1 hour ago, NickPlayeZ said:

Hey there so I was just trying to use this but when I drag my (german) ranger save onto it (open the save with that) it just quickly opens the command thingy and immediately closes it again without having any effect from what I can tell... I'm not really familiar with programming or anything like that; do you know what I might be doing wrong?

all good now for some reason worked after trying it 3 more times? Weird but alright

 

Another Question though:
whenever I decrypt, then change some stuff and try to encrypt it again it wont work (even if I revert any changes I made back). Why could that be?

 

  • 5 years later...
Posted (edited)

I have to say, AI has become incredibly powerful when it comes to program analysis. With GPT's help, I've managed to figure out the structure of ranger1jp's Slot 2 distribution ROM. This is how the Slot 2 structure is organized:

Spoiler
#ifndef RANGER_SLOT2_FORMAT_H
#define RANGER_SLOT2_FORMAT_H

#include <stddef.h>
#include <stdint.h>

/*
 * Pokemon Ranger 1 (ARGJ) Slot-2 ROM layout.
 * ROM offset X is mapped at NDS address 0x08000000 + X.
 * Multi-byte integers in the container are little-endian.
 */

/* GBA header: game_code is four ASCII bytes at ROM 0xAC, without a NUL. */
#define RANGER_GAME_CODE_OFFSET       0x000000ACu
#define RANGER_GAME_CODE_SIZE         4u
#define RANGER_DEFAULT_GAME_CODE      "ZP3J"
#define RANGER_MAKER_CODE             "01"
#define RANGER_GBA_FIXED_VALUE        0x96u

/*
 * ARGJ accepts all ten codes below. ZP3J is only the build default.
 * A valid GBA header using any other game code is not a Ranger beacon.
 */
#define RANGER_ALLOWED_GAME_CODES(X) \
    X("ZP3J") X("ZP4J") X("ZP5J") X("ZP6J") X("ZP7J") \
    X("ZR3J") X("ZR4J") X("ZR5J") X("ZR6J") X("ZR7J")

/* Ranger payload positions in the GBA ROM. */
#define RANGER_META_OFFSET            0x00100000u
#define RANGER_SIGNED_PAYLOAD_OFFSET  0x00100010u
#define RANGER_RSA_SIGNATURE_SIZE     0x80u
#define RANGER_CONTAINER_OFFSET       0x00100090u
#define RANGER_CONTAINER_RECORD_SIZE  0x28u
#define RANGER_CONTAINER_NAME_SIZE    0x24u

#pragma pack(push, 1)

typedef struct RangerGbaHeader {
    /*0x000*/ uint8_t entry_and_logo[0xA0];
    /*0x0A0*/ char    title[12];
    /*0x0AC*/ char    game_code[4]; /* one of RANGER_ALLOWED_GAME_CODES */
    /*0x0B0*/ char    maker_code[2];
    /*0x0B2*/ uint8_t fixed_value;  /* 0x96 */
    /*0x0B3*/ uint8_t unit_code;
    /*0x0B4*/ uint8_t device_type;
    /*0x0B5*/ uint8_t reserved_b5_bb[7];
    /*0x0BC*/ uint8_t software_version;
    /*0x0BD*/ uint8_t header_complement;
    /*0x0BE*/ uint8_t reserved_be_bf[2];
} RangerGbaHeader;

/* ROM 0x100000: payload_size includes the 0x80-byte signature and container. */
typedef struct RangerSlot2Meta {
    /*0x00*/ uint32_t payload_size;
    /*0x04*/ uint8_t  reserved[0x0C];
} RangerSlot2Meta;

/*
 * All container entries use the same physical 0x28-byte layout:
 *   record[0]   offset -> display-title glyph codes; name[] unused.
 *   record[1..] offset -> mission body; name[] is set_delivery00X.dat.
 *   final       offset == 0; terminates the table.
 * Offsets are relative to the container at ROM 0x100090.
 */
typedef struct RangerContainerRawRecord {
    /*0x00*/ uint32_t data_offset;
    /*0x04*/ uint8_t  raw_name[RANGER_CONTAINER_NAME_SIZE];
} RangerContainerRawRecord;

typedef struct RangerContainerTitleRecord {
    /*0x00*/ uint32_t title_offset;
    /*0x04*/ uint8_t  reserved[RANGER_CONTAINER_NAME_SIZE];
} RangerContainerTitleRecord;

typedef struct RangerContainerFileRecord {
    /*0x00*/ uint32_t file_offset;
    /*0x04*/ char     save_filename[RANGER_CONTAINER_NAME_SIZE];
} RangerContainerFileRecord;

typedef struct RangerContainerEndRecord {
    /*0x00*/ uint32_t zero_offset;
    /*0x04*/ uint8_t  reserved[RANGER_CONTAINER_NAME_SIZE];
} RangerContainerEndRecord;

/* One physical 0x28-byte slot, interpreted according to its table position. */
typedef union RangerContainerRecord {
    RangerContainerRawRecord   raw;
    RangerContainerTitleRecord title; /* records[0] */
    RangerContainerFileRecord  file;  /* records[1..N] */
    RangerContainerEndRecord   end;   /* records[N+1], zero_offset == 0 */
} RangerContainerRecord;

/*
 * Variable-sized container beginning at ROM 0x100090.
 * records[1] expresses the first record only. Physically it is followed by
 * more records through the zero-offset terminator, then title and DLC bodies.
 */
typedef struct RangerContainer {
    /*0x00*/ RangerContainerRecord records[1];
    /* dynamic: more records, title glyphs, then every mission body */
} RangerContainer;

/*
 * RSA/SHA-1 rules for both single- and multi-DLC packages:
 *
 *   payload_size  = RANGER_RSA_SIGNATURE_SIZE + container_size
 *   container_size = payload_size - RANGER_RSA_SIGNATURE_SIZE
 *   digest         = SHA1(container[0 .. container_size))
 *   rsa_signature  = RSA-1024 PKCS#1 v1.5 signature of that SHA-1 digest
 *
 * A multi-DLC package does NOT contain one RSA signature per mission.  Its one
 * continuous container contains the title record, every DLC file record, the
 * end record, title glyph data, and every DLC body.  One signature at ROM
 * 0x100010 authenticates that complete container.  Adding, removing, swapping,
 * renaming, or changing any DLC therefore changes the digest and requires one
 * new signature for the rebuilt whole container.
 *
 * The signature bytes themselves and RangerSlot2Meta are outside the SHA-1
 * input.  With no private key, rsa_signature stays zero and the NDS-side RSA
 * result bypass is required; container parsing and save checks still run.
 */
typedef struct RangerSignedPayload {
    /*0x00*/ uint8_t         rsa_signature[RANGER_RSA_SIGNATURE_SIZE];
    /*0x80*/ RangerContainer container;
} RangerSignedPayload;

/*
 * Complete nesting:
 * RangerSlot2Rom
 *   +- gba_program[]             ROM 0x000000..0x0FFFFF
 *   +- meta                      ROM 0x100000..0x10000F
 *   `- signed_payload            ROM 0x100010..
 *        +- rsa_signature[0x80]  ROM 0x100010..0x10008F
 *        `- container            ROM 0x100090..
 *             +- records[]       title, each DLC, terminator
 *             `- data[]          title glyphs and all DLC bodies
 */
typedef struct RangerSlot2Rom {
    /*0x000000*/ uint8_t             gba_program[RANGER_META_OFFSET];
    /*0x100000*/ RangerSlot2Meta     meta;
    /*0x100010*/ RangerSignedPayload signed_payload;
} RangerSlot2Rom;

#pragma pack(pop)

_Static_assert(sizeof(RangerGbaHeader) == 0xC0, "GBA header size");
_Static_assert(offsetof(RangerGbaHeader, game_code) == RANGER_GAME_CODE_OFFSET,
               "GBA game code offset");
_Static_assert(sizeof(RangerSlot2Meta) == 0x10, "metadata size");
_Static_assert(sizeof(RangerContainerRecord) == RANGER_CONTAINER_RECORD_SIZE,
               "container record size");
_Static_assert(sizeof(RangerContainerTitleRecord) == RANGER_CONTAINER_RECORD_SIZE,
               "title record size");
_Static_assert(sizeof(RangerContainerFileRecord) == RANGER_CONTAINER_RECORD_SIZE,
               "file record size");
_Static_assert(sizeof(RangerContainerEndRecord) == RANGER_CONTAINER_RECORD_SIZE,
               "end record size");
_Static_assert(offsetof(RangerSignedPayload, container) == RANGER_RSA_SIGNATURE_SIZE,
               "container offset within signed payload");
_Static_assert(offsetof(RangerSlot2Rom, meta) == RANGER_META_OFFSET,
               "metadata offset");
_Static_assert(offsetof(RangerSlot2Rom, signed_payload) == RANGER_SIGNED_PAYLOAD_OFFSET,
               "signature offset");
_Static_assert(offsetof(RangerSlot2Rom, signed_payload.container) == RANGER_CONTAINER_OFFSET,
               "container offset");

#endif

 

I also had it use this structure together with the existing distribution data to generate ROMs that can actually be run. See the attached file for details. Here are some screenshots of the program running.

D001:

Spoiler

image.thumb.png.61e59e0ce64aff0bcfa4628e4fa4f384.pngimage.thumb.png.8efbd9bb820af25ba726020ccc74b416.pngimage.thumb.png.d9e642a0757f26d8f16e904d8610477b.pngimage.thumb.png.175f6dff4ea6cfc25240a3534b90ff2e.pngimage.thumb.png.8d26520beb3f1ea5795c15c00cc59cdd.png

If player has already received mission, it will show note:

image.thumb.png.69e5ff66ce10571a083cf5fe1e3cf1ba.png

And here are other mission D002:

image.thumb.png.4840bb295e824884671bcd87a8da1c32.pngimage.thumb.png.cbf1b6d0fc47f1451b7b0465111658bd.png

D003:

image.thumb.png.c8163bb0a893c395d11c33819fc7d765.pngimage.thumb.png.7edea44c8ea1ea17b904b3debb77e13c.png

D004:

image.thumb.png.e11e60eafc90c1e7ad47071d9e8b3f61.pngimage.thumb.png.530d2529cfac885d748ae7429e1a7432.png

 

However, there is one important thing to note: Slot 2 requires RSA verification. Without the official private key, it cannot be used directly. If used directly with the original ROM, the game will get stuck on the loading screen, as shown below.

image.thumb.png.534ecc97a80877f20bec02477bf20aea.png

 

So for now I’ll provide two ways to bypass the verification.

  1. Modify the ROM:

revision	| rom_address 
ARGJ v1.0	|  0x125368
ARGJ v1.1	|  0x125F68

change
32 D0
to
FF D0

  2. Cheat-code method: It can be used for both emulator or retail cartirdges. When used together with nds homebrew NitroHax, this method can also be used with an original retail cartridge. I’ll also include the corresponding cheats.xml file in the attachments.

ARGJ revision 1.0
Game ID: ARGJ 137BF043

520FBB28 A802D032
120FBB28 0000D0FF

ARGJ revision 1.1
Game ID: ARGJ D1359FD4

520FC6A8 A802D032
120FC6A8 0000D0FF

 

In addition, based on analysis of the internal program logic, the Slot 2 ROM supports loading multiple DLC packages at the same time, with a maximum of three. The game will only display the title for the first DLC; the titles of the subsequent ones will not be shown, and they will all be downloaded silently in sequence.

I’ve also attached a sample ROM containing D001, D002, and D003 for reference.

 

ranger_slot2_D001.gba ranger_slot2_D002.gba ranger_slot2_D003.gba ranger_slot2_D004.gba cheats.xml ranger_slot2_D1-D2-D3.gba

Edited by Wokann

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...