SETTINGS
Appearance
Language
About

Settings

Select a category to the left.

Appearance

Theme

Light or dark? Choose how the site looks to you by clicking an image below.

Light Dark AMOLED

Language

Preferred Language

All content on blog.claranguyen.me is originally in UK English. However, if content exists in your preferred language, it will display as that instead. Feel free to choose that below. This will require a page refresh to take effect.

About

"blog.claranguyen.me" details

Domain Name: claranguyen.me
Site Version: 1.0.0
Last Updated: 2020/12/29
Action Replay DS (Part 3): Breakdown with working hardware
Saturday, August 6, 2022

Introduction

Well, I didn't really intend on this being 3 parts, but here we are. So if you haven't read the previous parts, you probably should. Here are some links:

Following Part 2, I went from having a frozen white screen "brick" of an Action Replay DS cartridge to having a working cartridge, just like how things were in 2007. I now have full access to working hardware. And I am able to flash any kind of firmware I want onto a physical ARDS cartridge utilising a trick with CFW. So I think it's now time to go and finish off the remaining research on how the thing works.

In Part 1, a lot of the recovery steps taken were based on guesses and looking at bytes in a hex editor. While I'm sure a lot of that research and guessing was correct (as I was able to recover all of my old codes), it would be nice to simplify it all down and organise it. Documentation, I guess. So, where do we start?

Game Address Lookup

In Part 1, I wrote and introduced some tools in a suite. One of them was ards_game_ls, which can be used to list the addresses of games that are on an Action Replay DS cartridge. Let's talk a little more about it, because there's things to improve.

The way this program searched for games was brute-force, or exhaustive. It started at 0x00054000 on the cartridge and just went through each byte searching for a magic number. Once it found one, it knows it found a game. Then it just figures out the code binary and names from there. While this does work, it is for more of a "data rescue" procedure. I genuinely doubt the actual cartridge will casually search through its entire memory to show a list of games. It's just too slow. There should be a lookup table somewhere. At least that's my hunch.

This is where 0x00044000 comes in. Behold:

A list of games by ID. You can see it in the strings to the right.

Well, ASME-AEA63749 shows up as the first one, and it's obviously Super Mario 64 DS. The rest of them are also valid game IDs. Google them up (except for NDS-Action Replay Opts). There is a pattern here. Each game is a field of 32 bytes. Hence, there is a 32 byte struct that stores game data and location. And this is an array of structs, so treat it as one literally. These structs can be proposed as:

C Code
/* List Node Struct */
typedef struct AR_GAME_LIST_NODE {
	                           // Bytes    Description
	uint32_t magic;            // 00 - 03. Always "00 00 00 00" if game.
	
	union {
		char raw[24];          // 04 - 27. Entire Game ID as C-String
		struct {               // -----------------------------------------
			char ID_LEFT[4];   // 04 - 07. 4 characters on cartridge
			char sep;          // 08 - 08. Always "-" (0x2D). Separator
			char ID_NCRC32[8]; // 09 - 16. ~CRC32(first 512 bytes of ROM)
			char null_term;    // 17 - 17. Terminates ID C-String above.
			char extra[10];    // 18 - 27. Remaining Buffer
		} segment;
	} ID;

	uint16_t location;         // 28 - 29. 0x40000 + (location << 8)
	uint16_t chunks;           // 30 - 31. Number of 0x100 byte chunks
} ar_game_list_node;

/* List Node Wrapper */
typedef struct AR_GAME_LIST_T {
	size_t             num_games;
	ar_game_list_node *games;
} ar_game_list_t;

Maybe it's a little excessive that there's a union and struct there. But I want to give a programmer the option to use node.ID.raw for printing out the raw C-String, or access individual parts at will via node.ID.segment.ID_LEFT, etc. That's why it's formatted that way. Code readability. Also, this is because the 24 bytes are indeed a buffer. Otherwise, we will run into errors accounting for NDS-Action Replay Opts, which is a valid input.

