Post

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 OffsetSizeDescription
0x0000 – 0x002F48 bytesUniversal e-Reader header
0x0030 – 0x007166 bytesTitle data (SHIFT-JIS encoded)
0x0072 – 0x00FC159 bytesLetter data
0x00FD – 0x01048 bytesGBA minigame statistics (unused)
0x0105variesYaz0-compressed villager data
Max size of 1851 (0x073B) bytes

padding data can occur after the Yaz0-compressed villager data

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 OffsetSizeDescriptionValueInfo
0x00 – 0x012 bytesFixed0x0030
0x021 byteFixed0x01
0x031 bytePrimary typevariesSee primary type flag
0x04 – 0x052 bytesFixed0x0001
0x06 – 0x072 bytesDot code lengthvaries0x0810 for long strip
0x0510 for short strip
0x08 – 0x0B4 bytesFixed0x00001012
0x0C – 0x0D2 bytesRegion typevariesSee region type flag
0x0E1 byteDot code typevaries0x01 for long strip
0x02 for short strip
0x0F1 byteFixed0x00
0x10 – 0x112 bytesUnknownvariesCan be set to anything
0x121 byteFixed0x10
0x13 – 0x142 bytesData checksumvariesSee data checksum
0x15 – 0x195 bytesFixed0x1900000008
0x1A – 0x218 bytesID string0x4E494E54454E444F‘NINTENDO’ in ASCII
0x22 – 0x254 bytesFixed0x00220009
0x26 – 0x294 bytesSize infovariesSee size info flag
0x2A – 0x2D4 bytesPermission flagsvariesSee permission flags
0x2E1 byteHeader checksumvariesSee header checksum
0x2F1 byteGlobal checksumvariesSee 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 NumberDescriptionInfo
0 – 7Unknown
8 – 11Region Info0000 represents a Japanese e-Card
0001 represents a North American / Australian e-Card
12 – 15Lower bits of card typeUsed 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 ValueCard TypeInfo
0x00 or 0x01BlankLoads a blank screen
0x02 or 0x03Dotcode applicationUsed in e-Reader BIOS to load an application
Supports a 17-byte title
Loads music type ‘A’
0x04 or 0x05Dotcode applicationUsed in e-Reader BIOS to load an application
Supports a 17-byte title
Loads music type ‘A’
0x0EDotcode applicationUsed in e-Reader BIOS to load an application
Supports a 33-byte title
Loads music type ‘A’
0x0FGame-specific dotcodeUsed 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 NumberDescriptionInfo
0Unknown
1 – 4Strip controlIf 0001, total dotcode strips is assumed to be 1
If any other value, the next 4 bits signify the total expected strips
5 – 8Total stripsThe e-Reader supports banking up to 12 strips
*the original Japanese model e-Reader only supports 8 strips
9 – 23Size of all stripsCalculated as the total number of bytes outside of header/title data + 2
This value MUST be the same as VPK size + 2
24 – 31FixedAlways 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 NumberDescriptionInfo
0Save controlIf 0, the application does not save
If 1, prompts FLASH saving
1Subtitle controlIf 0, the application includes a subtitle
If 1, there is no subtitle
2Application typeIf 0, the application is GBA/Z80 program
If 1, the application is an NES program
3 - 31FixedAlways 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:

  1. Delimit all data outside of the header into 48-byte (0x2F) chunks
  2. For each chunk, XOR their bytes together and sum their results
  3. Add the previous result to the sum of bytes 0x00 - 0x2E (all previous bytes in the header)
  4. 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 OffsetSizeInfo
0x0702 – 0x07076 bytesVillager name
6 characters max; if shorter, appended with 0x20 to fill 6 bytes
0x07081 byteStationery type
0x00 to 0x41, corresponds to stationery order as stored in the game
0x07091 byteRecepient name offset
Controls where to enter the recipient’s name in the letter header
0x070A – 0x00F3122 bytesLetter text, encoded in the game’s character map (see below)
0x00F4 – 0x00F52 bytesItem ID for attached present
0x00F61 bytee-Card number
0x00F7 - 0x00FC6 bytesCard name
Usually the same as the villager’s name
0x00FD – 0x01048 bytesVillager 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 OffsetSizeInfo
0x00001 byteCard number
0x0001 – 0x00066 bytesVillager name
Padded with 0x20 if less than 6 characters
0x0007 – 0x000A4 bytesCatchphrase
Padded with 0x20 if less than 4 characters
0x000B1 byteVillager model
0x00 to 0x1F. See villager models
0x000C1 byteIslander flag
0x00 for town resident. 0x01 for island resident
0x000D1 bytePersonality
0x00 to 0x05. See villager personality types
0x000E1 byteShirt ID (Lower)
Appended to 0x24 to make up the ID of the villager’s shirt
0x000F1 byteHouse type
0x00 to 0x04. See house types
0x00101 byteHouse palette
0x00 to 0x04
0x00111 byteWallpaper ID (Lower)
Appended to 0x27 to make up the ID of the villager’s home wallpaper
0x00121 byteCarpet ID (Lower)
Appended to 0x26 to make up the ID of the villager’s home carpet
0x0013 – 0x00142 bytesHouse base layer index
0x0015 – 0x00162 bytesHouse second layer index
0x00171 byteUmbrella ID (Lower)
Appended to 0x22 to make up the ID of the villager’s umbrella
0x00181 byteFavorite K.K. Song (Lower)
Appended to 0x2A to make up the ID of the villager’s favorite K.K. song
0x0019 – 0x001A2 bytesTown tune pitch
Controls pitch/instruments of your town tune when talking to the villager
0x001B1 byteFavorite clothing type
0x00 to 0x0A. See clothing types
0x001C1 byteHated clothing type
0x00 to 0x0A. See clothing types
0x001D – 0x00248 bytesGBA statistics
0x0025 – 0x08242048 bytesGBA head spritesheet (256x16 px)
0x0825 - 0x084432 bytesGBA Texture Palette
RGB5A3 array
0x0845 – 0x086432 bytesTexture Palette
RGB5A3 array
0x0865variesTexture 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 ValueModel type
0x00Cat
0x01Elephant
0x02Sheep
0x03Bear
0x04Dog
0x05Squirrel
0x06Rabbit
0x07Duck
0x08Hippo
0x09Wolf
0x0AMouse
0x0BPig
0x0CChicken
0x0DBull
0x0ECow
0x0FBird
0x10Frog
0x11Alligator
0x12Goat
0x13Tiger
0x14Anteater
0x15Koala
0x16Horse
0x17Octopus
0x18Lion
0x19Bear cub
0x1ARhino
0x1BGorilla
0x1COstrich
0x1DKangaroo
0x1EEagle
0x1FPenguin

Villager personality types

Details
Hex ValuePersonality type
0x00Normal
0x01Peppy
0x02Lazy
0x03Jock
0x04Cranky
0x05Snooty

House types

Details
Hex ValueHouse type
0x00Cottage
0x01Cabin
0x02Cabana
0x03Barnhouse
0x04Brick

Clothing types

Details
Hex ValueClothing type
0x00Cool
0x01Cute
0x02Funky
0x03Fresh
0x04Fancy
0x05Subtle
0x06Refined
0x07Gaudy
0x08Striking
0x09Strange
0x0AInvalid
This post is licensed under CC BY 4.0 by the author.