├── 010_templates ├── SARC.bt └── TSCB.bt ├── README.md ├── docs ├── README.md ├── dungeons.md └── file_formats │ ├── README.md │ ├── bfres.md │ ├── blwp.md │ ├── byml.md │ ├── hght.md │ ├── mate.md │ ├── sarc.md │ ├── tscb.md │ └── yaz0.md ├── logo.png └── tools └── unyaz0.cs /010_templates/SARC.bt: -------------------------------------------------------------------------------- 1 | BigEndian(); 2 | 3 | // Global SARC header 4 | struct SARC_HEADER { 5 | char magic[4]; 6 | uint16 headerLength; 7 | uint16 byteOrder; 8 | uint32 fileSize; 9 | uint32 dataOffset; 10 | uint32 unk0; 11 | } sarc_header; 12 | 13 | // Header for each node in the archive 14 | struct SFAT_HEADER { 15 | char magic[4]; 16 | uint16 headerLength; 17 | uint16 nodeCount; 18 | uint32 hashMultiplier; 19 | } sfat_header; 20 | 21 | // Nodes are basically entities in the archive 22 | struct SFAT_NODE { 23 | uint32 fileNameHash; 24 | 25 | BitfieldDisablePadding(); 26 | uchar nodeType : 8; 27 | uint32 fileNameTableOffset : 24; // 3-byte offset into string table below 28 | BitfieldEnablePadding(); 29 | 30 | uint32 nodeDataBeginOffset; 31 | uint32 nodeDataEndOffset; 32 | } sfat_node[ sfat_header.nodeCount ]; 33 | 34 | struct SFNT_HEADER { 35 | char magic[4]; 36 | uint16 headerLength; 37 | uint16 unk0; 38 | } sfnt_header; 39 | 40 | // String table incoming 41 | struct SFNT_STR { 42 | // These strings are null padded to 4 byte boundaries, skip padding 43 | while (ReadByte() == 0) { 44 | FSeek(FTell() + 1); 45 | } 46 | 47 | string value; 48 | } sfnt_str [ sfat_header.nodeCount ] ; 49 | 50 | // Finally, data table. 51 | // Data is sequentially stored and is somtimes padded (? padding differs between files) 52 | local uint32 dataTableStart = sarc_header.dataOffset; 53 | local uint32 dataStart, dataLength; 54 | local uint32 i; 55 | for (i=0;i; 49 | } lookupCollection; 50 | 51 | // Same thing, list of offsets to some unknown struct 52 | FSeek(lookupheader0Start + lookup_header0.size); 53 | 54 | string FILE_STR_READ(struct FILE_STR& s) 55 | { 56 | return s.fileName; 57 | } 58 | 59 | local int strReadRestorePos; 60 | struct { 61 | struct FILE_STR { 62 | 63 | lastPos = FTell(); 64 | skipPos = ReadUInt(); 65 | 66 | FSkip(skipPos); 67 | 68 | float centerX; 69 | float centerY; 70 | float edgeLength; 71 | float unk0; 72 | float unk1; 73 | float unk2; 74 | float unk3; // always 0 when variableCount is 0 (maybe?) 75 | uint32 unk4; // always equal to variableCount / 4 76 | 77 | uint32 strOffset; 78 | 79 | strReadRestorePos = FTell(); 80 | FSkip(strOffset - 4); // subtract the size of stroffset 81 | 82 | string fileName; 83 | FSeek(strReadRestorePos); 84 | 85 | uint32 unk5; 86 | uint32 unk6; 87 | uint32 unk7; // always 0 or 4 88 | 89 | if (unk7 != 0) 90 | { 91 | uint32 variableCount; // signals if there is a mate, water, grass 92 | // map available to go along with the hght 93 | 94 | uint32 additionalInts[variableCount]; 95 | // 20, 3, 0, 1, 0, 3, 1, 1 = hght, mate, water, grass 96 | // 20, 3, 1, 1, 0, 3, 0, 1 = hght, mate, water, grass why two? 97 | // 3, 1, 1, 0 = hght, mate, water 98 | // 3, 0, 1, 0 = hght, mate, grass 99 | // Always seems to have unk2 == 1 and unk3 == 0.. 100 | // Also, just because there's no water map doesn't mean 101 | // that there is no water on the map. It could be sourcing from 102 | // higher LOD's or we could be misunderstanding what water maps 103 | // are 104 | // nothing = hght, mate only 105 | } 106 | 107 | FSeek(lastPos); 108 | FSkip(4); 109 | } fileStruct[header.fileCollectionCount] ; 110 | } fileCollection; 111 | 112 | FSeek(header.stringTableOffset); 113 | struct TSCB_STRINGTABLE { 114 | uint32 unk0; // TODO: 115 | uint32 unk1; // these unks are bad, they're leftover from not reading the overflow vars in the previous struct 116 | uint32 unk2; // remove them at some point 117 | uint32 unk3; 118 | 119 | local int num; 120 | while (!FEof()) { 121 | // Skip any padding (these strings are null-padded to 4-byte boundaries) 122 | while (!FEof() && ReadByte() == 0) { 123 | FSeek(FTell() + 1); 124 | } 125 | 126 | if (FEof()) { 127 | break; 128 | } 129 | 130 | struct STRINGTABLE_ENTRY { 131 | string name; 132 | } stringtable_entry; 133 | } 134 | 135 | } stringtable; 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | The Legend of Zelda: Breath of the Wild
4 | Modding Documentation and Tools 5 |