Anyways, those location and chunks values at the very end are our focus. They jump straight to the location of the header for a game, which is followed by code binary, and then finally the code text and note strings. And since every single game can be put into chunks of 0x100 (256) bytes, we can optimise ards_game_ls for casual use, while keeping it's original brute-force mode as a sort of "data rescue" mode.

There really is an opportunity for some code elegance here. Build the array, jump to that point of memory, then use the knowledge of ar_game_info_t from this segment of Part 1's post to get the location of the game name. Jump to that and read it. Thus, you have a proper game lister in a few lines of code. Elegant. And since it jumps around memory rather than searching byte-for-byte, it will be extremely fast too.

It works btw.

Some games being listed. Notice the IDs match what was in the hex dump above.

And the time difference is substantial, for obvious reasons:

Time comparison. Top = Regular Mode. Bottom = Data Rescue Mode

This isn't even a proper benchmark. But I don't think I need to do one to show that it's clearly faster.

NDS ROM Structure

Since that code list was the very last thing I needed to know, in generics, I can document the overall structure of the ROM file. It becomes simple once you see it.

Breakdown
Happens at every 0x00100000 until 0x00FFFFFF

0x00000 - 0x3FFFF - Firmware
0x40000 - 0x43FFF - ???
0x44000 - 0x53FFF - Game List
0x54000 - 0xFFFFF - Game Data and Codes

Well, okay. I don't know what that "???" section is. But whatever. This is the overall structure of the ROM file. It's what all of the tools written for the 3 blog posts have relied on.

I can go into some detail on each part, linking to parts within the 3 blog posts where relevant information is helpful.

Segment 1: Firmware

This segment ranges from 0x00000 to 0x3FFFF. But it can vary in size. For the details and research, the section What is the "firmware" file anyways? in Part 2 breaks everything down.

The firmware file has an 8 byte header, as well as the bytes of this section in the NDS ROM. What the Code Manager does is validate the firmware with a CRC16 checksum stored in bytes 4 through 8 in the firmware file. Then it copies everything else to the cartridge over USB, flashing it. Everything that comes after the firmware's file size is FF up until (and including) 0x3FFFF.

This means you can extract the firmware easily from a ROM dump. But the first copy of the ROM has some modified data between 0x00002000 and 0x00004800. So to extract the firmware, simply jump to 0x001047FF. Then go back until you find a byte that isn't a FF. Then copy from 0x00100000 to that point. That's your firmware. Details on that are here. And if you just want a program to extract it for you, ards_firm_extract will do it easily.

Segment 2: ???

idk. It's the segment that ranges from 0x40000 to 0x43FFF. So naturally I nuked it and set everything to 00 to see if it still boots. It does.

Segment 3: Game List

This segment ranges from 0x44000 to 0x53FFF. It just stores a lookup array. This serves as a way for the cartridge to look up what games are stored on it, and then jump to that spot in memory instantly. Each game in this lookup array takes up 32 bytes. After the list ends, all bytes are FF up until (and including) 0x53FFF.

See Game Address Lookup up above for details on this. But it really is that simple.

Segment 4: Game Data and Codes

This segment requires it's own section, as it's the most complex. This segment ranges from 0x54000 all the way until the end of the current copy of the ROM at 0xFFFFF. The memory here is constantly overwritten. If you wipe out the memory of the Action Replay DS cartridge to remove all codes, it doesn't set memory here to zero. Instead, something overwrites it. Kind of like how deleting files on a hard drive works.

The location of games is determined by the lookup array defined in the previous segment at 0x44000. From there, jump to a specific address in this segment and you will find game data stored in the following format:

Breakdown
32 bytes - Header
?? bytes - Code Binary
?? bytes - String Position Data
?? bytes - String Data

The reason why 3 of the 4 sections have "??" as the byte count is because it is dynamic. But each section after the first relies on data from the previous ones. The code binary segment will read codes based on the number of codes specified in the header. The code text/note data will read text, and positional data is specified in the "String Position Data" segment. It is dynamic based on, obviously, how many codes are present.

