this repo has no description
1#include "ResourceFile.h"
2#include "BinaryIO.h"
3
4#include <boost/filesystem.hpp>
5#include "boost/filesystem/fstream.hpp"
6#include <sstream>
7#include <iostream>
8
9#ifdef __APPLE__
10#include <sys/xattr.h>
11#endif
12extern "C" {
13#include "hfs.h"
14}
15
16namespace fs = boost::filesystem;
17
18// CRC 16 table lookup array
19static unsigned short CRC16Table[256] =
20 {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
21 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
22 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
23 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
24 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
25 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
26 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
27 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
28 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
29 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
30 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
31 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
32 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
33 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
34 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
35 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
36 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
37 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
38 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
39 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
40 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
41 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
42 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
43 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
44 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
45 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
46 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
47 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
48 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
49 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
50 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
51 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
52
53// CalculateCRC
54static unsigned short CalculateCRC(unsigned short CRC, const char* dataBlock, int dataSize)
55{
56 while (dataSize)
57 {
58 CRC = (CRC << 8) ^ CRC16Table[((*dataBlock) ^ (CRC >> 8)) & 0x00FF];
59 dataBlock++;
60 dataSize--;
61 }
62
63 return CRC;
64}
65
66static void writeMacBinary(std::ostream& out, std::string filename,
67 ResType type, ResType creator,
68 const Resources& rsrc, const std::string& data)
69{
70
71 std::ostringstream resstream;
72 rsrc.writeFork(resstream);
73
74 const std::string& rsrcBytes = resstream.str();
75
76
77 std::ostringstream header;
78 byte(header, 0);
79 byte(header, filename.size());
80 header << filename;
81 while((int)header.tellp() < 65)
82 byte(header,0);
83 ostype(header, type);
84 ostype(header, creator);
85 byte(header, 0); // flags
86 byte(header, 0);
87 word(header, 0); // position.v
88 word(header, 0); // position.h
89 word(header, 0); // folder id
90 byte(header, 0); // protected flag
91 byte(header, 0);
92 longword(header, (int)data.size());
93 longword(header, (int)rsrcBytes.size());
94 longword(header, 0); // creation date
95 longword(header, 0); // modification date
96 while((int)header.tellp() < 124)
97 byte(header,0);
98 std::string headerData = header.str();
99 out << headerData;
100 word(out, CalculateCRC(0, &headerData[0], headerData.size()));
101 word(out, 0);
102 out << data;
103 while((int)out.tellp() % 128)
104 byte(out,0);
105 rsrc.writeFork(out);
106 while((int)out.tellp() % 128)
107 byte(out,0);
108}
109
110bool ResourceFile::read(std::string path, Format f)
111{
112 if(!assign(path, f))
113 return false;
114 return read();
115}
116
117bool ResourceFile::write(std::string path, Format f)
118{
119 if(!assign(path, f))
120 return false;
121 return write();
122}
123
124static bool CheckAppleDouble(fs::path path, std::string prefix)
125{
126 fs::path adPath = path.parent_path() / (prefix + path.filename().string());
127 fs::ifstream in(adPath);
128 if(in)
129 {
130 int magic1 = longword(in);
131
132 if(in && magic1 == 0x00051607)
133 {
134 int magic2 = longword(in);
135 if(in && magic2 == 0x00020000)
136 return true;
137 }
138 }
139 return false;
140}
141
142bool ResourceFile::assign(std::string pathstring, ResourceFile::Format f)
143{
144 this->pathstring = pathstring;
145 fs::path path(pathstring);
146
147 this->filename = path.stem().string();
148 fs::path rsrcPath = path.parent_path() / ".rsrc" / path.filename();
149 fs::path finfPath = path.parent_path() / ".finf" / path.filename();
150
151 format = f;
152 if(format == Format::autodetect)
153 {
154 if(path.extension() == ".bin")
155 format = Format::macbin;
156 else if(path.extension() == ".as")
157 format = Format::applesingle;
158 else if(path.extension() == ".dsk")
159 format = Format::diskimage;
160 else if(path.filename().string().substr(0,2) == "._")
161 {
162 path = path.parent_path() / path.filename().string().substr(2);
163 format = Format::underscore_appledouble;
164 this->pathstring = path.string();
165 }
166 else if(path.filename().string()[0] == '%')
167 {
168 path = path.parent_path() / path.filename().string().substr(1);
169 format = Format::percent_appledouble;
170 this->pathstring = path.string();
171 }
172 //else if(fs::exists(rsrcPath))
173 // format = Format::basilisk;
174 }
175 if(format == Format::autodetect)
176 {
177 if(CheckAppleDouble(path, "._"))
178 format = Format::underscore_appledouble;
179 if(CheckAppleDouble(path, "%"))
180 format = Format::percent_appledouble;
181 }
182 if(format == Format::autodetect)
183 {
184 fs::ifstream in(path);
185 if(in)
186 {
187 int magic1 = longword(in);
188 if(in && magic1 == 0x00051600)
189 {
190 int magic2 = longword(in);
191 if(in && magic2 == 0x00020000)
192 format = Format::applesingle;
193 }
194 }
195 }
196 if(format == Format::autodetect)
197 {
198#ifdef __APPLE__
199 format = Format::real;
200#else
201 format = Format::basilisk;
202#endif
203 }
204// std::cout << "assigned: " << pathstring << " format " << (int)format << "\n";
205 return true;
206}
207
208bool ResourceFile::read(std::istream& in, Format f)
209{
210 switch(f)
211 {
212 case Format::applesingle:
213 {
214 if(longword(in) != 0x00051600)
215 return false;
216 if(longword(in) != 0x00020000)
217 return false;
218 in.seekg(24);
219 int n = word(in);
220 for(int i = 0; i < n; i++)
221 {
222 in.seekg(26 + i * 12);
223 int what = longword(in);
224 int off = longword(in);
225 int len = longword(in);
226 in.seekg(off);
227 switch(what)
228 {
229 case 1:
230 {
231 std::vector<char> buf(len);
232 in.read(buf.data(), len);
233 data = std::string(buf.begin(), buf.end());
234 }
235 break;
236 case 2:
237 resources = Resources(in);
238 break;
239 case 9:
240 type = ostype(in);
241 creator = ostype(in);
242 break;
243 }
244 }
245 }
246 break;
247 case Format::macbin:
248 {
249 if(byte(in) != 0)
250 return false;
251 if(byte(in) > 63)
252 return false;
253 in.seekg(65);
254 type = ostype(in);
255 creator = ostype(in);
256 in.seekg(83);
257 int datasize = longword(in);
258 //int rsrcsize = longword(in);
259
260 in.seekg(0);
261 char header[124];
262 in.read(header, 124);
263 unsigned short crc = CalculateCRC(0,header,124);
264 if(word(in) != crc)
265 return false;
266 in.seekg(128);
267 std::vector<char> buf(datasize);
268 in.read(buf.data(), datasize);
269 data = std::string(buf.begin(), buf.end());
270 datasize = ((int)datasize + 0x7F) & ~0x7F;
271 in.seekg(128 + datasize);
272 resources = Resources(in);
273 }
274 break;
275 default:
276 return false;
277 }
278 return true;
279}
280
281bool ResourceFile::read()
282{
283 fs::path path(pathstring);
284
285 type = creator = 0x3F3F3F3F;
286
287 if(isSingleFork(format))
288 {
289 fs::ifstream in(path);
290 return read(in, format);
291 }
292
293 switch(format)
294 {
295 case Format::basilisk:
296 {
297 fs::ifstream dataIn(path);
298 if(!dataIn)
299 return false;
300 data = std::string(std::istreambuf_iterator<char>(dataIn),
301 std::istreambuf_iterator<char>());
302
303 fs::ifstream rsrcIn(path.parent_path() / ".rsrc" / path.filename());
304 if(rsrcIn)
305 resources = Resources(rsrcIn);
306 fs::ifstream finfIn(path.parent_path() / ".finf" / path.filename());
307 if(finfIn)
308 {
309 type = ostype(finfIn);
310 creator = ostype(finfIn);
311 }
312 }
313 break;
314#ifdef __APPLE__
315 case Format::real:
316 {
317 fs::ifstream dataIn(path);
318 if(!dataIn)
319 return false;
320 data = std::string(std::istreambuf_iterator<char>(dataIn),
321 std::istreambuf_iterator<char>());
322 fs::ifstream rsrcIn(path / "..namedfork" / "rsrc");
323 if(rsrcIn)
324 resources = Resources(rsrcIn);
325
326 char finf[32];
327 int n = getxattr(path.c_str(), XATTR_FINDERINFO_NAME,
328 finf, 32, 0, 0);
329 if(n > 0)
330 {
331 std::istringstream finfIn(std::string(finf, finf+n));
332 type = ostype(finfIn);
333 creator = ostype(finfIn);
334 }
335 }
336 break;
337#endif
338
339 case Format::underscore_appledouble:
340 case Format::percent_appledouble:
341 {
342 fs::ifstream dataIn(path);
343 data = std::string(std::istreambuf_iterator<char>(dataIn),
344 std::istreambuf_iterator<char>());
345
346 std::string prefix = format == Format::underscore_appledouble ?
347 "._" : "%";
348
349 fs::path adPath = path.parent_path() / (prefix + path.filename().string());
350 fs::ifstream in(adPath);
351 if(longword(in) != 0x00051607)
352 return false;
353 if(longword(in) != 0x00020000)
354 return false;
355 in.seekg(24);
356 int n = word(in);
357 for(int i = 0; i < n; i++)
358 {
359 in.seekg(26 + i * 12);
360 int what = longword(in);
361 int off = longword(in);
362 //int len = longword(in);
363 in.seekg(off);
364 switch(what)
365 {
366 case 2:
367 resources = Resources(in);
368 break;
369 case 9:
370 type = ostype(in);
371 creator = ostype(in);
372 break;
373 }
374 }
375 }
376 break;
377 default:
378 return false;
379 }
380 return true;
381}
382
383bool ResourceFile::write(std::ostream& out, Format f)
384{
385 switch(f)
386 {
387 case Format::macbin:
388 {
389 writeMacBinary(out, filename, type, creator, resources, data);
390 }
391 break;
392 case Format::applesingle:
393 {
394 longword(out, 0x00051600);
395 longword(out, 0x00020000);
396 for(int i = 0; i < 16; i++)
397 byte(out, 0);
398 word(out, 3);
399 std::streampos entries = out.tellp();
400 for(int i = 0; i < 3*3; i++)
401 longword(out, 0);
402 std::streampos dataStart = out.tellp();
403 out << data;
404 std::streampos rsrcStart = out.tellp();
405 resources.writeFork(out);
406 std::streampos finfStart = out.tellp();
407 ostype(out, type);
408 ostype(out, creator);
409 for(int i = 8; i < 32; i++)
410 byte(out, 0);
411 out.seekp(entries);
412 longword(out, 1);
413 longword(out, dataStart);
414 longword(out, rsrcStart - dataStart);
415 longword(out, 2);
416 longword(out, rsrcStart);
417 longword(out, finfStart - rsrcStart);
418 longword(out, 9);
419 longword(out, finfStart);
420 longword(out, 32);
421 }
422 break;
423 default:
424 return false;
425 }
426 return true;
427}
428
429bool ResourceFile::write()
430{
431 fs::path path(pathstring);
432
433 if(isSingleFork(format))
434 {
435 fs::ofstream out(path);
436 return write(out, format);
437 }
438
439 switch(format)
440 {
441 case Format::basilisk:
442 {
443 fs::create_directory(path.parent_path() / ".rsrc");
444 fs::create_directory(path.parent_path() / ".finf");
445
446 fs::ofstream dataOut(path);
447 fs::ofstream rsrcOut(path.parent_path() / ".rsrc" / path.filename());
448 fs::ofstream finfOut(path.parent_path() / ".finf" / path.filename());
449
450 dataOut << data;
451 resources.writeFork(rsrcOut);
452
453 ostype(finfOut, type);
454 ostype(finfOut, creator);
455 for(int i = 8; i < 32; i++)
456 byte(finfOut, 0);
457 }
458 break;
459#ifdef __APPLE__
460 case Format::real:
461 {
462 fs::ofstream dataOut(path);
463 fs::ofstream rsrcOut(path / "..namedfork" / "rsrc");
464 std::ostringstream finfOut;
465
466 dataOut << data;
467 resources.writeFork(rsrcOut);
468
469 ostype(finfOut, type);
470 ostype(finfOut, creator);
471 for(int i = 8; i < 32; i++)
472 byte(finfOut, 0);
473 setxattr(path.c_str(), XATTR_FINDERINFO_NAME,
474 finfOut.str().data(), 32, 0, 0);
475 }
476 break;
477#endif
478
479 case Format::underscore_appledouble:
480 case Format::percent_appledouble:
481 {
482 fs::ofstream dataOut(path);
483
484 dataOut << data;
485
486 std::string prefix = format == Format::underscore_appledouble ?
487 "._" : "%";
488
489 fs::path adPath = path.parent_path() / (prefix + path.filename().string());
490
491 fs::ofstream out(adPath);
492
493 longword(out, 0x00051607);
494 longword(out, 0x00020000);
495
496 for(int i = 0; i < 16; i++)
497 byte(out, 0);
498 word(out, 2);
499 std::streampos entries = out.tellp();
500 for(int i = 0; i < 2*3; i++)
501 longword(out, 0);
502 std::streampos rsrcStart = out.tellp();
503 resources.writeFork(out);
504 std::streampos finfStart = out.tellp();
505 ostype(out, type);
506 ostype(out, creator);
507 for(int i = 8; i < 32; i++)
508 byte(out, 0);
509 out.seekp(entries);
510 longword(out, 2);
511 longword(out, rsrcStart);
512 longword(out, finfStart - rsrcStart);
513 longword(out, 9);
514 longword(out, finfStart);
515 longword(out, 32);
516 }
517 break;
518#ifndef DARLING
519 case Format::diskimage:
520 {
521 std::ostringstream rsrcOut;
522 resources.writeFork(rsrcOut);
523 std::string rsrc = rsrcOut.str();
524 int size = rsrc.size();
525
526 size += 20 * 1024;
527 size += 800*1024 - size % (800*1024);
528
529 fs::ofstream(path, std::ios::binary | std::ios::trunc).seekp(size-1).put(0);
530
531 hfs_format(pathstring.c_str(), 0, 0, path.stem().string().substr(0,27).c_str(), 0, NULL);
532 hfsvol *vol = hfs_mount(pathstring.c_str(), 0, HFS_MODE_RDWR);
533 if(!vol)
534 return false;
535 //hfs_setvol(vol, )
536 hfsfile *file = hfs_create(vol, (path.stem().string().substr(0,31)).c_str(),
537 ((std::string)type).c_str(), ((std::string)creator).c_str());
538 hfs_setfork(file, 0);
539 hfs_write(file, data.data(), data.size());
540 hfs_setfork(file, 1);
541 hfs_write(file, rsrc.data(), rsrc.size());
542
543 hfs_close(file);
544 hfs_umount(vol);
545
546 }
547 break;
548#endif
549 default:
550 return false;
551 }
552 return true;
553}
554
555bool ResourceFile::hasPlainDataFork(ResourceFile::Format f)
556{
557 switch(f)
558 {
559#ifdef __APPLE__
560 case Format::real:
561#endif
562 case Format::basilisk:
563 case Format::underscore_appledouble:
564 case Format::percent_appledouble:
565 return true;
566 default:
567 return false;
568 }
569}
570
571bool ResourceFile::hasPlainDataFork()
572{
573 return hasPlainDataFork(format);
574}
575
576bool ResourceFile::isSingleFork(Format f)
577{
578 switch(f)
579 {
580 case Format::macbin:
581 case Format::applesingle:
582 return true;
583 default:
584 return false;
585 }
586}