Fixed audio playing
[kvidha.git] / fileio.c
1 #include <stdio.h>\r
2 #include <string.h>\r
3 #include <time.h>\r
4 \r
5 #define DAT_START 7\r
6 \r
7 typedef struct KvidFileType\r
8 {\r
9     FILE *FilePtr;\r
10     unsigned char AtEOF;\r
11     struct KvidFileType *Parent;\r
12     unsigned char DataFile;\r
13     unsigned char ReSeek;\r
14     unsigned char CanWrite;\r
15     unsigned long CurFAT;\r
16     unsigned long CurAU;\r
17     unsigned short Offset;\r
18     unsigned short AUSize;\r
19     unsigned char LastOp;\r
20     unsigned char CurBit, CurChar;\r
21 } KvidFile;\r
22 \r
23 KvidFile *MainDat;\r
24 \r
25 static unsigned long GetFAT(KvidFile *FileStr, unsigned long AU)\r
26 {\r
27     unsigned long CurFAT, i, TargetFAT;\r
28 \r
29     CurFAT = 0;\r
30     TargetFAT = AU / ((FileStr->AUSize >> 2) - 1);\r
31     for(i = 0; i < TargetFAT; i++)\r
32     {\r
33         fseek(FileStr->FilePtr, DAT_START + (CurFAT * FileStr->AUSize), SEEK_SET);\r
34         fread((void *)&CurFAT, 4, 1, FileStr->FilePtr);\r
35     }\r
36     return(CurFAT);\r
37 }\r
38 \r
39 static void SetFATEntry(KvidFile *FileStr, unsigned long AU, unsigned long Entry)\r
40 {\r
41     unsigned long CurFAT, i, TargetFAT;\r
42 \r
43     CurFAT = 0;\r
44     TargetFAT = AU / ((FileStr->AUSize >> 2) - 1);\r
45     for(i = 0; i < TargetFAT; i++)\r
46     {\r
47         fseek(FileStr->FilePtr, DAT_START + (CurFAT * FileStr->AUSize), SEEK_SET);\r
48         fread((void *)&CurFAT, 4, 1, FileStr->FilePtr);\r
49         if(CurFAT >= 0x80000000)\r
50                 return;\r
51     }\r
52     fseek(FileStr->FilePtr, DAT_START + (CurFAT * FileStr->AUSize) + (((AU % ((FileStr->AUSize >> 2) - 1)) + 1) << 2), SEEK_SET);\r
53     fwrite((void *)&Entry, 4, 1, FileStr->FilePtr);\r
54 }\r
55 \r
56 static unsigned long FindFreeAU(KvidFile *FileStr, unsigned char InhibitCreation)\r
57 {\r
58         unsigned long OldFAT, CurFAT, NumEntries, Entry, i, FATNum;\r
59 \r
60     CurFAT = 0;\r
61     FATNum = 0;\r
62     NumEntries = (FileStr->AUSize >> 2) - 1;\r
63     while(CurFAT < 0x80000000)\r
64     {\r
65         OldFAT = CurFAT;\r
66         fseek(FileStr->FilePtr, DAT_START + (CurFAT * FileStr->AUSize), SEEK_SET);\r
67         fread((void *)&CurFAT, 4, 1, FileStr->FilePtr);\r
68         for(i = 0; i < NumEntries; i++)\r
69         {\r
70                 fread((void *)&Entry, 4, 1, FileStr->FilePtr);\r
71             if(Entry == 0)\r
72                 return((FATNum * NumEntries) + i);\r
73         }\r
74         FATNum++;\r
75     }\r
76     if(InhibitCreation == 1)\r
77         return(0xFFFFFFFF);\r
78     fseek(FileStr->FilePtr, DAT_START + (OldFAT * FileStr->AUSize), SEEK_SET);\r
79     CurFAT = NumEntries * FATNum;\r
80         fwrite((void *)&CurFAT, 4, 1, FileStr->FilePtr);\r
81     fseek(FileStr->FilePtr, DAT_START + (CurFAT * FileStr->AUSize), SEEK_SET);\r
82     Entry = 0xFFFFFFFF;\r
83     fwrite((void *)&Entry, 4, 1, FileStr->FilePtr);\r
84     Entry = 0;\r
85     for(i = 0; i < NumEntries; i++)\r
86     {\r
87         fwrite((void *)&Entry, 4, 1, FileStr->FilePtr);\r
88     }\r
89     SetFATEntry(FileStr, CurFAT, 0xFFFFFFFE);\r
90     return(FindFreeAU(FileStr, 1));\r
91 }\r
92 \r
93 static unsigned char GetDatChar(KvidFile *FileStr)\r
94 {\r
95     unsigned char RetVal;\r
96     unsigned short OldAU;\r
97     unsigned short NumEntries;\r
98     KvidFile *CurFile;\r
99 \r
100     for(CurFile = FileStr->Parent; CurFile != NULL; CurFile = CurFile->Parent)\r
101         CurFile->ReSeek = 1;\r
102     if(FileStr->ReSeek == 1)\r
103     {\r
104         fseek(FileStr->FilePtr, DAT_START + (FileStr->CurAU * FileStr->AUSize) + FileStr->Offset, SEEK_SET);\r
105         FileStr->ReSeek = 0;\r
106         FileStr->CurFAT = 0xFFFFFFFF;\r
107     }\r
108     RetVal = (unsigned char)fgetc(FileStr->FilePtr);\r
109     if(FileStr->Offset++ == FileStr->AUSize - 1)\r
110     {\r
111         if(FileStr->CurFAT == 0xFFFFFFFF)\r
112             FileStr->CurFAT = GetFAT(FileStr, FileStr->CurAU);\r
113         OldAU = FileStr->CurAU;\r
114         fseek(FileStr->FilePtr, DAT_START + (FileStr->CurFAT * FileStr->AUSize) + (((FileStr->CurAU % ((FileStr->AUSize >> 2) - 1)) + 1) << 2), SEEK_SET);\r
115         fread((void *)&FileStr->CurAU, 4, 1, FileStr->FilePtr);\r
116         if(FileStr->CurAU == 0xFFFFFFFF)\r
117             FileStr->AtEOF = 1;\r
118         FileStr->Offset = 0;\r
119         NumEntries = (FileStr->AUSize >> 2) - 1;\r
120         if((FileStr->CurAU / NumEntries) == (OldAU / NumEntries))\r
121         {\r
122             fseek(FileStr->FilePtr, DAT_START + (FileStr->CurAU * FileStr->AUSize) + FileStr->Offset, SEEK_SET);\r
123         } else {\r
124             FileStr->ReSeek = 1;\r
125         }\r
126     }\r
127     return(RetVal);\r
128 }\r
129 \r
130 static void PutDatChar(unsigned char c, KvidFile *FileStr)\r
131 {\r
132     unsigned char RetVal;\r
133     unsigned long OldAU;\r
134     KvidFile *CurFile;\r
135 \r
136     for(CurFile = FileStr->Parent; CurFile != NULL; CurFile = CurFile->Parent)\r
137         CurFile->ReSeek = 1;\r
138     if(FileStr->ReSeek == 1)\r
139     {\r
140         fseek(FileStr->FilePtr, DAT_START + (FileStr->CurAU * FileStr->AUSize) + FileStr->Offset, SEEK_SET);\r
141         FileStr->ReSeek = 0;\r
142         FileStr->CurFAT = 0xFFFFFFFF;\r
143     }\r
144     fputc(c, FileStr->FilePtr);\r
145     if(FileStr->Offset++ == FileStr->AUSize - 1)\r
146     {\r
147         if(FileStr->CurFAT == 0xFFFFFFFF)\r
148             FileStr->CurFAT = GetFAT(FileStr, FileStr->CurAU);\r
149         fseek(FileStr->FilePtr, DAT_START + (FileStr->CurFAT * FileStr->AUSize) + (((FileStr->CurAU % ((FileStr->AUSize >> 2) - 1)) + 1) << 2), SEEK_SET);\r
150         OldAU = FileStr->CurAU;\r
151         fread((void *)&FileStr->CurAU, 4, 1, FileStr->FilePtr);\r
152         if(FileStr->CurAU == 0xFFFFFFFF)\r
153         {\r
154                 FileStr->CurAU = FindFreeAU(FileStr, 0);\r
155             SetFATEntry(FileStr, OldAU, FileStr->CurAU);\r
156             SetFATEntry(FileStr, FileStr->CurAU, 0xFFFFFFFF);\r
157         }\r
158         FileStr->Offset = 0;\r
159         FileStr->ReSeek = 1;\r
160     }\r
161 }\r
162 \r
163 unsigned char FileGetChar(KvidFile *FileStr)\r
164 {\r
165     signed long RetVal;\r
166 \r
167     if(FileStr->AtEOF == 1)\r
168         return(0);\r
169     if(FileStr->LastOp == 1)\r
170     {\r
171         FileStr->LastOp = 0;\r
172         fflush(FileStr->FilePtr);\r
173     }\r
174     if(FileStr->Parent != NULL)\r
175         return(GetDatChar(FileStr));\r
176     if((FileStr->Parent == NULL) && (FileStr->DataFile == 0))\r
177     {\r
178         if((RetVal = (signed long)fgetc(FileStr->FilePtr)) == EOF)\r
179         {\r
180             FileStr->AtEOF = 1;\r
181             return(0);\r
182         } else {\r
183             return((unsigned char)RetVal);\r
184         }\r
185     }\r
186     if((FileStr->Parent == NULL) && (FileStr->DataFile == 1))\r
187         return(GetDatChar(FileStr));\r
188 }\r
189 \r
190 void FilePutChar(unsigned char c, KvidFile *FileStr)\r
191 {\r
192     if(FileStr->CanWrite == 0)\r
193         return;\r
194         FileStr->LastOp = 1;\r
195     if(FileStr->Parent != NULL)\r
196         PutDatChar(c, FileStr);\r
197     if((FileStr->Parent == NULL) && (FileStr->DataFile == 0))\r
198         fputc(c, FileStr->FilePtr);\r
199     if((FileStr->Parent == NULL) && (FileStr->DataFile == 1))\r
200         PutDatChar(c, FileStr);\r
201 }\r
202 \r
203 void FileSkip(KvidFile *FileStr, unsigned long NumBytes)\r
204 {\r
205         KvidFile *CurFile;\r
206 \r
207     if(FileStr->AtEOF == 1)\r
208         return;\r
209         if((FileStr->Parent == NULL) && (FileStr->DataFile == 0))\r
210     {\r
211         fseek(FileStr->FilePtr, NumBytes, SEEK_CUR);\r
212         return;\r
213     }\r
214     for(CurFile = FileStr->Parent; CurFile != NULL; CurFile = CurFile->Parent)\r
215         CurFile->ReSeek = 1;\r
216     if(FileStr->ReSeek == 1)\r
217         FileStr->CurFAT = 0xFFFFFFFF;\r
218     while((NumBytes > 0) && (FileStr->AtEOF == 0))\r
219     {\r
220         if(NumBytes < FileStr->AUSize - FileStr->Offset)\r
221             {\r
222                 if(FileStr->ReSeek == 0)\r
223                 fseek(FileStr->FilePtr, NumBytes, SEEK_CUR);\r
224             FileStr->Offset += NumBytes;\r
225             NumBytes = 0;\r
226         } else {\r
227             NumBytes -= FileStr->AUSize - FileStr->Offset;\r
228                 if(FileStr->CurFAT == 0xFFFFFFFF)\r
229                     FileStr->CurFAT = GetFAT(FileStr, FileStr->CurAU);\r
230                 fseek(FileStr->FilePtr, DAT_START + (FileStr->CurFAT * FileStr->AUSize) + (((FileStr->CurAU % ((FileStr->AUSize >> 2) - 1)) + 1) << 2), SEEK_SET);\r
231                 fread((void *)&FileStr->CurAU, 4, 1, FileStr->FilePtr);\r
232                 if(FileStr->CurAU == 0xFFFFFFFF)\r
233                     FileStr->AtEOF = 1;\r
234                 FileStr->Offset = 0;\r
235                 FileStr->ReSeek = 1;\r
236             }\r
237     }\r
238     if(FileStr->ReSeek == 1)\r
239     {\r
240         fseek(FileStr->FilePtr, DAT_START + (FileStr->CurAU * FileStr->AUSize) + FileStr->Offset, SEEK_SET);\r
241         FileStr->ReSeek = 0;\r
242         FileStr->CurFAT = 0xFFFFFFFF;\r
243     }\r
244 }\r
245 \r
246 unsigned long FileReadBits(unsigned char NumBits, KvidFile *FileStr)\r
247 {\r
248     unsigned char i;\r
249     unsigned long Value;\r
250 \r
251     Value = 0;\r
252     for(i = NumBits; i > 0; i--)\r
253     {\r
254         if(FileStr->CurBit > 7)\r
255         {\r
256             FileStr->CurChar = fgetc(FileStr->FilePtr);\r
257             FileStr->CurBit = 7;\r
258         }\r
259         if((FileStr->CurChar & (1 << FileStr->CurBit)) != 0)\r
260             Value += 1 << (i - 1);\r
261         FileStr->CurBit--;\r
262     }\r
263     return(Value);\r
264 }\r
265 \r
266 void FileRead(void *Buffer, unsigned long NumBytes, KvidFile *FileStr)\r
267 {\r
268         if((FileStr->Parent == NULL) && (FileStr->DataFile == 0))\r
269     {\r
270         fread(Buffer, NumBytes, 1, FileStr->FilePtr);\r
271         return;\r
272     }\r
273     while(NumBytes-- > 0)\r
274         *(unsigned char *)Buffer++ = FileGetChar(FileStr);\r
275 }\r
276 \r
277 void FileWrite(void *Buffer, unsigned long NumBytes, KvidFile *FileStr)\r
278 {\r
279         if(FileStr->CanWrite == 0)\r
280         return;\r
281         if((FileStr->Parent == NULL) && (FileStr->DataFile == 0))\r
282     {\r
283         fwrite(Buffer, NumBytes, 1, FileStr->FilePtr);\r
284         return;\r
285     }\r
286     while(NumBytes-- > 0)\r
287         FilePutChar(*(unsigned char *)Buffer++, FileStr);\r
288 }\r
289 \r
290 void FileClose(KvidFile *FileStr)\r
291 {\r
292         if(FileStr->Parent == NULL)\r
293         fclose(FileStr->FilePtr);\r
294 }\r
295 \r
296 unsigned char OpenRootDir(KvidFile *FileStr, KvidFile *ParentFile)\r
297 {\r
298         if(ParentFile->DataFile == 0)\r
299         return(0);\r
300         FileStr->DataFile = 0;\r
301         FileStr->CanWrite = ParentFile->CanWrite;\r
302     FileStr->Parent = ParentFile;\r
303     FileStr->FilePtr = ParentFile->FilePtr;\r
304     FileStr->AtEOF = 0;\r
305     FileStr->LastOp = 0;\r
306     FileStr->ReSeek = 1;\r
307     FileStr->CurAU = 1;\r
308     FileStr->Offset = 0;\r
309     FileStr->AUSize = ParentFile->AUSize;\r
310     FileStr->CurBit = 255;\r
311     return(1);\r
312 }\r
313 \r
314 unsigned char OpenNestedFile(unsigned char *FileName, KvidFile *FileStr, KvidFile *ParentFile, unsigned long *TimeBuffer)\r
315 {\r
316         unsigned char FileNameBuffer[20];\r
317     KvidFile RootDir;\r
318 \r
319     if(OpenRootDir(&RootDir, ParentFile) == 0)\r
320         return(0);\r
321     FileStr->DataFile = 0;\r
322     FileStr->CanWrite = ParentFile->CanWrite;\r
323     FileStr->Parent = ParentFile;\r
324     FileStr->FilePtr = ParentFile->FilePtr;\r
325     FileStr->AtEOF = 0;\r
326     FileStr->LastOp = 0;\r
327     FileStr->ReSeek = 1;\r
328     FileStr->Offset = 0;\r
329     FileStr->AUSize = ParentFile->AUSize;\r
330     FileStr->CurBit = 255;\r
331     while(1)\r
332     {\r
333         FileRead((void *)FileNameBuffer, 20, &RootDir);\r
334         if((FileNameBuffer[0] == 0) || (RootDir.AtEOF == 1))\r
335             return(0);\r
336         if(strcmp(FileNameBuffer, FileName) == 0)\r
337         {\r
338                 FileRead((void *)FileNameBuffer, 4, &RootDir);\r
339             FileNameBuffer[4] = 0;\r
340             if(TimeBuffer == NULL)\r
341                         FileSkip(&RootDir, 4);\r
342             else\r
343                 FileRead((void *)TimeBuffer, 4, &RootDir);\r
344             FileRead((void *)&FileStr->CurAU, 4, &RootDir);\r
345             if(strcmp(FileNameBuffer, "SDIR") == 0)\r
346             {\r
347                 FileRead((void *)FileNameBuffer, 5, FileStr);\r
348                 FileNameBuffer[5] = 0;\r
349                 if(strcmp(FileNameBuffer, "KFDAT") != 0)\r
350                         return(0);\r
351                 FileRead((void *)&FileStr->AUSize, 2, FileStr);\r
352                 FileStr->DataFile = 1;\r
353             }\r
354             return(1);\r
355         } else {\r
356             FileSkip(&RootDir, 12);\r
357         }\r
358     }\r
359 }\r
360 \r
361 unsigned char OpenFile(unsigned char *FileName, KvidFile *FileStr)\r
362 {\r
363     FileStr->DataFile = 0;\r
364     FileStr->Parent = NULL;\r
365     FileStr->AtEOF = 0;\r
366     FileStr->LastOp = 0;\r
367     FileStr->CurBit = 255;\r
368     if((FileStr->FilePtr = fopen(FileName, "rb+")) == NULL)\r
369     {\r
370         if((FileStr->FilePtr = fopen(FileName, "rb")) == NULL)\r
371             return((MainDat == NULL)?0:OpenNestedFile(FileName, FileStr, MainDat, NULL));\r
372         else\r
373             FileStr->CanWrite = 0;\r
374     } else {\r
375         FileStr->CanWrite = 1;\r
376     }\r
377     return(1);\r
378 }\r
379 \r
380 unsigned char DeleteFile(unsigned char *FileName, KvidFile *ParentFile)\r
381 {\r
382         unsigned char FileNameBuffer[20];\r
383     unsigned short OldOffset;\r
384     unsigned long OldAU, CurAU, CurFAT;\r
385     KvidFile RootDir;\r
386 \r
387     if(OpenRootDir(&RootDir, ParentFile) == 0)\r
388         return(0);\r
389     while(1)\r
390     {\r
391         OldOffset = RootDir.Offset;\r
392         OldAU = RootDir.CurAU;\r
393         FileRead((void *)FileNameBuffer, 20, &RootDir);\r
394         if((FileNameBuffer[0] == 0) || (RootDir.AtEOF == 1))\r
395             return(0);\r
396         if(strcmp(FileNameBuffer, FileName) == 0)\r
397         {\r
398                 FileSkip(&RootDir, 8);\r
399             FileRead(&CurAU, 4, &RootDir);\r
400                 RootDir.Offset = OldOffset;\r
401             RootDir.CurAU = OldAU;\r
402             RootDir.ReSeek = 1;\r
403             FilePutChar(1, &RootDir);\r
404             while(CurAU < 0x80000000)\r
405             {\r
406                 CurFAT = GetFAT(&RootDir, CurAU);\r
407                 fseek(RootDir.FilePtr, DAT_START + (CurFAT * RootDir.AUSize) + ((CurAU % ((RootDir.AUSize >> 2) - 1)) << 2) + 4, SEEK_SET);\r
408                 OldAU = CurAU;\r
409                 fread((void *)&CurAU, 4, 1, RootDir.FilePtr);\r
410                 fseek(RootDir.FilePtr, DAT_START + (CurFAT * RootDir.AUSize) + ((OldAU % ((RootDir.AUSize >> 2) - 1)) << 2) + 4, SEEK_SET);\r
411                 CurFAT = 0;\r
412                 fwrite((void *)&CurFAT, 4, 1, RootDir.FilePtr);\r
413             }\r
414             return(1);\r
415         } else {\r
416             FileSkip(&RootDir, 12);\r
417         }\r
418     }\r
419 }\r
420 \r
421 unsigned char CreateFile(unsigned char *FileName, KvidFile *FileStr, KvidFile *ParentFile, unsigned long FileType)\r
422 {\r
423         KvidFile RootDir;\r
424     unsigned char FileNameBuffer[20];\r
425     unsigned char Existing;\r
426     unsigned long OldAU;\r
427     unsigned short OldOffset;\r
428     unsigned long i;\r
429     unsigned long CurTime;\r
430     unsigned long NewAU;\r
431 \r
432         if(ParentFile == NULL)\r
433     {\r
434         FileStr->DataFile = 0;\r
435         FileStr->Parent = NULL;\r
436         FileStr->AtEOF = 1;\r
437         FileStr->LastOp = 0;\r
438         FileStr->CanWrite = 1;\r
439         if((FileStr->FilePtr = fopen(FileName, "wb+")) == NULL)\r
440                 return(0);\r
441         else\r
442                 return(1);\r
443     }\r
444     if((OpenRootDir(&RootDir, ParentFile) == 0) || (RootDir.CanWrite == 0))\r
445         return(0);\r
446     FileStr->DataFile = 0;\r
447     FileStr->CanWrite = ParentFile->CanWrite;\r
448     FileStr->Parent = ParentFile;\r
449     FileStr->FilePtr = ParentFile->FilePtr;\r
450     FileStr->AtEOF = 0;\r
451     FileStr->LastOp = 0;\r
452     FileStr->ReSeek = 1;\r
453     FileStr->Offset = 0;\r
454     FileStr->AUSize = ParentFile->AUSize;\r
455     FileStr->CurBit = 255;\r
456     while(1)\r
457     {\r
458         OldAU = RootDir.CurAU;\r
459         OldOffset = RootDir.Offset;\r
460         FileRead((void *)FileNameBuffer, 20, &RootDir);\r
461         if(((Existing = FileNameBuffer[0]) < 2) || (RootDir.AtEOF == 1))\r
462         {\r
463                 RootDir.CurAU = OldAU;\r
464             RootDir.Offset = OldOffset;\r
465             RootDir.ReSeek = 1;\r
466             for(i = 0; FileName[i] != 0; i++)\r
467                 FileNameBuffer[i] = FileName[i];\r
468             while(i < 20)\r
469                 FileNameBuffer[i++] = 0;\r
470             FileWrite((void *)FileNameBuffer, 20, &RootDir);\r
471             FileWrite((void *)&FileType, 4, &RootDir);\r
472             CurTime = (unsigned long)time(NULL);\r
473             FileWrite((void *)&CurTime, 4, &RootDir);\r
474             NewAU = FindFreeAU(ParentFile, 0);\r
475             SetFATEntry(ParentFile, NewAU, 0xFFFFFFFF);\r
476             RootDir.ReSeek = 1;\r
477             FileWrite((void *)&NewAU, 4, &RootDir);\r
478             if(Existing == 0)\r
479                     for(i = 0; i < 32; i++)\r
480                         FilePutChar(0, &RootDir);\r
481             FileStr->CurAU = NewAU;\r
482             return(1);\r
483         }\r
484         FileSkip(&RootDir, 12);\r
485     }\r
486 }\r
487 \r
488 unsigned char OpenDatFile(unsigned char *FileName, KvidFile *FileStr)\r
489 {\r
490         unsigned char Signature[6];\r
491     FileStr->DataFile = 1;\r
492     FileStr->Parent = NULL;\r
493     FileStr->ReSeek = 0;\r
494     FileStr->AtEOF = 0;\r
495     FileStr->LastOp = 0;\r
496     FileStr->CurBit = 255;\r
497     if((FileStr->FilePtr = fopen(FileName, "rb+")) == NULL)\r
498     {\r
499         if((FileStr->FilePtr = fopen(FileName, "rb")) == NULL)\r
500             return(0);\r
501         else\r
502             FileStr->CanWrite = 0;\r
503     } else {\r
504         FileStr->CanWrite = 1;\r
505     }\r
506     fread((void *)Signature, 1, 5, FileStr->FilePtr);\r
507     Signature[5] = 0;\r
508     if(strcmp(Signature, "KFDAT") != 0)\r
509     {\r
510         fclose(FileStr->FilePtr);\r
511         return(0);\r
512     }\r
513     fread((void *)&FileStr->AUSize, 2, 1, FileStr->FilePtr);\r
514     if((FileStr->AUSize % 4) != 0)\r
515     {\r
516         fclose(FileStr->FilePtr);\r
517         return(0);\r
518     }\r
519     FileStr->CurFAT = 0;\r
520     FileStr->CurAU = 0;\r
521     FileStr->Offset = 0;\r
522     return(1);\r
523 }\r