Header

To break this down, first is the header. Thus, the following struct is useful:

C Code
typedef struct AR_GAME_INFO_T {
	                          // Bytes    Description
	uint32_t magic;           // 00 - 03. Always "01 00 1C 00".
	uint16_t num_codes;       // 04 - 05. Number of codes present
	uint16_t nx20;            // 06 - 07. Always "20 00".
	uint32_t offset_text;     // 08 - 11. Bytes from game_info_t to text - 1
	uint32_t offset_strlen;   // 12 - 15. Bytes from game_info_t to strlen
	uint16_t wDosDate;        // 16 - 17. DOS date (?)
	uint16_t wDosTime;        // 18 - 19. DOS time (?)
	char     ID[4];           // 20 - 23. 4 characters that appear on cartridge
	uint32_t idk;             // 24 - 27
	uint32_t N_CRC32;         // 28 - 31. ~CRC32(first 512 bytes of ROM)
} ar_game_info_t;

This is straight from part 1, here. With it, we have the first 32 bytes of a game's code data.

Deciphering Code Binary - Codes

Now for the code binary. Before each code or folder, there are 2 bytes which tell you what type the next few bytes will be. Then there is 2 bytes for quantity. After that is the body of the code, assuming it's a code. If it's a folder, a code will follow. Let's break it down.

The first 2 bytes are to tell the type. This can be one of the following values:

For simplicity, we'll talk about a code first. So if I had something like this:

2 Line Code Example
01 00 02 00 94 F1 17 12 22 00 00 00 00 00 00 D2
00 00 00 00

The first 2 bytes are 01 00. Therefore, this is a code. The next 2 bytes are for the number of lines the code has. Thus, this tells us that the code has 2 lines. A line is like XXXXXXXX YYYYYYYY. In the case of Action Replay, this is optimally represented as 8 bytes per line. Each line is also stored as little-endian. This means that 01234567 89ABCDEF would be stored in memory as 67 45 23 01 EF CD AB 89.

With this in mind, the example up above has 2 lines. If we want to decode it, it will have 94 F1 17 12 22 00 00 00 and 00 00 00 D2 00 00 00 00 to decode. This becomes:

2 Line Code Example
1217F194 00000022
D2000000 00000000

The binary storage is useful because it stores this in 8 bytes. If it was stored as text, it would take up 32 bytes. An Action Replay DS cartridge can only store 1 MB of codes compact. That isn't a lot. So if you stored the codes as text, you would only have 1/4th of 1 MB. 256 KB. That is 5.625x less than a standard floppy disk. Binary is important.

Oh, also if this is a Master Code, the format is exactly the same, except the first 2 bytes are 11 00 rather than 01 00. In a codelist XML file, this is indicated master before the actual code such as:

XML (Master Code)
<cheat>
	<name>(M)</name>
	<codes>master 01234567 89ABCDEF</codes>
</cheat>

For a On by default or Always On code, these can be done by specifying on or always_on respectively before the actual code values.

XML (On by Default)
<cheat>
	<name>On by default</name>
	<codes>on 01234567 89ABCDEF</codes>
</cheat>
XML (Always On)
<cheat>
	<name>Always On</name>
	<codes>always_on 01234567 89ABCDEF</codes>
</cheat>

Lastly, for some extremely specific situations where you will want combinations of the master, on, and always_on, you can specify multiple at once:

XML (Flag Stress Test)
<cheat>
	<name>Master and On by Default</name>
	<codes>master on 01234567 89ABCDEF</codes>
</cheat>
<cheat>
	<name>Master and Always On</name>
	<codes>master always_on 01234567 89ABCDEF</codes>
</cheat>
<cheat>
	<name>On by Default and Always On</name>
	<codes>on always_on 01234567 89ABCDEF</codes>