6 | 7 | ### Documentation 8 | Documentation on various different file formats used throughout BotW can all be 9 | found in the `docs` folder in the project root directory. 10 | 11 | ### Contributing 12 | Contributions are always welcome, whether it's modifying source code to add new 13 | features or bug fixes, documenting new file formats or simply editing some 14 | grammar. 15 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | * [File Formats](file_formats/) 2 | -------------------------------------------------------------------------------- /docs/dungeons.md: -------------------------------------------------------------------------------- 1 | Dungeon levels are stored in [`SARC`](file_formats/sarc.md) archive files in 2 | the Packs directory, files are named `Dungeonxxx.pack` from `000` to `119` 3 | numerically. 4 | 5 | The extracted directory hierarchy tree structure on average follows this: 6 | 7 | ``` 8 | * `Actor` 9 | * `Pack` 10 | * `DgnMrgPrt_Dungeon000.sbactorpack` 11 | * `Map` 12 | * `CDungeon` 13 | * `Dungeon000` 14 | * `Dungeon000_Clustering.sblwp` 15 | * `Dungeon000_Dynamic.smubin` 16 | * `Dungeon000_Static.smubin` 17 | * `Dungeon000_TeraTree.sblwp` 18 | * `DungeonData` 19 | * `CDungeon` 20 | * `Dungeon000.bdgnenv` 21 | * `Model` 22 | * `DgnMrgPrt_Dungeon000.sbfres` 23 | * `DgnMrgPrt_Dungeon000.Tex2.sbfres` 24 | * `NavMash` 25 | * `CDungeon` 26 | * `Dungeon000` 27 | * `Dungeon000.shknm2` 28 | * `Physics` 29 | * `StaticCompound` 30 | * `CDungeon` 31 | * `Dungeon000.shksc` 32 | -------------------------------------------------------------------------------- /docs/file_formats/README.md: -------------------------------------------------------------------------------- 1 | ## Raw Formats By Extension 2 | 3 | * [`arc`](arc.md) - Generic SARC archive. 4 | * [`bfres`](bfres.md) - [!] 5 | * [`blwp`](blwp.md) - List of instanced meshes. 6 | * [`byml`](byml.md) - Binary YAML file. 7 | * [`hght`](hght.md) - Heightmap referenced by `tscb`. 8 | * [`mate`](mate.md) - Material maps referenced by `hght`. 9 | * [`pack`](sarc.md) - SARC archive containing many other archives. 10 | * [`sarc`](sarc.md) - Generic SARC archive. 11 | * [`tscb`](tscb.md) - Terrain file. 12 | 13 | ## Compressed Formats By Extension 14 | 15 | A lot of files are compressed or packed in Breath of the Wild. The most common 16 | type of compression is [Yaz0](yaz0.md) a run length encoding method. 17 | 18 | * `sbfres`: [bfres](bfres.md) compressed with [Yaz0](yaz0.md). 19 | * `sblwp`: [blwp](blwp.md) compressed with [Yaz0](yaz0.md). 20 | * `sbyml`: [byml](byml.md) compressed with [Yaz0](yaz0.md). 21 | * `szs`: [sarc](sarc.md) compressed with [Yaz0](yaz0.md). 22 | -------------------------------------------------------------------------------- /docs/file_formats/bfres.md: -------------------------------------------------------------------------------- 1 | model format 2 | -------------------------------------------------------------------------------- /docs/file_formats/blwp.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | These files contain transformation data of static instanced meshes in Breath of the Wild. 4 | Each mesh is identified by a name in the string table and can have one or many 5 | instances at various transformations. 6 | 7 | Each fragment of the map has these. And dungeons too. 8 | 9 | 10 | ## Data Structure 11 | 12 | ### Header 13 | 14 | | Offset | Size | Description | 15 | |:------:|:----:|-------------------------------------------------------| 16 | | 0x00 | 4 | String "PrOD" in ASCII (file identifier). | 17 | | 0x04 | 2 | Version 0x0001 in Breath of the Wild. | 18 | | 0x08 | 4 | Always 1 | 19 | | 0x0C | 4 | Unknown | 20 | | 0x10 | 4 | File Size. | 21 | | 0x14 | 4 | Number of Meshes | 22 | | 0x18 | 4 | String Table Offset | 23 | | 0x1C | 4 | Padding | 24 | 25 | ### Mesh 26 | 27 | | Offset | Size | Description | 28 | |:------:|:----:|---------------------------------| 29 | | 0x00 | 4 | Size of Instances (count * 32) | 30 | | 0x04 | 2 | Instances Count | 31 | | 0x08 | 4 | Mesh Name (String Table Offset) | 32 | | 0x0C | 4 | Null Padding | 33 | 34 | #### Mesh Instance 35 | 36 | | Offset | Size | Description | 37 | |:------:|:----:|---------------------------------| 38 | | 0x00 | 12 | Position Vector (3 floats) | 39 | | 0x04 | 12 | Rotation (d) Vector (3 floats) | 40 | | 0x08 | 4 | Uniform Scale Float | 41 | | 0x0C | 4 | Null Padding | 42 | 43 | ### Mesh Name String Table 44 | 45 | | Offset | Size | Description | 46 | |:------:|:----:|----------------------------------------| 47 | | 0x00 | 4 | Number of strings in the string table. | 48 | | 0x04 | 4 | Size of the string table. | 49 | 50 | Each string is null terminated and then padded up to the next highest 4 byte 51 | alignment. 52 | -------------------------------------------------------------------------------- /docs/file_formats/byml.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | BYML is a binary YAML file and is used as a generic data container throughout 4 | Breath of the Wild. Unlike other Nintendo games Breath of the Wild identifies 5 | its version as `0x02` being the first to do so. 6 | 7 | BYML is mostly used in the following files: `Actor/ActorInfo.product.sbyml`, 8 | `Event/EventInfo.sbyml` and extensively throughout `Bootup.pack` storing data 9 | about tips, shop information, status effects, area data and more. 10 | 11 | ## Data Structure 12 | 13 | The file is broken up into a node structure, with possible interlinking between 14 | the nodes. Each node has a one byte format identifier. The File begins with a 15 | header which points to three special nodes; the hash key table; 16 | the string table node and the root node. 17 | 18 | ### Header 19 | 20 | | Offset | Size | Description | 21 | |:------:|:----:|----------------------------------------------------------------------------------------------------------------------------------------------------------------| 22 | | 0x00 | 2 | String "BY" (big endian) or "YB" (little endian) in ASCII (file identifier). | 23 | | 0x02 | 2 | Version 0x0002 in Breath of the Wild. Values of 1 and 3 are accepted as well. | 24 | | 0x04 | 4 | Offset to the hash key table, relative to start (usually 0x010). May be 0 if no hash nodes are used. Must be a string value node (0xc2). | 25 | | 0x08 | 4 | Offset to the string table, relative to start. May be 0 if no strings are used. Must be a string value node (0xc2). | 26 | | 0x0c | 4 | Offset to the root node, relative to start. Must be either an array node (0xc0) or a hash node (0xc1). | 27 | 28 | ### Nodes 29 | 30 | Every node format has a unique one byte identifier as follows. Some nodes are 31 | considered value nodes as indicated below. The container nodes have a longer encoding 32 | in the file, which must be four byte aligned. The order of encoding full nodes 33 | within the file does not seem to matter. 34 | 35 | | Identifier | Type | Description | 36 | |:----------:|:----------------------:|------------------------------------------------------------------------------------------------------| 37 | | 0xA0 | Value (Index) | String. Value is an index into the string table. | 38 | | 0xC0 | Container | Array. Node is an array of nodes, typically, though not necessarily, all of the same format. | 39 | | 0xC1 | Container | Hash. Node is a mapping from strings in the hash key table to other nodes. | 40 | | 0xC2 | Container (Special) | String table. Special purpose node type only seen in the hash key table and the string table. | 41 | | 0xD0 | Value | Bool. Node is 1 or 0 representing true or false respectively. | 42 | | 0xD1 | Value | Int. Node is a signed 32 bit integer value. | 43 | | 0xD2 | Value | Float. Node is a binary32 floating-point number. | 44 | | 0xD3 | Value | UInt. Node is an unsigned 32 bit integer value. The game uses this for some CRC32 hashes and for masks. | 45 | | 0xD4 | Value (Special) | Int64. Node is a 64 bit integer value. Not seen in Breath of the Wild. | 46 | | 0xD5 | Value (Special) | UInt64. Node is an unsigned 64 bit integer value. Not seen in Breath of the Wild. | 47 | | 0xD6 | Value (Special) | Double. Node is a binary64 floating-point number. Not seen in Breath of the Wild. | 48 | | 0xFF | Value | Null. Value is always 0. Not seen in Breath of the Wild. | 49 | 50 | 51 | #### Value Nodes 52 | Value nodes can only be encoded as children of container nodes. Each value node has 53 | a direct four byte encoding. 54 | 55 | For string nodes, this encoding is simply a four byte index into the string table respectively. 56 | 57 | For special value nodes, the value is an offset to a 64 bit integer / floating-point number relative 58 | to the start of the file. 59 | 60 | #### 0xC0 - Array Node 61 | 62 | | Offset | Size | Description | 63 | |:----------:|:----:|-----------------------------------------------------------------------------| 64 | | 0x00 | 1 | 0xC0 node type. | 65 | | 0x01 | 3 | Number of entries in array. | 66 | | 0x04 | N | Array of N node types; the type of each element in the array. | 67 | | 0x04 + N' | 4\*N | Array of N node values. For regular value nodes, this is a 4 byte node value. For other nodes, this is a 4 byte offset to the node relative to the start of the file. | 68 | 69 | N' is N rounded up to the nearest multiple of 4. 70 | 71 | #### 0xC1 - Hash Node 72 | 73 | Hash / dictionary nodes are used to encode name value collections. The order of entries 74 | in the dictionary does not seem to matter. 75 | 76 | | Offset | Size | Description | 77 | |:------:|:----:|----------------------------------| 78 | | 0x00 | 1 | 0xC1 node type. | 79 | | 0x01 | 3 | Number of entries in dictionary. | 80 | 81 | What then follows is a variable length array of dictionary entries. Each entry 82 | has the following structure. 83 | 84 | | Offset | Size | Description | 85 | |:------:|:----:|------------------------------------------------------------------------------------------------------------------------------------------| 86 | | 0x00 | 3 | Name. Value is an index in the hash key table. | 87 | | 0x03 | 1 | The node type. | 88 | | 0x04 | 4 | Value. For regular value nodes, this is the 4 byte node value. For other nodes, this is a 4 byte offset to the node relative to the start of the file. | 89 | 90 | #### 0xC2 - String Table Node 91 | 92 | | Offset | Size | Description | 93 | |:------:|:----------:|-----------------------------------------------------------------------------------------------------------------------------------------------| 94 | | 0x00 | 1 | 0xC2 node type. | 95 | | 0x01 | 3 | Number of entries in the string table. | 96 | | 0x04 | Y=4\*(N+1) | Array of N+1 offsets to each string relative to the start of the node. The last entry is an offset to the end of the last string. | 97 | | 0x04+Y'| variable | Array of N null-terminated strings stored in alphabetical order. | 98 | 99 | Y' is Y rounded up to the nearest multiple of 4. 100 | -------------------------------------------------------------------------------- /docs/file_formats/hght.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | These files are contained within the archives of `Terrain/A/MainField` and referenced by 4 | the [`MainField.tscb`](tscb.md). Each one contains the height map data for it's relevent 5 | grid tile related to it's LOD. 6 | 7 | HGHT maps are accompanied by a series of other terrain-descriptor formats. For each 8 | HGHT map, there is an optional GRASS, WATER, and MATE map. 9 | 10 | ## Data Structure 11 | 12 | Incredibly simple, each height map seems to be `256x256x2` (`131072`) bytes long. 13 | This gives us 16 bits of accuracy per heightmap point, each point is encoded as 14 | an unsigned short. 15 | -------------------------------------------------------------------------------- /docs/file_formats/mate.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | This file is a complementary map for the heightmaps in the game describing (at least) 4 | the materials that the game world is using. 5 | 6 | ## Data Structure 7 | 8 | This map describes the materials used by the terrain renderer. 9 | 10 | | Channel | Size | Description | 11 | |:-------:|:----:|------------------------------------------------------------------------------------| 12 | | Red | 1 | Material ID's. Range from 0-88. Maybe used by the lookup table in TSCB (also 0-88) | 13 | | Green | 1 | Unknown | 14 | | Blue | 1 | Unknown | 15 | | Alpha | 1 | Unknown | 16 | -------------------------------------------------------------------------------- /docs/file_formats/sarc.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | SARC is a proprietary archive format used throughout Breath of the Wild, common 4 | file extensions that use this format are: `.arc`, `.sarc`, `.pack` (when the 5 | archive contains mostly other archive files), `.bars` (audio data) and 6 | `.bgenv` (shader files). They are also sometimes compressed using [yaz0](yaz0.md) 7 | into `.szs` files 8 | 9 | SARC header followed by a file table, file name table, finally content is stored. 10 | 11 | ## Data Structure 12 | 13 | ### SARC Header 14 | 15 | | Offset | Size | Description | 16 | |:------:|:----:|------------------------------------------------------------------------------------| 17 | | 0x00 | 4 | String "SARC" in ASCII (file identifier). | 18 | | 0x04 | 2 | Header Length: length of this header (**0x14**) | 19 | | 0x06 | 2 | Byte order mark (BOM): 0xFE, 0xFF for big endian and 0xFF, 0xFE for little endian. | 20 | | 0x08 | 4 | File size of the entire archive in bytes. | 21 | | 0x0C | 4 | Beginning of data offset. | 22 | | 0x10 | 4 | Unknown. Always 0x01000000? | 23 | 24 | ### File Table 25 | 26 | #### SFAT Header 27 | 28 | The SARC Header is immediately followed by the following structure. 29 | 30 | | Offset | Size | Description | 31 | |:------:|:----:|------------------------------------------------------------------------------------| 32 | | 0x00 | 4 | String "SFAT" in ASCII (identifier). | 33 | | 0x04 | 2 | Header Length: length of this header (**0x0C**) | 34 | | 0x06 | 2 | Node count. | 35 | | 0x08 | 4 | Hash Multiplier. Always 0x00000065. | 36 | 37 | #### Node 38 | 39 | The SFAT Header is followed by an array of SFAT Node structures. 40 | 41 | | Offset | Size | Description | 42 | |:------:|:----:|-----------------------------------------------------------------------------------------------------| 43 | | 0x00 | 4 | File name hash. | 44 | | 0x04 | 1 | 0x00 for archives without any file name stored, 0x01 for archives with file names stored. | 45 | | 0x05 | 3 | File name table entry offset, relative to the end of the file name table header, divided by 4. | 46 | | 0x08 | 4 | Beginning of node file data, relative to the Beginning of data offset specified in the SARC header. | 47 | | 0x0C | 4 | End of node file data, relative to the Beginning of data offset specified in the SARC header. | 48 | 49 | The file name hash is calculated like so: 50 | 51 | ```c 52 | uint GetHash(char* name, int length, uint multiplier) 53 | { 54 | uint result = 0; 55 | for(int i = 0; i < length; i++) 56 | result = name[i] + result * multiplier; 57 | return result; 58 | } 59 | ``` 60 | 61 | ### File Name Table 62 | 63 | The File Name Table is a list of null-terminated strings which represent the 64 | filenames of the packed files. It is referenced by SFAT Nodes. 65 | 66 | #### SFNT Header 67 | The SFAT Node array is immediately followed by an 0x8 byte SFNT Header structure. 68 | 69 | | Offset | Size | Description | 70 | |:------:|:----:|----------------------------------------------------------------| 71 | | 0x00 | 4 | String "SFNT" in ASCII (identifier). | 72 | | 0x04 | 2 | Header Length: the length of this header in bytes. Always 0x8. | 73 | | 0x06 | 2 | Unknown, always zero? | 74 | 75 | #### Strings 76 | The SFAT Header is immediately followed by 4-byte aligned null-terminated 77 | strings that represent the filenames of the packed files. 78 | -------------------------------------------------------------------------------- /docs/file_formats/tscb.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | TSCB is only used once throughout the entire game: `Terrain/A/MainField.tscb` 4 | 5 | It is some sort of master file for all files in the MainField folder, containing 6 | a list of every "section" of the map inside. 7 | 8 | ## Data Structure 9 | 10 | ### TSCB Header 11 | 12 | | Offset | Size | Description | 13 | |:------:|:----:|-------------------------------------------| 14 | | 0x00 | 4 | String "TSCB" in ASCII (file identifier). | 15 | | 0x04 | 4 | Unknown (0x0A000000) | 16 | | 0x08 | 4 | Unknown (1) | 17 | | 0x0C | 4 | String table offset. | 18 | | 0x10 | 4 | Unknown float. (500.0f) | 19 | | 0x10 | 4 | Unknown float (800.0f) | 20 | | 0x14 | 4 | Material lookup table count (88) | 21 | | 0x18 | 4 | Tile table count (9033) | 22 | | 0x1C | 4 | Unknown (0) | 23 | | 0x20 | 4 | Unknown (0) | 24 | | 0x24 | 4 | Unknown float (32.0f) | 25 | | 0x28 | 4 | Unknown (8) | 26 | | 0x2C | 4 | Size of Material Lookup Table (2116) | 27 | 28 | ### Material Lookup Table 29 | 30 | Immediately after the TSCB header are 88 `uint32`s, as described by the count in 31 | the header. Each `uint32` represents an absolute offset to the applicable material 32 | lookup struct below. 33 | 34 | | Offset | Size | Description | 35 | |:------:|:----:|------------------------------------------------| 36 | | 0x00 | 4 | `uint32` Index. Goes from 0 to 88 numerically. | 37 | | 0x04 | 4 | `float` Red? | 38 | | 0x08 | 4 | `float` Green? | 39 | | 0x0C | 4 | `float` Blue? | 40 | | 0x10 | 4 | `float` Alpha? | 41 | 42 | This table is possibly used by the terrain MATE format, the red channel could 43 | specify the material index as it tends to obey values between 0-88. 44 | 45 | ### Tile Table 46 | 47 | Immediately after the Material Lookup Table ( Header + Size Of Material Lookup 48 | Table) is the Tile Table. 9033 entries describing tiles of the map each with 49 | additional file descriptors in the MainField folder. 50 | 51 | | Offset | Size | Description | 52 | |:------:|:----:|---------------------------------------------------------------------| 53 | | 0x00 | 4 | `float centerX` | 54 | | 0x04 | 4 | `float centerY` | 55 | | 0x08 | 4 | `float edgeLength` | 56 | | 0x0C | 4 | `float unk0` | 57 | | 0x10 | 4 | `float unk1` | 58 | | 0x14 | 4 | `float unk2` | 59 | | 0x18 | 4 | `float unk3` | 60 | | 0x1C | 4 | `uint32 unk4` (always equal to variableCount / 4) | 61 | | 0x20 | 4 | `uint32 stringOffset` offset to the tile "name" in the string table | 62 | | 0x24 | 4 | `uint32 unk5` Always 0. | 63 | | 0x28 | 4 | `uint32 unk6` Always 0. | 64 | | 0x2C | 4 | `uint32 unk7` ( always seems to be 0 or 4) | 65 | 66 | Furthermore, **if `unk7` is NOT 0** there will be a further `uint32` at `0x30` 67 | describing a number of further `uint32`s in the structure. 68 | -------------------------------------------------------------------------------- /docs/file_formats/yaz0.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | Yaz0 is a run length encoding method used throughout Breath of the Wild. The 4 | format is thoroughly understood and has a variety of tools to decompress it. 5 | 6 | A simple tool to decompress Yaz0 files can be found in [`/tools/unyaz0.cs`](/tools/unyaz0.cs). (Compile using `csc unyaz0.cs`) 7 | 8 | ## Data Structure 9 | 10 | ### Header 11 | 12 | | Offset | Type | Description | 13 | |:------:|:--------:|-------------------------------| 14 | | 0x00 | char[4] | "Yaz0" | 15 | | 0x04 | uint32_t | size of the uncompressed data | 16 | | 0x08 | uint32_t | reserved | 17 | | 0x0C | uint32_t | reserved | 18 | 19 | Certain games use the first reserved field for the alignment of the files data if the compressed file is a [SARC](sarc.md). 20 | 21 | ### Data Groups 22 | 23 | The complete compressed data is organized in data groups. Each data group 24 | consists of 1 group header byte and 8 chunks: 25 | 26 | | N | Size | Description | 27 | |:-:|:---------:|-------------------------------| 28 | | 1 | 1 byte | the group header byte | 29 | | 8 | 1-3 bytes | 8 chunks | 30 | 31 | Each bit of the group header correspondents to one chunk: 32 | * The MSB (most significant bit, 0x80) correspondents to chunk 1 33 | * The LSB (lowest significant bit, 0x01) correspondents to chunk 8 34 | 35 | A set bit (=1) in the group header means, that the chunk is exact 1 byte long. This byte must be copied to the output stream 1:1. A cleared bit (=0) defines, that the chunk is 2 or 3 bytes long interpreted as a back reference to already decompressed data that must be copied. 36 | 37 | | Size | Data Bytes | Size Calculation | 38 | |---------|------------|-----------------------------------------| 39 | | 2 bytes | NR RR | N = 1..f SIZE = N+2 (=3..0x11) | 40 | | 3 bytes | 0R RR NN | N= 00..ff SIZE = N+0x12 (=0x12..0x111) | 41 | 42 | * RRR is a value between 0x000 and 0xfff. Go back RRR+1 bytes in the output stream to find the start of the data to be copied. 43 | * SIZE is calculated from N (see above) and declares the number of bytes to be copied. 44 | * It is important to know, that a chunk may reference itself. For example if RRR=1 (go back 1+1=2) and SIZE=10 the previous 2 bytes are copied 10/2=5 times. 45 | 46 | Decoding data groups and chunks is done until the end of the destination data is reached. 47 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/handsomematt/botw-modding/a490f2c485bfea252d362461634a6ff9978953b1/logo.png -------------------------------------------------------------------------------- /tools/unyaz0.cs: -------------------------------------------------------------------------------- 1 | // unyaz0 2 | // compile using: `csc unyaz0.cs` 3 | 4 | using System; 5 | using System.IO; 6 | 7 | namespace unyaz0 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | if (args.Length != 2) 14 | { 15 | Console.WriteLine("usage: {0} ", Environment.GetCommandLineArgs()[0]); 16 | Environment.Exit(1); 17 | } 18 | 19 | var input = new FileStream(args[0], FileMode.Open, FileAccess.Read); 20 | var output = new FileStream(args[1], FileMode.Create, FileAccess.ReadWrite); 21 | 22 | Decompress(input, output); 23 | } 24 | 25 | static UInt16 Reverse(UInt16 value) 26 | { 27 | byte[] bytes = BitConverter.GetBytes(value); 28 | Array.Reverse(bytes); 29 | return BitConverter.ToUInt16(bytes, 0); 30 | } 31 | 32 | static UInt32 Reverse(UInt32 value) 33 | { 34 | byte[] bytes = BitConverter.GetBytes(value); 35 | Array.Reverse(bytes); 36 | return BitConverter.ToUInt32(bytes, 0); 37 | } 38 | 39 | static void Decompress(FileStream input, FileStream output) 40 | { 41 | using (var reader = new BinaryReader(input)) 42 | using (var writer = new BinaryWriter(output)) 43 | { 44 | if (Reverse(reader.ReadUInt32()) != 0x59617A30) 45 | throw new Exception("Invalid Yaz0 header."); 46 | 47 | var decompressedSize = Reverse(reader.ReadUInt32()); 48 | reader.BaseStream.Seek(8, SeekOrigin.Current); 49 | 50 | int decompressedBytes = 0; 51 | while (decompressedBytes < decompressedSize) 52 | { 53 | var groupConfig = reader.ReadByte(); 54 | for (int i = 7; i >= 0; i--) 55 | { 56 | if ((groupConfig & (1 << i)) == (1 << i)) 57 | { 58 | writer.Write(reader.ReadByte()); 59 | decompressedBytes++; 60 | } 61 | else if (decompressedBytes < decompressedSize) 62 | { 63 | var dataBackSeekOffset = Reverse(reader.ReadUInt16()); 64 | int dataSize; 65 | 66 | byte nibble = (byte)(dataBackSeekOffset >> 12); 67 | if (nibble == 0) 68 | dataSize = reader.ReadByte() + 0x12; 69 | else 70 | { 71 | dataSize = nibble + 0x02; 72 | dataBackSeekOffset &= 0x0FFF; 73 | } 74 | 75 | for (int j = 0; j < dataSize; j++) 76 | { 77 | writer.BaseStream.Seek(-dataBackSeekOffset - 1, SeekOrigin.Current); 78 | byte readByte = (byte)writer.BaseStream.ReadByte(); 79 | writer.Seek(0, SeekOrigin.End); 80 | writer.Write(readByte); 81 | decompressedBytes++; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | --------------------------------------------------------------------------------