1 module rsrc; 2 3 4 struct RSRCResource 5 { 6 int typeNum; 7 ushort resourceID; 8 bool purgeable; 9 string name = null; 10 const(ubyte)[] content; 11 ushort nameOffset; 12 uint dataOffset; 13 } 14 15 struct RSRCType 16 { 17 char[4] id; 18 int numRes() 19 { 20 return cast(int)(resIndices.length); 21 } 22 int[] resIndices; // indices of resources of this type 23 } 24 25 struct RSRCWriter 26 { 27 ubyte[] buffer; 28 RSRCResource[] resources; 29 RSRCType[] types; 30 31 void addType(char[4] id) 32 { 33 types ~= RSRCType(id, []); 34 } 35 36 37 void addResource(int typeNum, ushort resourceID, bool purgeable, string name, const(ubyte)[] content) 38 { 39 types[typeNum].resIndices ~= cast(int)(resources.length); 40 resources ~= RSRCResource(typeNum, resourceID, purgeable, name, content); 41 } 42 43 ubyte[] write() 44 { 45 // compute resource data 46 ubyte[] resourceData; 47 foreach(ref r; resources) 48 { 49 r.dataOffset = cast(uint)resourceData.length; 50 resourceData.writeBE_uint(cast(uint)(r.content.length)); 51 resourceData ~= r.content; 52 } 53 54 ubyte[] resourceMap = buildResourceMap(); 55 56 ubyte[] buffer; 57 58 // Offset from beginning of resource file to resource data. Basically guaranteed to be 0x100. 59 buffer.writeBE_uint(0x100); 60 61 /// Offset from beginning of resource file to resource map. 62 buffer.writeBE_uint(0x100 + cast(uint)(resourceData.length)); 63 64 // Length of resource data 65 buffer.writeBE_uint(cast(uint)(resourceData.length)); 66 67 // Length of resource map 68 buffer.writeBE_uint(cast(uint)(resourceMap.length)); 69 70 // System-reserved data. In practice, this is usually all null bytes. 71 foreach(n; 0..112) 72 buffer.writeBE_ubyte(0); 73 foreach(n; 0..128) 74 buffer.writeBE_ubyte(0); 75 76 // Undocumented, but rez does fill the first 16 77 // bytes of resourceMap with the same beginning of 78 // the resource file header 79 resourceMap[0..16] = buffer[0..16]; 80 return buffer ~ resourceData ~ resourceMap; 81 } 82 83 ubyte[] buildResourceMap() 84 { 85 ubyte[] buf; 86 foreach(n; 0..16) 87 buf.writeBE_ubyte(0); // Note: modified afterwards to duplicate file header 88 89 foreach(n; 0..4) 90 buf.writeBE_ubyte(0); 91 92 // Don't know what this number of for, but it changes with builds 93 // distort => 0x0A00 94 // panagement => 0x0900 95 // couture => 0x0A00 96 // graillon => 0x0A00 97 buf.writeBE_ushort(0x0A00); 98 99 100 buf.writeBE_ushort(0); 101 102 ubyte[] resourceName = buildResourceNameList(); 103 ubyte[] typelist = buildTypeList(); 104 105 // Offset from beginning of resource map to type list. 106 buf.writeBE_ushort(28); // = size of resource map header 107 108 // Offset from beginning of resource map to resource name list. 109 buf.writeBE_ushort(cast(ushort)(28 + typelist.length)); 110 111 return buf ~ typelist ~ resourceName; 112 } 113 114 ubyte[] buildTypeList() 115 { 116 ubyte[] buf; 117 buf.writeBE_ushort( cast(ushort)(types.length - 1)); 118 119 ubyte[] referenceLists; 120 121 int offsetOfFirstReferenceList = 2 + cast(int)(types.length) * 8; 122 123 int resMysteriousID = 0x100_0000; 124 125 foreach(t; types) 126 { 127 // Resource type. This is usually a 4-character ASCII mnemonic, but may be any 4 bytes. 128 foreach(n; 0..4) 129 buf.writeBE_ubyte(cast(ubyte)(t.id[n])); 130 131 // Number of resources of this type in the map minus 1. 132 buf.writeBE_ushort( cast(ushort)(t.numRes - 1)); 133 134 // Offset from beginning of type list to reference list for resources of this type. 135 buf.writeBE_ushort( cast(ushort)( offsetOfFirstReferenceList + referenceLists.length) ); 136 137 // build reference list for this type 138 foreach(rindex; 0..t.numRes) 139 { 140 const(RSRCResource)* res = &resources[t.resIndices[rindex]]; 141 142 // Resource ID 143 referenceLists.writeBE_ushort(res.resourceID); 144 145 // Offset from beginning of resource name list to length of resource name, or -1 (0xffff) if none. 146 referenceLists.writeBE_ushort(res.nameOffset); 147 148 // Resource attributes. Combination of ResourceAttrs flags, see below. (Note: packed into 4 bytes together with the next 3 bytes.) 149 referenceLists.writeBE_ubyte(res.purgeable ? (1 << 5) : 0); 150 151 // Offset from beginning of resource data to length of data for this resource. (Note: packed into 4 bytes together with the previous 1 byte.) 152 uint doffset = res.dataOffset; 153 referenceLists.writeBE_ubyte((doffset & 0xff0000) >> 16); 154 referenceLists.writeBE_ubyte((doffset & 0x00ff00) >> 8); 155 referenceLists.writeBE_ubyte((doffset & 0x0000ff) ); 156 157 // Reserved for handle to resource (in memory). "Should be 0 in file." 158 // But it is not actually zero when written by Rez, unknown use... 159 referenceLists.writeBE_uint(resMysteriousID); 160 resMysteriousID += 0x100_0000; 161 } 162 } 163 164 return buf ~ referenceLists; 165 } 166 167 ubyte[] buildResourceNameList() 168 { 169 ubyte[] buf; 170 foreach(ref r; resources) 171 { 172 string name = r.name; 173 174 if (name !is null) 175 { 176 r.nameOffset = cast(ushort)(buf.length); 177 buf.writeBE_ubyte(cast(ubyte)(name.length)); 178 foreach(char ch; name) 179 buf.writeBE_ubyte(cast(ubyte)ch); 180 } 181 else 182 r.nameOffset = 0xffff; 183 } 184 return buf; 185 } 186 187 } 188 189 void writeBE_ubyte(ref ubyte[] buf, ubyte b) 190 { 191 buf ~= b; 192 } 193 194 void writeBE_ushort(ref ubyte[] buf, ushort s) 195 { 196 buf ~= (s >> 8) & 0xff; 197 buf ~= (s >> 0) & 0xff; 198 } 199 200 void writeBE_uint(ref ubyte[] buf, uint u) 201 { 202 buf ~= (u >> 24) & 0xff; 203 buf ~= (u >> 16) & 0xff; 204 buf ~= (u >> 8) & 0xff; 205 buf ~= (u >> 0) & 0xff; 206 } 207 208 ubyte[] makeRSRC_pstring(string s) 209 { 210 import std.stdio; 211 assert(s.length <= 255); 212 ubyte[] buf; 213 buf.writeBE_ubyte(cast(ubyte)(s.length)); 214 foreach(char ch; s) 215 { 216 buf.writeBE_ubyte(cast(ubyte)(ch)); 217 } 218 return buf; 219 } 220 221 ubyte[] makeRSRC_fourCC(char[4] ch) 222 { 223 ubyte[] buf; 224 buf.writeBE_ubyte(ch[0]); 225 buf.writeBE_ubyte(ch[1]); 226 buf.writeBE_ubyte(ch[2]); 227 buf.writeBE_ubyte(ch[3]); 228 return buf; 229 } 230 231 ubyte[] makeRSRC_fourCC_string(string fourcc) 232 { 233 assert(fourcc.length == 4); 234 ubyte[] buf; 235 buf.writeBE_ubyte(fourcc[0]); 236 buf.writeBE_ubyte(fourcc[1]); 237 buf.writeBE_ubyte(fourcc[2]); 238 buf.writeBE_ubyte(fourcc[3]); 239 return buf; 240 } 241 242 ubyte[] makeRSRC_cstring(string s) 243 { 244 ubyte[] buf; 245 foreach(char ch; s) 246 { 247 buf.writeBE_ubyte(cast(ubyte)(ch)); 248 } 249 buf.writeBE_ubyte(0); 250 return buf; 251 }