</cheat>
<cheat>
	<name>Master and On by Default and Always On</name>
	<codes>master on always_on 01234567 89ABCDEF</codes>
</cheat>

Whenever you put this into the Code Manager on PC, it will only show the first modifier. But in the XML file, it will preserve all flags given. And when pushed to the ARDS cartridge, it will also retain all flags. However, always_on will assume on by default. So it is useless to have all three tacked onto a single code. More on why down below.

Deciphering Code Binary - Folders

As I said earlier, 02 00 or 06 00 is a folder. If this is the case, then the 2 bytes after will be the number of codes the folder has. The codes inside the folder retain the same format discussed above. So here's an example:

Folder with 2 codes example
02 00 02 00 01 00 02 00 F8 9D 16 22 11 00 00 00
F9 9D 16 22 0D 00 00 00 01 00 02 00 F8 9D 16 22
19 00 00 00 F9 9D 16 22 17 00 00 00

The first 2 bytes are 02 00. Therefore, this is a folder. The next 2 bytes tells us that this folder has 2 codes in it. From here, it's easy. The remaining parts are just codes, which can be figured out from the procedure up above.

The first code has 2 lines, as it's 01 00 02 00. This resolves to:

First code
22169DF8 00000011
22169DF9 0000000D

Then the next bytes show another 2 line code, as it's also 01 00 02 00. This code resolves to:

Second code
22169DF8 00000019
22169DF9 00000017

As mentioned, both 02 00 and 06 00 are folders. The difference is that 02 00 is a regular folder of a square shape. Any codes in it can be enabled in any combination. 06 00 indicates that the folder is a radio folder. This means that only one code in that folder can be on at once. In a codelist XML file, this is indicated allowedon such as:

XML
<folder>
	<name>Radio Folder</name>
	<allowedon>1</allowedon>
	...
</folder>

The allowedon value can only be set to 1. If set to a higher value with force (via XML), it will still transfer to the DS with the same flag set. So even if you set it to 2, 3, or higher, only 1 code will be allowed to be active at once.

But what if we force it?

<folder>
	<name>Radio Folder Always On</name>
	<allowedon>1</allowedon>
	<cheat>
		<name>Cheat A (Always On)</name>
		<codes>always_on AAAAAAAA AAAAAAAA</codes>
	</cheat>
	<cheat>
		<name>Cheat B (Always On)</name>
		<codes>always_on BBBBBBBB BBBBBBBB</codes>
	</cheat>
	<cheat>
		<name>Cheat C (On by Default)</name>
		<codes>on CCCCCCCC CCCCCCCC</codes>
	</cheat>
	<cheat>
		<name>Cheat D (Normal)</name>
		<codes>DDDDDDDD DDDDDDDD</codes>
	</cheat>
</folder>

Hilariously, this works. So you can open up the folder and it will just have 3 of those cheats checked off, even though they shouldn't be:

If you try to enable or disable any of them, it will go back to default behaviour, where it will only allow 1 code to be enabled at a time in the folder. It bypasses the always_on in favour of 1 code at a time in this case.

And of course, nested folders don't work. My tools were written with nested folders in consideration though via recursion. This was from before I had actual hardware to test it with. If a recursive folder was forced via XML, it will not be recognised by the Code Manager, and will not be put into the cartridge. No binary of any cheats inside the nested folder exist in a ROM dump with a nested folder.

Deciphering Code Binary - Misc

This procedure goes on until we try to read a code and get a 00 00. This indicates the end of the code binary segment. At least that's how it looks. There is another way to tell. In the ar_game_info_t struct, there is an integer called code_bytes_size. If you exceed this, you know you're at the end too. That header is really useful.

I was tempted to believe that the first 2 bits of the first byte would be the type. This would make sense, considering that 01 / 11 (in hex) are both codes, and 02 / 06 are folders. Here's what I mean:

