Japanese e-Reader Card Format Documentation for Dōbutsu no Mori e+
Introduction
This documentation covers the structure of Dōbutsu no Mori e+ villager e-Reader cards. The header/title section is universal and can be applied to other e-Reader projects, but the rest of the data is specific to Dōbutsu no Mori e+.
Format
By default, e-Reader cards come formatted as a compressed
.raw
file. In order to view data in a hex editor or make modifications, you must decompress this.raw
file into a standard binary file. This can be done standalone by using CaitSith2’s e-Reader development tools.
Dōbutsu no Mori e+ villager cards are always 2,112 bytes (0x0840) exactly and cannot go beyond this size. If below this size, padding bytes must be added to fit 2,112 bytes exactly.
General structure
Hex Offset | Size | Description |
---|---|---|
0x0000 – 0x002F | 48 bytes | Universal e-Reader header |
0x0030 – 0x0071 | 66 bytes | Title data (SHIFT-JIS encoded) |
0x0072 – 0x00FC | 159 bytes | Letter data |
0x00FD – 0x0104 | 8 bytes | GBA minigame statistics (unused) |
0x0105 | varies | Yaz0-compressed villager data Max size of 1851 (0x073B) bytes |
padding data can occur after the Yaz0-compressed villager data
Header
An e-Reader card’s header data is always 48 bytes long, from offset 0x00 to 0x2F. This header structure is universal, meaning it applies to every e-Reader card in all regions.
Hex Offset | Size | Description | Value | Info |
---|---|---|---|---|
0x00 – 0x01 | 2 bytes | Fixed | 0x0030 | – |
0x02 | 1 byte | Fixed | 0x01 | – |
0x03 | 1 byte | Primary type | varies | See primary type flag |
0x04 – 0x05 | 2 bytes | Fixed | 0x0001 | – |
0x06 – 0x07 | 2 bytes | Dot code length | varies | 0x0810 for long strip 0x0510 for short strip |
0x08 – 0x0B | 4 bytes | Fixed | 0x00001012 | – |
0x0C – 0x0D | 2 bytes | Region type | varies | See region type flag |
0x0E | 1 byte | Dot code type | varies | 0x01 for long strip 0x02 for short strip |
0x0F | 1 byte | Fixed | 0x00 | – |
0x10 – 0x11 | 2 bytes | Unknown | varies | Can be set to anything |
0x12 | 1 byte | Fixed | 0x10 | – |
0x13 – 0x14 | 2 bytes | Data checksum | varies | See data checksum |
0x15 – 0x19 | 5 bytes | Fixed | 0x1900000008 | – |
0x1A – 0x21 | 8 bytes | ID string | 0x4E494E54454E444F | ‘NINTENDO’ in ASCII |
0x22 – 0x25 | 4 bytes | Fixed | 0x00220009 | – |
0x26 – 0x29 | 4 bytes | Size info | varies | See size info flag |
0x2A – 0x2D | 4 bytes | Permission flags | varies | See permission flags |
0x2E | 1 byte | Header checksum | varies | See header checksum |
0x2F | 1 byte | Global checksum | varies | See global checksum |
Primary type flag
Details
When converting the hex value at this offset to an 8-bit binary number, the first bit from the right is the upper bit of the card type represented by the dot code data. The corresponding lower bits are found in the region type flag. In Dōbutsu no Mori e+, villager cards always have this value set to 0x02
, which is 00000010
in binary. The upper bit here is 0
, which is the only useful part of this flag.
This is later used to define the dot code’s card type and is critical for applications that rely on the base e-Reader BIOS. In the case of Animal Crossing villager cards, this is not super important since connecting to the GameCube is done through special software that the GameCube itself transfers over.
Region type flag
Details
The hex value at this offset is in little endian and represents a 16-bit binary number. When converted, the 16 bits represent the following:
Bit Number | Description | Info |
---|---|---|
0 – 7 | Unknown | – |
8 – 11 | Region Info | 0000 represents a Japanese e-Card 0001 represents a North American / Australian e-Card |
12 – 15 | Lower bits of card type | Used in conjuction with the upper bit from primary type |
In Dōbutsu no Mori e+, villager cards will always have the value at this offset set to 0xF062
. Converting this to binary, we get 0100011000001111
. Bits 12 through 15 are 1111
and are combined with the upper bit of 0
from the primary type to make up 01111
. We then convert this number back to the hexadecimal 0x0F
, which corresponds to “game-specific” in our card type table. This makes sense since these cards cannot be used without connecting to the GameCube!
Card type table
Details
Combining the upper bit and lowers bits from the primary type and region type flags results in a 5-bit binary number which represents the card type of the dot code data. Listed below is a table that shows common card types and their corresponding values. The values are converted from binary back into hex for easier reading.
Hex Value | Card Type | Info |
---|---|---|
0x00 or 0x01 | Blank | Loads a blank screen |
0x02 or 0x03 | Dotcode application | Used in e-Reader BIOS to load an application Supports a 17-byte title Loads music type ‘A’ |
0x04 or 0x05 | Dotcode application | Used in e-Reader BIOS to load an application Supports a 17-byte title Loads music type ‘A’ |
0x0E | Dotcode application | Used in e-Reader BIOS to load an application Supports a 33-byte title Loads music type ‘A’ |
0x0F | Game-specific dotcode | Used in cards that connect to the GameCube |
Data checksum
Details
Calculated as the 2-byte complement (bitwise NOT) of the sum of all 2-byte segments (halfwords) outside of the header.
Size info flag
Details
The hex value at this offset is in little endian and represents a 32-bit binary number. When converted, the 32 bits represent the following:
Bit Number | Description | Info |
---|---|---|
0 | Unknown | – |
1 – 4 | Strip control | If 0001 , total dotcode strips is assumed to be 1 If any other value, the next 4 bits signify the total expected strips |
5 – 8 | Total strips | The e-Reader supports banking up to 12 strips *the original Japanese model e-Reader only supports 8 strips |
9 – 23 | Size of all strips | Calculated as the total number of bytes outside of header/title data + 2 This value MUST be the same as VPK size + 2 |
24 – 31 | Fixed | Always 00100010 |
Permission flags
Details
The hex value at this offset is in little endian and represents a 32-bit binary number. When converted, the 32 bits represent the following:
Bit Number | Description | Info |
---|---|---|
0 | Save control | If 0 , the application does not save If 1 , prompts FLASH saving |
1 | Subtitle control | If 0 , the application includes a subtitle If 1 , there is no subtitle |
2 | Application type | If 0 , the application is GBA/Z80 program If 1 , the application is an NES program |
3 - 31 | Fixed | Always filled with 0 |
Header checksum
Details
Calculated by taking the bitwise exclusive (XOR) of all individual hex values at offsets 0x0C
, 0x0D
, 0x10
, 0x11
, 0x26
, 0x27
, 0x28
, 0x29
, 0x2A
, 0x2B
, 0x2C
, and 0x2D
.
Global checksum
Details
Calculated by perfoming the following:
- Delimit all data outside of the header into 48-byte (0x2F) chunks
- For each chunk, XOR their bytes together and sum their results
- Add the previous result to the sum of bytes
0x00 - 0x2E
(all previous bytes in the header) - Take the single-byte complement (bitwise NOT) of this value
Python script that does all of this for you given an e-Reader file:
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
def process_file(file_path):
try:
with open(file_path, "rb") as f:
data = f.read()
if len(data) != 2112:
print("Size error! This is not the size of a valid e+ card")
return
header = data[:48]
chunks = [data[i:i + 48] for i in range(48, len(data), 48)]
checksums = []
for chunk in chunks:
checksum = 0
for byte in chunk:
checksum ^= byte
checksums.append(checksum)
checksum_total = sum(checksums)
header_sum = sum(header[:47])
total_sum = checksum_total + header_sum
single_byte_complement = ~total_sum & 0xFF
print(f"Global header checksum: {hex(single_byte_complement)}")
except Exception as e:
print(f"An error occurred: {e}")
# Replace with the path to an e-Reader .bin file
process_file("PATH-TO-FILE")
Card Titles
Directly after the header comes the title data, which simply describes the title of the card. The length of titles can vary and depends on the format of the card, as described in the card type table.
The hex values here are also interpreted differently depending on the region info bits in the header’s region flag. If the region is set to Japan, the bytes here are encoded in the 2-byte SHIFT-JIS character set. If the region is North America, the bytes here are encoded in standard ASCII, making them readable by default in most hex editors.
If a title is shorter than the maximum bytes, it must be padded out with 0x00
to fit within its respective length.
For the purposes of Dōbutsu no Mori e+ villager cards, the title data is 66 bytes long; 33 bytes for a “main” title, followed by 33 bytes for a “subtitle”. A typical villager’s e+ card will just use the title ぶつの森e+
for both its main title and subtitle.
Letter + GBA Details
Every standard e+ villager card has letter and GBA data attached to it. While the GBA portion is unused, the letter data controls what letter is sent to you when you scan this card at the post office. This data is found directly after the titles, starting from offset 0x0702
to 0x0104
.
Hex Offset | Size | Info |
---|---|---|
0x0702 – 0x0707 | 6 bytes | Villager name 6 characters max; if shorter, appended with 0x20 to fill 6 bytes |
0x0708 | 1 byte | Stationery type 0x00 to 0x41, corresponds to stationery order as stored in the game |
0x0709 | 1 byte | Recepient name offset Controls where to enter the recipient’s name in the letter header |
0x070A – 0x00F3 | 122 bytes | Letter text, encoded in the game’s character map (see below) |
0x00F4 – 0x00F5 | 2 bytes | Item ID for attached present |
0x00F6 | 1 byte | e-Card number |
0x00F7 - 0x00FC | 6 bytes | Card name Usually the same as the villager’s name |
0x00FD – 0x0104 | 8 bytes | Villager stats for GBA minigames Unused, since e+ cards cannot be used in GBA e-Reader minigames |
Character Encoding
The bytes controlling the letter text are encoded using Dōbutsu no Mori e+’s custom hiragana character set, where each byte corresponds to a certain character. Here’s a simple Python script I wrote that showcases the game’s character set and translates any hex values inside this letter data into readable Japanese:
Hex to DnMe+ Character Converter
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
character_map = [
"あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ", "た",
"ち", "つ", "て", "と", "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み",
" ", "!", "\"", "む", "め", "%", "&", "'", "(", ")", "~", "♥", ",", "-", ".", "♪",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", "🌢", "<", "+", ">", "?",
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "も", "💢", "や", "ゆ", "_",
"よ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "ら", "り", "る", "れ", " ",
"□", "。", "「", "」", "、", "・", "ヲ", "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ",
"ー", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ",
"タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ",
"ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ン", "ヴ", "☺",
"ろ", "わ", "を", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "ゃ", "ゅ", "ょ", "っ", "\n", "ガ", "ギ",
"グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ",
"ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ", "が", "ぎ", "ぐ", "げ", "ご", "ざ", "じ", "ず", "ぜ",
"ぞ", "だ", "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ"
]
# Provide your own hex data
hex_data = """
"""
hex_values = hex_data.split()
translated_text = "".join(character_map[int(byte, 16)] if int(byte, 16) < len(character_map) else " " for byte in hex_values)
print(translated_text)
Yaz0 Compressed Villager Data
The real meat of the e+ villager cards come in the form of the Yaz0 compressed data, which contains all of the villager information needed to move them into your town. When you swipe an e+ villager at the wishing well, this Yaz0 data gets appended to your save file. Three days later, the villager you swiped will move into your town permanently.
Since all of the villager’s attributes and textures are stored in the data embedded within the card, you can modify all of this to your liking and move in a completely custom villager. Cuyler’s Custom Villager Creator tool automatically structures custom textures and villager information as a compressed Yaz0 file that you can append to your save file manually.
Hex Offset | Size | Info |
---|---|---|
0x0000 | 1 byte | Card number |
0x0001 – 0x0006 | 6 bytes | Villager name Padded with 0x20 if less than 6 characters |
0x0007 – 0x000A | 4 bytes | Catchphrase Padded with 0x20 if less than 4 characters |
0x000B | 1 byte | Villager model 0x00 to 0x1F. See villager models |
0x000C | 1 byte | Islander flag 0x00 for town resident. 0x01 for island resident |
0x000D | 1 byte | Personality 0x00 to 0x05. See villager personality types |
0x000E | 1 byte | Shirt ID (Lower) Appended to 0x24 to make up the ID of the villager’s shirt |
0x000F | 1 byte | House type 0x00 to 0x04. See house types |
0x0010 | 1 byte | House palette 0x00 to 0x04 |
0x0011 | 1 byte | Wallpaper ID (Lower) Appended to 0x27 to make up the ID of the villager’s home wallpaper |
0x0012 | 1 byte | Carpet ID (Lower) Appended to 0x26 to make up the ID of the villager’s home carpet |
0x0013 – 0x0014 | 2 bytes | House base layer index |
0x0015 – 0x0016 | 2 bytes | House second layer index |
0x0017 | 1 byte | Umbrella ID (Lower) Appended to 0x22 to make up the ID of the villager’s umbrella |
0x0018 | 1 byte | Favorite K.K. Song (Lower) Appended to 0x2A to make up the ID of the villager’s favorite K.K. song |
0x0019 – 0x001A | 2 bytes | Town tune pitch Controls pitch/instruments of your town tune when talking to the villager |
0x001B | 1 byte | Favorite clothing type 0x00 to 0x0A. See clothing types |
0x001C | 1 byte | Hated clothing type 0x00 to 0x0A. See clothing types |
0x001D – 0x0024 | 8 bytes | GBA statistics |
0x0025 – 0x0824 | 2048 bytes | GBA head spritesheet (256x16 px) |
0x0825 - 0x0844 | 32 bytes | GBA Texture Palette RGB5A3 array |
0x0845 – 0x0864 | 32 bytes | Texture Palette RGB5A3 array |
0x0865 | varies | Texture data |
courtesy of Cuyler
Texture Image Formats
Texture palettes are stored as an array in the RGB5A3 image format, which supports 16 bits per pixel with an alpha channel. The max size of this array is 16, meaning a villager’s textures can, at maximum, include 16 different colors. The rest of the data is then delimited based on the model selected, and each bytes upper and lower nibble reference the index of the palette to control each pixel’s color in a texture.
For the GBA minigames, all head sprites for the villager are stored in a single 256x16 texture (spritesheet). The spritesheet features sixteen 16x16 sprites for a villager’s various expressions and head angles.
Villager models
Details
Hex Value | Model type |
---|---|
0x00 | Cat |
0x01 | Elephant |
0x02 | Sheep |
0x03 | Bear |
0x04 | Dog |
0x05 | Squirrel |
0x06 | Rabbit |
0x07 | Duck |
0x08 | Hippo |
0x09 | Wolf |
0x0A | Mouse |
0x0B | Pig |
0x0C | Chicken |
0x0D | Bull |
0x0E | Cow |
0x0F | Bird |
0x10 | Frog |
0x11 | Alligator |
0x12 | Goat |
0x13 | Tiger |
0x14 | Anteater |
0x15 | Koala |
0x16 | Horse |
0x17 | Octopus |
0x18 | Lion |
0x19 | Bear cub |
0x1A | Rhino |
0x1B | Gorilla |
0x1C | Ostrich |
0x1D | Kangaroo |
0x1E | Eagle |
0x1F | Penguin |
Villager personality types
Details
Hex Value | Personality type |
---|---|
0x00 | Normal |
0x01 | Peppy |
0x02 | Lazy |
0x03 | Jock |
0x04 | Cranky |
0x05 | Snooty |
House types
Details
Hex Value | House type |
---|---|
0x00 | Cottage |
0x01 | Cabin |
0x02 | Cabana |
0x03 | Barnhouse |
0x04 | Brick |
Clothing types
Details
Hex Value | Clothing type |
---|---|
0x00 | Cool |
0x01 | Cute |
0x02 | Funky |
0x03 | Fresh |
0x04 | Fancy |
0x05 | Subtle |
0x06 | Refined |
0x07 | Gaudy |
0x08 | Striking |
0x09 | Strange |
0x0A | Invalid |