1 /*
2  *  A Z-Machine
3  *  Copyright (C) 2000 Andrew Hunter
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 /*
21  * Deal with files
22  */
23 
24 #include "../config.h"
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <sys/stat.h>
31 
32 #include "file.h"
33 #include "zmachine.h"
34 
35 #if WINDOW_SYSTEM != 2 && WINDOW_SYSTEM != 3 && WINDOW_SYSTEM != 4
36 
37 struct ZFile
38 {
39   FILE* handle;
40 };
41 
open_file(char * filename)42 ZFile* open_file(char* filename)
43 {
44   ZFile* res;
45 
46   res = malloc(sizeof(ZFile));
47   res->handle = fopen(filename, "r");
48 
49   if (res->handle == NULL)
50     {
51       free(res);
52       return NULL;
53     }
54 
55   return res;
56 }
57 
open_file_write(char * filename)58 ZFile* open_file_write(char* filename)
59 {
60   ZFile* res;
61 
62   res = malloc(sizeof(ZFile));
63   res->handle = fopen(filename, "w");
64 
65   if (res->handle == NULL)
66     {
67       free(res);
68       return NULL;
69     }
70 
71   return res;
72 }
73 
close_file(ZFile * file)74 void   close_file(ZFile* file)
75 {
76   fclose(file->handle);
77   free(file);
78 }
79 
read_page(ZFile * file,int page_no)80 ZByte* read_page(ZFile* file, int page_no)
81 {
82   ZByte* page;
83 
84   page = malloc(4096);
85   if (page == NULL)
86     return NULL;
87 
88   fseek(file->handle, 4096*page_no, SEEK_SET);
89   fread(page, 4096, 1, file->handle);
90 
91   return page;
92 }
93 
read_block(ZFile * file,int start_pos,int end_pos)94 ZByte* read_block(ZFile* file, int start_pos, int end_pos)
95 {
96   ZByte* block;
97   size_t rd;
98 
99   block = malloc(end_pos-start_pos);
100   if (block == NULL)
101     return NULL;
102 
103   if (fseek(file->handle, start_pos, SEEK_SET))
104     zmachine_fatal("Failed to seek to position %i", start_pos);
105   rd = fread(block, 1, end_pos-start_pos, file->handle);
106   if (rd != end_pos-start_pos)
107     zmachine_fatal("Tried to read %i items of 1 byte, got %i items",
108 		   end_pos-start_pos, rd);
109 
110   return block;
111 }
112 
read_byte(ZFile * file)113 ZByte inline read_byte(ZFile* file)
114 {
115   return fgetc(file->handle);
116 }
117 
read_word(ZFile * file)118 ZUWord read_word(ZFile* file)
119 {
120   return (read_byte(file)<<8)|read_byte(file);
121 }
122 
read_rword(ZFile * file)123 ZUWord read_rword(ZFile* file)
124 {
125   return read_byte(file)|(read_byte(file)<<8);
126 }
127 
read_block2(ZByte * block,ZFile * file,int start_pos,int end_pos)128 void read_block2(ZByte* block, ZFile* file, int start_pos, int end_pos)
129 {
130   fseek(file->handle, start_pos, SEEK_SET);
131   fread(block, end_pos-start_pos, 1, file->handle);
132 }
133 
get_file_size(char * filename)134 ZDWord get_file_size(char* filename)
135 {
136   struct stat buf;
137 
138   if (stat(filename, &buf) != 0)
139     {
140       return -1;
141     }
142 
143   return buf.st_size;
144 }
145 
end_of_file(ZFile * file)146 int end_of_file(ZFile* file)
147 {
148   return feof(file->handle)!=0;
149 }
150 
write_block(ZFile * file,ZByte * block,int length)151 void write_block(ZFile* file, ZByte* block, int length)
152 {
153   fwrite(block, 1, length, file->handle);
154 }
155 
write_byte(ZFile * file,ZByte byte)156 inline void write_byte(ZFile* file, ZByte byte)
157 {
158   fputc(byte, file->handle);
159 }
160 
write_word(ZFile * file,ZWord word)161 void write_word(ZFile* file, ZWord word)
162 {
163   write_byte(file, word>>8);
164   write_byte(file, word);
165 }
166 
write_dword(ZFile * file,ZDWord word)167 void write_dword(ZFile* file, ZDWord word)
168 {
169   write_byte(file, word>>24);
170   write_byte(file, word>>16);
171   write_byte(file, word>>8);
172   write_byte(file, word);
173 }
174 
175 #elif WINDOW_SYSTEM == 2
176 
177 #include <windows.h>
178 
179 struct ZFile
180 {
181   HANDLE file;
182 };
183 
open_file(char * filename)184 ZFile* open_file(char* filename)
185 {
186   ZFile* f;
187 
188   f = malloc(sizeof(ZFile));
189 
190   f->file = CreateFile(filename,
191 		       GENERIC_READ,
192 		       FILE_SHARE_READ,
193 		       NULL,
194 		       OPEN_EXISTING,
195 		       FILE_ATTRIBUTE_NORMAL,
196 		       NULL);
197 
198   if (f->file == INVALID_HANDLE_VALUE)
199     {
200       free(f);
201       return NULL;
202     }
203 
204   return f;
205 }
206 
open_file_write(char * filename)207 ZFile* open_file_write(char* filename)
208 {
209   ZFile* f;
210 
211   f = malloc(sizeof(ZFile));
212 
213   f->file = CreateFile(filename,
214 		       GENERIC_READ|GENERIC_WRITE,
215 		       FILE_SHARE_READ|FILE_SHARE_WRITE,
216 		       NULL,
217 		       CREATE_ALWAYS,
218 		       FILE_ATTRIBUTE_NORMAL,
219 		       NULL);
220 
221   if (f->file == INVALID_HANDLE_VALUE)
222     {
223       free(f);
224       return NULL;
225     }
226 
227   return f;
228 }
229 
close_file(ZFile * file)230 void close_file(ZFile* file)
231 {
232   CloseHandle(file->file);
233   free(file);
234 }
235 
read_byte(ZFile * file)236 ZByte read_byte(ZFile* file)
237 {
238   ZByte block[1];
239   DWORD nread;
240 
241   if (!ReadFile(file->file, block, 1, &nread, NULL))
242     zmachine_fatal("Unable to read byte from file");
243   return block[0];
244 }
245 
read_word(ZFile * file)246 ZUWord read_word(ZFile* file)
247 {
248   return (read_byte(file)<<8)|read_byte(file);
249 }
250 
read_rword(ZFile * file)251 ZUWord read_rword(ZFile* file)
252 {
253   return read_byte(file)|(read_byte(file)<<8);
254 }
255 
read_block(ZFile * file,int start_pos,int end_pos)256 ZByte* read_block(ZFile* file,
257 		  int start_pos,
258 		  int end_pos)
259 {
260   ZByte* block;
261   DWORD  nread;
262 
263   block = malloc(sizeof(ZByte)*(end_pos-start_pos));
264 
265   if (SetFilePointer(file->file, start_pos, NULL, FILE_BEGIN) == -1)
266     {
267       zmachine_fatal("Unable to seek to %i", start_pos);
268       free(block);
269       return NULL;
270     }
271   if (!ReadFile(file->file, block, end_pos-start_pos, &nread, NULL))
272     {
273       zmachine_fatal("Unable to read %i bytes", end_pos-start_pos);
274       free(block);
275       return NULL;
276     }
277 
278   if (nread != end_pos-start_pos)
279     {
280       zmachine_fatal("Tried to read %i bytes, but only got %i",
281 		     end_pos-start_pos, nread);
282       free(block);
283       return NULL;
284     }
285 
286   return block;
287 }
288 
read_block2(ZByte * block,ZFile * file,int start_pos,int end_pos)289 void read_block2(ZByte* block,
290 		 ZFile* file,
291 		 int start_pos,
292 		 int end_pos)
293 {
294   DWORD  nread;
295 
296   if (SetFilePointer(file->file, start_pos, NULL, FILE_BEGIN) == -1)
297     zmachine_fatal("Unable to seek");
298   if (!ReadFile(file->file, block, end_pos-start_pos, &nread, NULL))
299     zmachine_fatal("Unable to read file");
300 
301   if (nread != end_pos-start_pos)
302     zmachine_fatal("Tried to read %i bytes, but only got %i",
303 		   end_pos-start_pos, nread);
304 }
305 
write_block(ZFile * file,ZByte * block,int length)306 void write_block(ZFile* file, ZByte* block, int length)
307 {
308   DWORD nwrite;
309 
310   WriteFile(file->file, block, length, &nwrite, NULL);
311 }
312 
write_byte(ZFile * file,ZByte byte)313 void write_byte(ZFile* file, ZByte byte)
314 {
315   write_block(file, &byte, 1);
316 }
317 
write_word(ZFile * file,ZWord word)318 void write_word(ZFile* file, ZWord word)
319 {
320   write_byte(file, word>>8);
321   write_byte(file, word);
322 }
323 
write_dword(ZFile * file,ZDWord word)324 void write_dword(ZFile* file, ZDWord word)
325 {
326   write_byte(file, word>>24);
327   write_byte(file, word>>16);
328   write_byte(file, word>>8);
329   write_byte(file, word);
330 }
331 
get_file_size(char * filename)332 ZDWord get_file_size(char* filename)
333 {
334   HANDLE hnd;
335   ZDWord sz;
336 
337   hnd = CreateFile(filename,
338 		   GENERIC_READ,
339 		   FILE_SHARE_READ,
340 		   NULL,
341 		   OPEN_EXISTING,
342 		   FILE_ATTRIBUTE_NORMAL,
343 		   NULL);
344 
345   if (hnd == INVALID_HANDLE_VALUE)
346     return -1;
347 
348   sz = GetFileSize(hnd, NULL);
349 
350   CloseHandle(hnd);
351 
352   return sz;
353 }
354 
355 /* end_of_file not implemented: implement to fix the Windows port */
356 
357 #elif WINDOW_SYSTEM == 3
358 
359 /* Mac OS file handling functions */
360 
361 #include <Carbon/Carbon.h>
362 #include "carbondisplay.h"
363 
364 /*
365  * We add a couple of functions to deal with opening files straight from
366  * FSRefs
367  */
368 
369 struct ZFile
370 {
371   FSRef  fileref;
372   SInt16 forkref;
373   int    endOfFile;
374 };
375 
file_error_text(OSStatus stat)376 static char* file_error_text(OSStatus stat)
377 {
378   switch (stat)
379     {
380     case notOpenErr:
381       return "Volume not found";
382     case dirFulErr:
383       return "Directory full";
384     case dskFulErr:
385       return "Disk full";
386     case nsvErr:
387       return "Volume not found";
388     case ioErr:
389       return "I/O error";
390     case bdNamErr:
391       return "Bad filename";
392     case fnOpnErr:
393       return "File not open";
394     case eofErr:
395       return "End of file";
396     case posErr:
397       return "Bad file position";
398     case tmfoErr:
399       return "Too many files open";
400     case fnfErr:
401       return "File not found";
402     case wPrErr:
403     case vLckdErr:
404       return "Volume locked";
405     case fLckdErr:
406       return "File locked";
407     case fBsyErr:
408       return "File busy";
409     case rfNumErr:
410       return "Invalid reference number";
411     default:
412       {
413 	static char str[255];
414 
415 	sprintf(str, "Unknown reason code - %i", (int) stat);
416 	return str;
417       }
418     }
419 }
420 
open_file(char * filename)421 ZFile* open_file(char* filename)
422 {
423   FSRef ref;
424 
425   FSPathMakeRef(filename, &ref, NULL);
426 
427   return open_file_fsref(&ref);
428 }
429 
open_file_write(char * filename)430 ZFile* open_file_write(char* filename)
431 {
432   FSRef    ref;
433   FSSpec   spec;
434   OSStatus erm;
435   FInfo    inf;
436   int      x;
437 
438   char*    dirname;
439   UniChar* uniname;
440   int      lastslash = -1;
441   FSRef    parent;
442 
443   erm = FSPathMakeRef(filename, &ref, NULL);
444 
445   if (erm != fnfErr)
446     {
447       erm = FSDeleteObject(&ref);
448       if (erm != noErr)
449 	return NULL;
450     }
451 
452   dirname = malloc(strlen(filename)+1);
453   uniname = malloc((strlen(filename)+1)*sizeof(int));
454   strcpy(dirname, filename);
455   for (x=0; filename[x] != 0; x++)
456     {
457       uniname[x] = filename[x];
458       if (filename[x] == '/')
459 	lastslash = x;
460     }
461   uniname[x] = 0;
462 
463   if (lastslash == -1)
464     {
465       free(dirname);
466       free(uniname);
467       return NULL;
468     }
469   dirname[lastslash] = 0;
470 
471   erm = FSPathMakeRef(dirname, &parent, NULL);
472   if (erm != NULL)
473     {
474       free(dirname);
475       free(uniname);
476       return NULL;
477     }
478 
479   erm = FSCreateFileUnicode(&parent, strlen(filename) - lastslash-1,
480 			    uniname + lastslash + 1,
481 			    kFSCatInfoNone, NULL, &ref, &spec);
482 
483   if (erm != noErr)
484       {
485 	free(dirname);
486 	free(uniname);
487 	return NULL;
488       }
489 
490   free(dirname);
491   free(uniname);
492 
493   FSpGetFInfo(&spec, &inf);
494 
495   inf.fdType    = 'BINA';
496   inf.fdCreator = SIGNATURE;
497 
498   FSpSetFInfo(&spec, &inf);
499 
500   return open_file_write_fsref(&ref);
501 }
502 
open_file_fsref(FSRef * ref)503 ZFile* open_file_fsref(FSRef* ref)
504 {
505   HFSUniStr255 dfork;
506   ZFile *file;
507   SInt16 refnum;
508   OSErr erm;
509 
510   FSGetDataForkName(&dfork);
511 
512   erm = FSOpenFork(ref, dfork.length, dfork.unicode, fsRdPerm, &refnum);
513 
514   if (erm != noErr)
515     return NULL;
516 
517   file = malloc(sizeof(ZFile));
518   file->fileref = *ref;
519   file->forkref = refnum;
520   file->endOfFile = 0;
521 
522   return file;
523 }
524 
open_file_write_fsref(FSRef * ref)525 ZFile* open_file_write_fsref(FSRef* ref)
526 {
527   HFSUniStr255 dfork;
528   ZFile *file;
529   SInt16 refnum;
530   OSErr erm;
531 
532   FSGetDataForkName(&dfork);
533 
534   erm = FSOpenFork(ref, dfork.length, dfork.unicode, fsWrPerm, &refnum);
535 
536   if (erm != noErr)
537     return NULL;
538 
539   file = malloc(sizeof(ZFile));
540   file->fileref = *ref;
541   file->forkref = refnum;
542   file->endOfFile = 0;
543 
544   return file;
545 }
546 
get_file_fsref(ZFile * file)547 FSRef get_file_fsref(ZFile* file)
548 {
549   return file->fileref;
550 }
551 
close_file(ZFile * file)552 void   close_file(ZFile* file)
553 {
554   FSCloseFork(file->forkref);
555   free(file);
556 }
557 
read_page(ZFile * file,int page_no)558 ZByte* read_page(ZFile* file, int page_no)
559 {
560   return read_block(file, 4096*page_no, 4096*page_no+4096);
561 }
562 
read_block(ZFile * file,int start_pos,int end_pos)563 ZByte* read_block(ZFile* file, int start_pos, int end_pos)
564 {
565   ZByte* block;
566   OSStatus erm;
567   ByteCount rd;
568 
569   block = malloc(end_pos-start_pos);
570   if (block == NULL)
571     return NULL;
572 
573   erm = FSReadFork(file->forkref, fsFromStart, start_pos,
574 		   end_pos-start_pos, block, &rd);
575   if (erm != noErr)
576     zmachine_fatal("Error while reading from file - %s", file_error_text(erm));
577   if (erm == eofErr) endOfFile = 1;
578   if (rd != end_pos-start_pos)
579     zmachine_fatal("Tried to read %i items of 1 byte, got %i items",
580 		   end_pos-start_pos, rd);
581 
582   return block;
583 }
584 
read_byte(ZFile * file)585 ZByte inline read_byte(ZFile* file)
586 {
587   char byte;
588   OSErr res;
589 
590   res = FSReadFork(file->forkref, fsAtMark, 0, 1, &byte, NULL);
591   if (res == eofErr) file->endOfFile = 1;
592   return byte;
593 }
594 
read_word(ZFile * file)595 ZUWord read_word(ZFile* file)
596 {
597   return (read_byte(file)<<8)|read_byte(file);
598 }
599 
read_rword(ZFile * file)600 ZUWord read_rword(ZFile* file)
601 {
602   return read_byte(file)|(read_byte(file)<<8);
603 }
604 
read_block2(ZByte * block,ZFile * file,int start_pos,int end_pos)605 void read_block2(ZByte* block, ZFile* file, int start_pos, int end_pos)
606 {
607   OSErr erm;
608 
609   erm = FSReadFork(file->forkref, fsFromStart, start_pos,
610 		  end_pos-start_pos, block, NULL);
611   if (erm == eofErr) file->endOfFile = 1;
612 }
613 
get_file_size(char * filename)614 ZDWord get_file_size(char* filename)
615 {
616   FSRef ref;
617   OSStatus res;
618 
619   res = FSPathMakeRef(filename, &ref, NULL);
620 
621   if (res != noErr)
622     return -1;
623 
624   return get_file_size_fsref(&ref);
625 }
626 
end_of_file(ZFile * file)627 int end_of_file(ZFile* file)
628 {
629   return file->endOfFile;
630 }
631 
get_file_size_fsref(FSRef * file)632 ZDWord get_file_size_fsref(FSRef* file)
633 {
634   FSCatalogInfo inf;
635 
636   FSGetCatalogInfo(file, kFSCatInfoDataSizes, &inf, NULL, NULL, NULL);
637 
638   return inf.dataLogicalSize;
639 }
640 
write_block(ZFile * file,ZByte * block,int length)641 void write_block(ZFile* file, ZByte* block, int length)
642 {
643   FSWriteFork(file->forkref, fsAtMark, 0, length, block, NULL);
644 }
645 
write_byte(ZFile * file,ZByte byte)646 inline void write_byte(ZFile* file, ZByte byte)
647 {
648    FSWriteFork(file->forkref, fsAtMark, 0, 1, &byte, NULL);
649 }
650 
write_word(ZFile * file,ZWord word)651 void write_word(ZFile* file, ZWord word)
652 {
653   write_byte(file, word>>8);
654   write_byte(file, word);
655 }
656 
write_dword(ZFile * file,ZDWord word)657 void write_dword(ZFile* file, ZDWord word)
658 {
659   write_byte(file, word>>24);
660   write_byte(file, word>>16);
661   write_byte(file, word>>8);
662   write_byte(file, word);
663 }
664 
665 #endif
666 
write_string(ZFile * file,const char * string)667 void write_string(ZFile* file, const char* string) {
668   write_block(file, (unsigned char*)string, strlen(string));
669 }
670 
write_stringf(ZFile * file,const char * format,...)671 void write_stringf(ZFile* file, const char* format, ...) {
672   char buffer[4096];
673   va_list ap;
674 
675   va_start(ap, format);
676   vsnprintf(buffer, 4096, format, ap);
677   buffer[4095] = 0;
678   va_end(ap);
679 
680   write_string(file, buffer);
681 }
682 
write_stringu(ZFile * file,const int * string)683 void write_stringu(ZFile* file, const int* string) {
684   /* Maybe FIXME: write in UTF-8 format? */
685   int len, x;
686   char* str;
687 
688   for (len=0; string[len] != 0; len++);
689 
690   str = malloc(len+1);
691 
692   for (x=0; x<len; x++) {
693     str[x] = string[x]<128?string[x]:'?';
694   }
695   str[x] = 0;
696 
697   write_string(file, str);
698 
699   free(str);
700 }
701