Binary
Codes:
  0x0001 = 00000000 00000001  Normal Code
  0x0011 = 00000000 00010001  Master Code
  0x8001 = 10000000 00000001  Normal Code + On by Default
  0x8009 = 10000000 00001001  Normal Code + Always On (Assumes "On by Default")
  0x8011 = 10000000 00010001  Master Code + On by Default
  0x8019 = 10000000 00011001  Master Code + Always On (Assumes "On by Default")

Folders:
  0x0002 = 00000000 00000010  Normal Folder
  0x0006 = 00000000 00000110  Radio Folder (Only 1 code active)

So the bits are the same. It's just that there's extra bits tacked on. They could have extra meaning. Let's say that they do. If so, then we can make the initial flag be like a AR_FLAG_TERMINATE = 0, AR_FLAG_CODE = 1, AR_FLAG_FOLDER = 2. Then higher bit values can be modifiers that go on top of them. For instance, AR_FLAG_MASTER = 0x10 and AR_FLAG_ONLYONE = 0x04. So just like in Part 2, I threw the Code Manager into IDA Pro and figured out all of the possible ones the easy way. Thus,

C Code
typedef enum AR_FLAG_T {
	AR_FLAG_TERMINATE  = 0x0000, /* 0000 0000 0000 0000 */
	AR_FLAG_CODE       = 0x0001, /* 0000 0000 0000 0001 */
	AR_FLAG_FOLDER     = 0x0002, /* 0000 0000 0000 0010 */
	AR_FLAG_ONLYONE    = 0x0004, /* 0000 0000 0000 0100 */
	AR_FLAG_ON_ALWAYS  = 0x0008, /* 0000 0000 0000 1000 */
	AR_FLAG_MASTER     = 0x0010, /* 0000 0000 0001 0000 */
	AR_FLAG_ON_DEFAULT = 0x8000  /* 1000 0000 0000 0000 */
} ar_flag_t;

ar_flag_t value;

/* To get a radio folder...       0x02 | 0x04          = 0x06   */
value = AR_FLAG_FOLDER | AR_FLAG_ONLYONE;

/* To get a master code...        0x01 | 0x10          = 0x11   */
value = AR_FLAG_CODE | AR_FLAG_MASTER;

/* To get a code on by default... 0x01 | 0x8000        = 0x8001 */
value = AR_FLAG_CODE | AR_FLAG_ON_DEFAULT;

/* To get a code on always...     0x01 | 0x8000 | 0x08 = 0x8009 */
value = AR_FLAG_CODE | AR_FLAG_ON_DEFAULT | AR_FLAG_ON_ALWAYS;

Flexibility is a C programmer's dream. Though I'm not sure if it's practical here. It's always going to be one of those values. It does make my code cleaner though, because then I only have to check for the first 2 bits of the flag. Originally, I had a switch statement that had 6 different cases to account for all of the combinations. So this does simplify it down, seriously.

String Position Data & String Data

Following the binary of codes are a series of incrementing 16-bit integers. These integers grow with every text string (cheat, folder, game name, notes) that is with the game. All it does is store the position of the beginning of each string in the next section, which is "String Data". As such, I have to put both of them into a single section to explain it properly.

I teach by example. So here's an example:

String Position Data (0x00 - 0x1F) & String Data (0x20 - end).

So the first 32 bytes are the string position data. Everything is the actual text. I was nice and put the text representation on the side just like in previous pictures. It will be easier to use that for position.

So let's make this as simple as possible. Every 2 bytes is a position. So looking at 01 00, considering string index values are 0-based, this means the first string starts at the 2nd byte in the text section. The next 2 bytes says 06 00. So the next string is at 7th byte. Then the next one is 07 00, which means it's at the 8th byte. So let's highlight these three in the diagram:

First 5 string positions, as well as actual strings highlighted.

Let's put it into a single row.

First 5 strings highlighted with index above.

Each string goes until a 00 byte is hit. This is because this is how C-Strings work. Strings are null-terminated, and the 00 is the null terminator. Thus, a blank string is a single byte, being 00. This is why you see blanks, like at 06 and at 0D. And it's why strings with actual text have an extra byte at the end, also being 00.

This is how the entirety of the string section goes. Each code, folder, and game name have 2 strings that go with them. This is to account for how codes can have notes alongside them. The first string is always the game's name. The next string is always blank, as a game doesn't have a note. Then the third string and fourth string will be the first cheat or folder with a binary representation in the code binary section above. It just goes from there.

This example does have a proper layout, by the way.

Excuse the extremely dramatic names. It's intentional. Anyways, "Radio" is a folder. It has no note. Then the next instance is a cheat, which is "Press SELECT to Annihilate". This is a code inside "Radio". It has no note. This would also be a good time to mention that the names are stored in reverse order. If you look at that example, everything is in reverse order. It starts with the "Radio" folder, which is the last item. And it recursively goes in and starts with "Press SELECT to Annihilate", which is the last code in that folder. Going further down, the "(M)" is the last code, being the first in the layout up above.

Japanese Text Encoding

Since I put the effort into flashing a Japanese firmware onto my cartridge, I'll write about the details of text encoding here. Whenever you are able to input text, you are given an extra keyboard to type text. This is a カタカナ (Katakana) keyboard.

This kind of came as a surprise at first. But then I looked into how the text was encoded. It's JIS X 0201. If you look at the table there, there is no way to represent ひらがな (Hiragana) or Kanji at all. Hence why they aren't giving the option to type it into the Action Replay code editor shown above.

Speaking of the code editor, you can see a comparison between the USA and Japanese versions of text entry down below. Use the dropdown boxes to configure which side shows what.

USA (v1.55)
Japanese (v1.50)

The Japanese keyboard has an error. It's missing . It has , as shown on the symbols page. The English keyboards are identical. But the letter sprites are remade to be slightly bigger on the Japanese release. The text at the top is, being アタラシイゲームタイトルノニュウリョク, is 新しいゲームタイトルの入力 (Enter new game title). Other than the text getting translation, those keyboard options, and how they are encoded on the cartridge, there isn't that much difference between the two releases.

Action Replay in Japan (Pro Action Replay, Ez, MAX, MAX2, and MAX3)

In Japan, Action Replay is called プロアクションリプレイ (Pro Action Replay). They got some other models too, like the MAX2 and MAX3. You can find out about more of their releases here. There are links to the firmwares for those in Part 2. They have microSD card slots on them. Kind of makes me think of the Action Replay DSi. But, what if we flashed the firmware onto a regular Action Replay DS? My old hardware surely wouldn't work with it, right?

Well, I actually tried it. It does boot. But you lose access to the PC, which means no Code Manager. Additionally, if you try to add a game or a code, it will fail to proceed because it says "Insert Action Replay", as if the cartridge isn't inserted at all. So it's missing things and will not work properly. It boots, but that's about it. It can easily be flashed back with the trick mentioned in Part 2 with CFW or a Flashcart.

This doesn't stop me from comparing the versions I have booted with each other though.

Main Menu

v1.50
MAX v1.62
MAX2 v2.03
MAX3 v3.05
Japanese v1.50, v1.62, v2.03, and v3.05. Main Menu.

There is an extra option on the screen for MAX2 and MAX3. This is もどる (戻る / Go back). The reason for this is because the there is a menu prior to this on the official carts, as shown in step 9 on this page [Archive]. The other option is マックスドライブ (MAX DRIVE), which is a way to back up your saves.

There is a text change in the sliding green/red part. The first one was 「ゲームカードに差し替えてください」 (Replace with a game card). This is actually because the ROM dump probably was of one of the versions of the Pro Action Replay that didn't have an extra cartridge slot on it. So it literally required you to swap out the cartridge for your game. The v1.62 version is from the Pro Action Replay MAX, which does have an extra slot, so you can insert your games. Hence, it says 「ゲームカードを挿入してください」 (Insert a game card). Lastly, the last part was changed from 「ください」 to 「下さい」 in MAX2 and MAX3. They are pronounced the same. The attention to detail here is impressive.

Game Card Inserted

...So let's do what it says and insert a game card. I'm thinking 流星のロックマン ドラゴン ("Mega Man Star Force: Dragon" in the USA). Thus,

v1.21
v1.50
MAX2 v2.03
MAX3 v3.05
Japanese v1.21, v1.50, v2.03, and v3.05. Main Menu with Game Card in.

Decided to throw 1.21 in here too. 「ゲームタイトルフメイ」 (ゲームタイトル不明 / Game Title Unknown) shows whenever the game isn't in the code list on the cartridge. This is unchanged between all 4 versions. But the font size is notably larger in 1.21. The start button has definitely changed though. From being square to being round. And the text changed from 「スタート」 (Start) to 「ゲームを始める」 (Start the game). It isn't showing in 1.21, probably because it wants a codelist for that game to be present before it boots, or something. Now I'm just guessing.

For some reason, screenshots of these are hard to find. Manuals on the archived Datel Japan page are a good source, obviously. So is the Amazon page for these products. Anyways, you may have noticed that MAX3 is a lot brighter. This might not be intentional, but that's just how that firmware is. It's like it tried to fade in from a white screen and it stopped midway. This carries over to every other menu, kind of. I tried booting up v3.02 as well. It has the same thing happen to it.

Game List Menu

The game lists look like this. Interestingly, the English text <Add New Game> is present.

v1.50
MAX2 v2.03
MAX3 v3.05
Japanese v1.50, v2.03, and v3.05. Game List Menu.

Keyboard

The second keyboard's name changed from 「カタカナ」 (Katakana) to 「ニホンゴ」 (日本語 / Japanese). Additionally, 「エイスウ」 (英数 / Alphanumeric) got changed to 「エイゴ」 (英語 / English). I think that's a weird change. The first version is more accurate.

v1.50
MAX2 v2.03
MAX3 v3.05
Japanese v1.50, v2.03, and v3.05. Katakana text entry.

Options Menu

The options menus are interesting. All 3 versions all have different options menus.

v1.50
MAX2 v2.03
MAX3 v3.05
Japanese v1.50, v2.03, and v3.05. Options Menu.

To save you from trying to translate that yourself, here you go:

Raw Text Proper Translated
コードリストノセーブ コードリストのセーブ Save the codelist
オートPCリンク オートPCリンク Auto PC Link
コードリストヲカキダス コードリストを書き出す Export codelist
コードリストヲヨミコム コードリストを読み込む Load codelist

That's about all that I can do from an emulator, and even physical hardware. For the time being, I think I'll stick to having Japanese v1.50 on my physical cartridge. Because that one actually works with the Code Manager and has no problems. I'm pretty sure that Datel Japan's product is just the same thing as the USA's but with a different stock firmware installed on it, as well as distributing a Japanese version of the Code Manager, which uses the same font style you see in the v1.50 screenshots.

What's next?

You may have noticed that I didn't go into any details on how the Action Replay injects code into games. I guess the intent of this was to figure out one big problem. And that one problem is how the devices brick themselves. And to show a way to preserve your own data if you happen to have the right hardware. I guess I didn't really think about how the cheats actually work. It was fun to figure out how the codes are stored. That being said, writing Action Replay codes is very easy if you have documentation. So I'm sure the hardware is able to decode that and intercept game data. It's all just conditionals, memory writes, etc. But, that's for another time. Or for someone more bored than me.




Clara Nguyễn
Hi! I am a Vietnamese/Italian mix with a Master's Degree in Computer Science from UTK. I have been programming since I was 6 and love to write apps and tools to make people's lives easier. I also love to do photography and media production. Nice to meet you!


Blog Links
Post Archive
Affiliates/Cool People
Nigoli's Blog
Raas's Blog