Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
fat32.c
Go to the documentation of this file.
1 /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator
2  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3  Copyright (C)2016-2020 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program 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
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 // Very slow, naive and stupid FAT32 implementation. There is even no LFN support, or anything fancy.
20 
21 #ifdef __CC65__
22 # define MEGA65_BUILD
23 #endif
24 
25 #if defined(MEGA65_BUILD) || !defined(XEMU_BUILD) || (defined(XEMU_BUILD) && defined(SD_CONTENT_SUPPORT))
26 
27 #ifndef MEGA65_BUILD
28 # define FDISK_SUPPORT
29 #endif
30 
31 #ifdef XEMU_BUILD
32 # include "xemu/emutools.h"
33 # include "xemu/emutools_files.h"
34 # define FATDEBUG DEBUG
35 # define FATDEBUGPRINT DEBUGPRINT
36 #else
37 # define NL "\n"
38 # define FATDEBUG printf
39 # define FATDEBUGPRINT printf
40 # define FATAL(...) do { fprintf(stderr, "FATAL: "); fprintf(stderr, __VA_ARGS__); exit(1); } while(0)
41 # define ERROR_WINDOW(...) do { fprintf(stderr, "ERROR: "); fprintf(stderr, __VA_ARGS__); } while(0)
42 # define OFF_T_ERROR ((off_t)-1)
43 # ifdef MEGA65_BUILD
44  typedef unsigned long int Uint32;
45  typedef unsigned int Uint16;
46  typedef unsigned char Uint8 ;
47 # else
48 # include <stdint.h>
49  typedef uint32_t Uint32;
50  typedef uint16_t Uint16;
51  typedef uint8_t Uint8 ;
52 # endif
53 #endif
54 
55 #include <stdio.h>
56 #include <sys/types.h>
57 #include <fcntl.h>
58 #include <unistd.h>
59 #include <string.h>
60 #include <stdlib.h>
61 #include <time.h>
62 
63 #include "fat32.h"
64 
65 
66 #define FS_MINIMAL_SIZE_IN_BLOCKS 10
67 
68 
69 static struct {
70  //int (*reader)(Uint32 block, void *data);
71  //int (*writer)(Uint32 block, void *data);
74  int part; // selected partition number
75 } disk;
76 
77 
79 
80 
81 static int mfat_read_DEVICE_blk ( Uint32 block, void *buf )
82 {
83  if (block >= disk.blocks) {
84  FATDEBUGPRINT("FATFS: WARNING: Host FS: reading outside of the device" NL);
85  return -1;
86  }
87  return disk.reader(block, buf);
88 }
89 static int mfat_write_DEVICE_blk ( Uint32 block, void *buf )
90 {
91  if (block >= disk.blocks) {
92  FATDEBUGPRINT("FATFS: WARNING: Host FS: writing outside of the device" NL);
93  return -1;
94  }
95  return disk.writer(block, buf);
96 }
97 
98 static int mfat_read_part_blk ( Uint32 block, void *buf )
99 {
100  if (disk.part < 0) {
101  FATDEBUGPRINT("FATFS: WARNING: read partition block: invalid partition is selected" NL);
102  return -1;
103  }
104  if (block >= mfat_partitions[disk.part].blocks) {
105  FATDEBUGPRINT("FATFS: WARNING: read partition block: trying to read block outside of partition!" NL);
106  return -1;
107  }
108  return mfat_read_DEVICE_blk(block + mfat_partitions[disk.part].first_block, buf);
109 }
110 static int mfat_write_part_blk ( Uint32 block, void *buf )
111 {
112  if (disk.part < 0) {
113  FATDEBUGPRINT("FATFS: WARNING: write partition block: invalid partition is selected" NL);
114  return -1;
115  }
116  if (block >= mfat_partitions[disk.part].blocks) {
117  FATDEBUGPRINT("FATFS: WARNING: write partition block: trying to read block outside of partition!" NL);
118  return -1;
119  }
120  return mfat_write_DEVICE_blk(block + mfat_partitions[disk.part].first_block, buf);
121 }
122 
123 static int mfat_read_cluster ( Uint32 cluster, Uint32 block_in_cluster, void *buf )
124 {
125  cluster &= 0x0FFFFFFFU; // some well known fact, that FAT32 is actually FAT28, and the highest 4 bits should not be used!
126  if (cluster < 2 || cluster >= mfat_partitions[disk.part].clusters) {
127  FATDEBUGPRINT("FATFS: WARNING: read cluster: invalid cluster number %d" NL, cluster);
128  return -1;
129  }
130  if (block_in_cluster >= mfat_partitions[disk.part].cluster_size_in_blocks) {
131  FATDEBUGPRINT("FATFS: WARNING: read cluster: invalid in-cluster-block data %d" NL, block_in_cluster);
132  return -1;
133  }
134  return mfat_read_part_blk(cluster * mfat_partitions[disk.part].cluster_size_in_blocks + mfat_partitions[disk.part].data_area_fake_ofs + block_in_cluster, buf);
135 }
136 static int mfat_write_cluster ( Uint32 cluster, Uint32 block_in_cluster, void *buf )
137 {
138  cluster &= 0x0FFFFFFFU; // some well known fact, that FAT32 is actually FAT28, and the highest 4 bits should not be used!
139  if (cluster < 2 || cluster >= mfat_partitions[disk.part].clusters) {
140  FATDEBUGPRINT("FATFS: WARNING: write cluster: invalid cluster number %d" NL, cluster);
141  return -1;
142  }
143  if (block_in_cluster >= mfat_partitions[disk.part].cluster_size_in_blocks) {
144  FATDEBUGPRINT("FATFS: WARNING: write cluster: invalid in-cluster-block data %d" NL, block_in_cluster);
145  return -1;
146  }
147  return mfat_write_part_blk(cluster * mfat_partitions[disk.part].cluster_size_in_blocks + mfat_partitions[disk.part].data_area_fake_ofs + block_in_cluster, buf);
148 }
149 
150 #define AS_WORD(p,o) (p[o] + (p[o+1] << 8))
151 #define AS_DWORD(p,o) (p[o] + (p[o+1] << 8) + (p[o+2] << 16) + (p[o+3] << 24))
152 
153 
154 static struct {
155  Uint8 buf[512];
157  int dirty;
158  int ofs;
159 } fat_cache;
160 
161 
162 
163 static int mfat_flush_fat_cache ( void )
164 {
165  if (fat_cache.dirty) {
166  if (mfat_write_part_blk(fat_cache.block, fat_cache.buf))
167  return -1;
168  // Also, we need to write the "backup" copy of FAT.
169  // We assume max of two FATs are used, but currenty, other parts of this source assumes ALWAYS two copies.
170  // To be careful, let's check it here though.
171  int fat2_offset = mfat_partitions[disk.part].fat2_start - mfat_partitions[disk.part].fat1_start;
172  if (fat2_offset > 0)
173  mfat_write_part_blk(fat_cache.block + fat2_offset, fat_cache.buf); // FIXME: should we error check this? if "main" FAT was OK to be written but not the "backup" one??
174  fat_cache.dirty = 0;
175  }
176  return 0;
177 }
178 
179 
180 static int cluster_was_free;
181 
182 // Return value:
183 // 0 = end of chain [regardless of the real used EOC]
184 // 1 = fatal error [like I/O layer read problem ...]
185 // other = cluster number from the chained cluster
186 static Uint32 mfat_read_fat_chain ( Uint32 cluster )
187 {
188  Uint32 block;
189  cluster &= 0x0FFFFFFFU; // some well known fact, that FAT32 is actually FAT28, and the highest 4 bits should not be used!
190  if (cluster < 2 || cluster >= mfat_partitions[disk.part].clusters) {
191  FATDEBUGPRINT("FATFS: WARNING: read fat: invalid cluster number %d" NL, cluster);
192  return 1;
193  }
194  Uint32 cluster_in = cluster;
195  block = mfat_partitions[disk.part].fat1_start + (cluster >> 7);
196  fat_cache.ofs = (cluster & 127) << 2;
197  if (block != fat_cache.block) {
198  mfat_flush_fat_cache();
199  if (mfat_read_part_blk(block, fat_cache.buf))
200  return 1;
201  fat_cache.block = block;
202  FATDEBUG("FATFS: UNCACHED block: %d" NL, block);
203  } else
204  FATDEBUG("FATFS: COOL, fat block is cached for %d" NL, block);
205  cluster = AS_DWORD(fat_cache.buf, fat_cache.ofs) & 0x0FFFFFFFU;
206  FATDEBUG("FATFS: DEBUG: mfat_read_chain: got cluster: $%08X" NL, AS_DWORD(fat_cache.buf, fat_cache.ofs));
207  // In theory there is some "official" end-of-chain marker, but in reality it seems anything which is outside of normal
208  // cluster number on the FS "should" be considered as end-of-chain marker.
209  // That's actually great, we should not worry here what is the "EOC" marker, and just say this:
210  if (cluster < 2 || cluster >= mfat_partitions[disk.part].clusters) {
211  // THIS IS AN IMPORTANT HACK! Normally this function is used to follow FAT chain. There, it's should never
212  // enounter free cluster in chain, if it does, that's considered error or "EOC". However. In case of searching
213  // free cluster it is VERY important to know it was EOC (thus the cluster is not free!) or free. So we create
214  // this ugly stuff here can be used by the caller ...
215  cluster_was_free = (cluster == 0);
216  return 0;
217  }
218  if (cluster == cluster_in) {
219  FATDEBUGPRINT("FATFS: ERROR: cluster %u refers itself!" NL, cluster);
220  return 0; // serious problem, cluster refeers to itself???? We just handle the problem as it would be EOC as well ...
221  }
222  return cluster;
223 }
224 
225 
226 // Next can be:
227 // 0 = end of chain
228 // 1 = FREE
229 // Other = valid next cluster to be chained
230 static int mfat_write_fat_chain ( Uint32 cluster, Uint32 next )
231 {
232  if (next >= mfat_partitions[disk.part].clusters)
233  return 1;
234  else if (next == 0)
235  next = mfat_partitions[disk.part].eoc_marker; // 0x0FFFFFFFU;
236  else if (next == 1)
237  next = 0;
238  // first we want to READ ... it also flushes possible previous cached dirty block
239  // It also calculates the block offset (fat_cache.ofs) for us!
240  // And moreover, it also uses cached copies to avoid read, if the same block from FAT was used before!!
241  int ret = mfat_read_fat_chain(cluster);
242  if (ret == 1) // ERROR!!!!
243  return 1;
244  for (int a = fat_cache.ofs; a < fat_cache.ofs + 4; a++, next >>= 8) {
245  if (fat_cache.buf[a] != (Uint8)next) {
246  fat_cache.buf[a] = (Uint8)next;
247  fat_cache.dirty = 1; // now we make the cache dirty, oh-oh
248  }
249  }
250  return 0;
251 }
252 
253 // Free a chain of clusters in FAT starting at argument "cluster"
254 // Returns with zero on successfull operation, or non-zero if any error encountered
255 static int mfat_free_fat_chain ( Uint32 cluster )
256 {
257  while (cluster >= 2) {
258  Uint32 next_cluster = mfat_read_fat_chain(cluster);
259  if (next_cluster == 1)
260  goto error;
261  if (mfat_write_fat_chain(cluster, 1)) // 1=FREE in our API
262  goto error;
263  cluster = next_cluster;
264  }
265  if (mfat_flush_fat_cache())
266  return 1;
267  return cluster; // mfat_read_fat_chain() returns with zero on last cluster thus return with 0 considered OK, other error is PROBLEM!
268 error:
269  mfat_flush_fat_cache(); // even in case of error, we try to flush FAT cache to avoid further corruption of FAT without forgetting this!
270  return 1;
271 }
272 
273 
274 // Allocate LINEAR chunk in FAT for "size" bytes. SIZE CANNOT BE ZERO!!!!!!
275 // Returns cluster number as the first one, or ZERO IN CASE OF ERROR
276 // In case of success, FAT is already written to have the correct chain.
278 {
279  if (!size)
280  return 0; // error
281  Uint32 cluster_byte_size = mfat_partitions[disk.part].cluster_size_in_blocks * 512;
282  FATDEBUG("FATFS: DEBUG: %s() is about seeking for free linear chunk in FAT for %u bytes size object" NL, __func__, size);
283  // OK, so now, size is the needed number of clusters to allocate
284  // start from cluster 2, the first data cluster, to try with (though probably that's root dir, so won't be free, but anyway, for strange situations ...)
285  for (Uint32 cluster = mfat_partitions[disk.part].root_dir_cluster + 1, first = 0, len = 0, seq = 0; cluster < mfat_partitions[disk.part].clusters; cluster++) {
286  Uint32 next = mfat_read_fat_chain(cluster);
287  FATDEBUG("FATFS: %u cluster's result in FAT: %u" NL, cluster, next);
288  if (next == 1)
289  return 0; // error!
290  if (next == 0 && cluster_was_free) { // cluster is free
291  if (first == 0) {
292  first = cluster;
293  len = cluster_byte_size;
294  seq = 1;
295  } else {
296  len += cluster_byte_size;
297  seq++;
298  }
299  if (len >= size) { // we found it :)
300  FATDEBUG("FATFS: DEBUG: %s() founds %u sized free linear chunk in FAT at cluster %u" NL, __func__, size, first);
301  for (Uint32 a = 0; a < seq; a++) {
302  if (mfat_write_fat_chain(first + a, a != seq - 1 ? first + a + 1 : 0)) { // 0 is end-of-chain marker in my implementation
303  // In case of an error, we try to FREE what we've did ...
304  // if that errors out, we can't do too much at this point
305  // And btw that called mfat_flush_fat_cache() as well ...
306  mfat_free_fat_chain(first);
307  return 0; // error!
308  }
309  }
310  mfat_flush_fat_cache(); // flush FAT cache at the end, to be sure everything is written out. FIXME: what happens if it errors out? :-O
311  return first;
312  }
313  } else // cluster is not free
314  first = 0;
315  }
316  FATDEBUG("FATFS: DEBUG: %s() could not found %d sized free linear chunk in FAT ..." NL, __func__, size);
317  return 0; // not found :(
318 }
319 
320 
321 
322 
324 {
325  disk.reader = reader;
326  disk.writer = writer;
327  disk.blocks = device_size;
328  disk.part = -1;
329  fat_cache.block = -1;
330  fat_cache.dirty = 0;
331 }
332 
333 
334 // Reads and parse MBR. Fills mfat_partitions array
335 // Returns with a canidate of the first usable partition [also partiton type], though the FS itself is NOT checked.
336 // returns with -1, if there is some error, or no usable partition candidate found.
337 // Caller must use mfat_use_part() after this function to really select a partition at FS level, by using the retval, or decide at your own.
338 // mfat_init() must be called before this function!
339 int mfat_init_mbr ( void )
340 {
341  int first_valid = -1;
342  Uint8 cache[512], *p;
343  // DANGER WILL ROBINSON! Previous partition could be in use!! Flush cache!
344  mfat_flush_fat_cache();
345  fat_cache.block = -1;
346  fat_cache.dirty = 0;
347  // end of the danger zone
348  if (mfat_read_DEVICE_blk(0, cache)) // read MBR
349  return -1;
350  // parse partition entries
351  memset(mfat_partitions, 0, sizeof mfat_partitions);
352  p = cache + 0x1BE;
353  for (int a = 0; a < 4; a++, p += 16) {
355  mfat_partitions[a].valid = 0;
356  mfat_partitions[a].first_block = AS_DWORD(p, 0x8); //p[0x8] + (p[0x9] << 8) + (p[0xA] << 16) + (p[0xB] << 24);
357  mfat_partitions[a].blocks = AS_DWORD(p, 0xC); //p[0xC] + (p[0xD] << 8) + (p[0xE] << 16) + (p[0xF] << 24);
359  mfat_partitions[a].part_type = p[4];
360  // Check some basic things on possible canditates for a valid partition, but it's only about sizes, layout, partition type, FS is not checked at this point!
361  if (
362  (p[4] == 0xB || p[4] == 0xC) &&
364  mfat_partitions[a].first_block > 0 &&
365  mfat_partitions[a].first_block < disk.blocks &&
366  mfat_partitions[a].last_block < disk.blocks
367  ) {
368  mfat_partitions[a].valid = 1;
369  }
370  FATDEBUG("FATFS: Part#%d: %08X - %08X (size=%08X), type = %02X, valid=%d size=%d Mbytes" NL,
371  a,
372  mfat_partitions[a].first_block,
373  mfat_partitions[a].last_block,
375  mfat_partitions[a].part_type,
376  mfat_partitions[a].valid,
377  mfat_partitions[a].blocks >> 11
378  );
379  if (mfat_partitions[a].valid && first_valid == -1)
380  first_valid = a;
381  }
382  return first_valid;
383 }
384 
385 
386 static void hexdump ( Uint8 *p, int lines, const char *title )
387 {
388  if (title)
389  FATDEBUG("*** %s" NL, title);
390  for (int b = 0 ; b < lines ; b ++) {
391  FATDEBUG("%03X ", b * 32);
392  for (int a = 0; a < 32; a++)
393  FATDEBUG("%02X ", p[a]);
394  for (int a = 0; a < 32; a++)
395  FATDEBUG("%c", p[a] >=32 && p[a] < 127 ? p[a] : '?');
396  FATDEBUG(NL);
397  p += 32;
398  }
399 }
400 
401 
402 
403 
404 // Use this function to select a partition to be used. It also does a check if a partition is a FAT32 one.
405 // Requires: mfat_init() then mfat_init_mbr() functions before.
406 int mfat_use_part ( int part )
407 {
408  int previous_part = disk.part;
409  // DANGER WILL ROBINSON! Previous partition may be was in use! Flush cache!
410  mfat_flush_fat_cache();
411  fat_cache.block = -1;
412  fat_cache.dirty = 0;
413  // end of the danger zone
414  disk.part = part;
415  if (part < 0 || part > 3 || !mfat_partitions[part].valid)
416  goto error;
417  if (mfat_partitions[part].fs_validated) // if filesystem was validated before, we returns ...
418  return 0;
419  // try to validate filesystem now, if it's really FAT32, ALSO it puts the needed information into mfat_partitions[] used by the actual FS code!!!
420  Uint8 cache[512];
421  if (mfat_read_part_blk(0, cache))
422  goto error;
423  FATDEBUG("FATFS: INFO: Bytes per logical sector: %d" NL, AS_WORD(cache,0xB));
424  if (AS_WORD(cache, 0xB) != 512) {
425  FATDEBUGPRINT("FATFS: WARNING: Only 512 bytes / logical sector is supported!" NL);
426  goto error;
427  }
428  FATDEBUG("FATFS: INFO: Logical sectors per cluster: %d (cluster size = %d bytes)" NL, cache[0xD], cache[0xD] * 512);
429  if (cache[0xD] == 0) { // FIXME: better check, should be some non-zero & power of two ...
430  FATDEBUGPRINT("FATFS: WARNING: Invalid logical sectors per cluster information!" NL);
431  goto error;
432  }
434  FATDEBUG("FATFS: INFO: Reserved logical sectors: %d" NL, AS_WORD(cache, 0xE));
435  FATDEBUG("FATFS: INFO: FAT copies: %d" NL, cache[0x10]);
436  if (cache[0x10] != 2) {
437  FATDEBUGPRINT("FATFS: WARNING: Invalid number of FAT copies!" NL);
438  goto error;
439  }
440  FATDEBUG("FATFS: INFO: Logical sectors by FAT: %d" NL, AS_DWORD(cache, 0x24));
441  FATDEBUG("FATFS: INFO: Cluster of ROOT dir: %d" NL, AS_DWORD(cache, 0x2C));
443 
444  FATDEBUG("FATFS: INFO: FS information sector at block: %d" NL, AS_WORD(cache, 0x30));
446 
447 
448  mfat_partitions[part].fat1_start = AS_WORD(cache, 0xE); // FAT starts after the reserved sectors area
449  mfat_partitions[part].fat2_start = AS_WORD(cache, 0xE) + AS_DWORD(cache, 0x24);
450  Uint32 a = AS_WORD(cache, 0xE) + AS_DWORD(cache, 0x24) * 2;
451  mfat_partitions[part].clusters = ((mfat_partitions[part].blocks - a) / mfat_partitions[part].cluster_size_in_blocks) + 2;
452  FATDEBUG("FATFS: INFO: Number of clusters CALCULATED (incl. 0,1): %d" NL, mfat_partitions[part].clusters);
453  FATDEBUG("FATFS: INFO: Number of clusters from FAT size: %d" NL, AS_DWORD(cache, 0x24) * 128); // FIXME: decide what number of clusters data is valid [obviously, the smaller value of both ...]
454 
456 
457 
458  if (mfat_read_part_blk(mfat_partitions[part].fat1_start, cache))
459  goto error;
460  FATDEBUG("FATFS: INFO: FAT1 marks #0: $%08X" NL, AS_DWORD(cache, 0));
462  if ((mfat_partitions[part].eoc_marker & 0x0FFFFFFFU) < 0x0FFFFFF8U)
463  mfat_partitions[part].eoc_marker = 0x0FFFFFF8U;
464  FATDEBUG("FATFS: EOC marker will be: %X" NL, mfat_partitions[part].eoc_marker);
465  FATDEBUG("FATFS: INFO: FAT1 marks #1: $%08X" NL, AS_DWORD(cache, 4));
466  if (mfat_read_part_blk(mfat_partitions[part].fat2_start, cache))
467  goto error;
468  FATDEBUG("FATFS: INFO: FAT2 marks #0: $%08X" NL, AS_DWORD(cache, 0));
469  FATDEBUG("FATFS: INFO: FAT2 marks #1: $%08X" NL, AS_DWORD(cache, 4));
470  //if (mfat_read_
471  //luster(mfat_partitions[part].root_dir_cluster, 0, cache))
472  // goto error;
473  //hexdump(cache, 16, "Root directory");
475  do {
476  for (int b = 0; b < mfat_partitions[part].cluster_size_in_blocks; b++)
477  if (mfat_read_cluster(a, b, cache)) {
478  FATDEBUGPRINT("FATFS: Error following the root directory!" NL);
479  goto error;
480  } else {
481  FATDEBUG("FATFS: INFO: Root directory, cluster=%d/block=%d" NL, a, b);
482  hexdump(cache, 16, NULL);
483  for (int c = 0; c < 512; c += 32) {
484  if (cache[c] >= 32 && cache[c] != 0xE5 && (cache[c + 0xB] & 8)) {
485  cache[c + 0xB] = 0;
486  FATDEBUG("FATFS: INFO: VOLUME is: \"%s\"" NL, cache + c);
487  }
488  if (cache[c] == 0)
489  goto end_of_directory;
490  }
491  }
492  a = mfat_read_fat_chain(a);
493  if (a == 1)
494  goto error;
495  } while (a);
496 end_of_directory:
497 
498  // just for fun ...
499  //for (a = 2; a < mfat_partitions[part].
500 
501  // FS information sector
502  if (mfat_partitions[part].fs_info_block_number > 0 && mfat_partitions[part].fs_info_block_number != 0xFFFF) {
503  if (mfat_read_part_blk(mfat_partitions[part].fs_info_block_number, cache))
504  goto error;
505  hexdump(cache, 16, "FS information sector");
506  FATDEBUG("FATFS: INFO: FS info signature @0=$%08X @1E4=$%08X @1FC=$%08X" NL,
507  AS_DWORD(cache, 0), AS_DWORD(cache, 0x1E4), AS_DWORD(cache, 0x1FC)
508  );
509  if (AS_DWORD(cache, 0) != 0x41615252 || AS_DWORD(cache, 0x1E4) != 0x61417272 || AS_DWORD(cache, 0x1FC) != 0xAA550000U) {
510  FATDEBUGPRINT("FATFS: *** BAD FS info sector signature(s)" NL);
511  goto error;
512  }
513  } else {
514  FATDEBUGPRINT("FATFS: No FS information sector!" NL);
515  goto error;
516  }
517  FATDEBUGPRINT("FATFS: GOOD, FS seems to be intact! :-)" NL);
519  return 0;
520 error:
521  disk.part = previous_part;
522  return -1;
523 }
524 
525 
526 /* ---- FILESYSTEM like functions, generic, even directories are just "files" with special format though ---- */
527 
528 
530 {
531  p->cluster = cluster;
532  p->in_cluster_block = 0;
533  p->in_block_pos = 0;
534  p->eof = 0;
535  p->size_constraint = -1;
536  p->file_pos = 0;
537  p->partition = &mfat_partitions[disk.part];
538  p->start_cluster = cluster; // for rewind() like function?
539 }
540 
541 
543 {
544  mfat_open_stream(p, mfat_partitions[disk.part].root_dir_cluster); // we can't use p->partition here since mfat_open_stream is what sets it!!!
545 }
546 
547 
549 {
550  p->cluster = p->start_cluster;
551  p->in_cluster_block = 0;
552  p->in_block_pos = 0;
553  p->eof = 0;
554  p->file_pos = 0;
555 }
556 
557 
558 // Return value: 0=error, otherwise file size determined by fat chains converted to bytes, not the one given in the directory entry!
559 // It also fills fragmented (if it's not NULL) with the fact, that file is fragmented or not, unless return value is zero (error)
560 Uint32 mfat_get_real_size ( mfat_stream_t *p, int *fragmented )
561 {
562  Uint32 clusters = 0;
564  if (fragmented)
565  *fragmented = 0;
566  while (cluster >= 2 && cluster < p->partition->clusters) {
567  clusters++;
568  Uint32 next_cluster = mfat_read_fat_chain(cluster);
569  if (next_cluster == 1)
570  return 0; // error
571  if (next_cluster >= 2 && next_cluster != (cluster + 1) && fragmented)
572  *fragmented = 1;
573  cluster = next_cluster;
574  }
575  return clusters * p->partition->cluster_size_in_blocks * 512;
576 }
577 
578 // Warning: uses own cache! That is, it's not possible to deal with more than ONE stream at the same time!!
579 static struct {
580  Uint8 buf[512];
583  int part;
584 } stream_cache = {
585  .part = -1,
586  .cluster_block = -1,
587  .cluster = -1
588 };
589 
590 
591 // Warning: uses own cache! That is, it's not possible to deal with more than ONE stream at the same time!!
592 // Currently do NOT mix with mfat_write_stream()!
593 int mfat_read_stream ( mfat_stream_t *p, void *buf, int size )
594 {
595  //static Uint8 cache[512];
596  //static Uint32 cached_cluster;
597  //static int cached_cluster_block = -1;
598  //static int cached_part = -1;
599  int ret = 0;
600  if (p->eof)
601  return 0;
602  if (p->size_constraint >= 0 && p->file_pos + size > p->size_constraint)
603  size = p->size_constraint - p->file_pos;
604  while (size > 0) {
605  if (p->cluster != stream_cache.cluster || p->in_cluster_block != stream_cache.cluster_block || disk.part != stream_cache.part) {
606  if (mfat_read_cluster(p->cluster, p->in_cluster_block, stream_cache.buf))
607  goto error;
608  stream_cache.cluster = p->cluster;
609  stream_cache.cluster_block = p->in_cluster_block;
610  stream_cache.part = disk.part;
611  } else
612  FATDEBUG("FATFS: INFO: WOW, data block has been cached for cluster %d, block %d within cluster" NL, stream_cache.cluster, stream_cache.cluster_block);
613  int piece = 512 - p->in_block_pos;
614  if (size < piece)
615  piece = size;
616  if (piece == 0)
617  goto eof;
618  memcpy(buf, stream_cache.buf + p->in_block_pos, piece);
619  ret += piece;
620  p->file_pos += piece;
621  size -= piece;
622  buf = (Uint8*)buf + piece;
623  p->in_block_pos += piece;
624  if (p->in_block_pos == 512) {
625  p->in_block_pos = 0;
626  p->in_cluster_block++;
628  p->cluster = mfat_read_fat_chain(p->cluster);
629  if (p->cluster == 1)
630  goto error;
631  if (p->cluster == 0)
632  goto eof;
633  p->in_cluster_block = 0;
634  }
635  } else if (p->in_block_pos > 512) {
636  FATAL("FATFS: in_block_pos is greater than 512");
637  }
638  }
639  return ret;
640 eof:
641  p->eof = 1;
642  return ret;
643 error:
644  p->eof = 1;
645  return -1;
646 }
647 
648 
649 #if 0
650 // Warning: uses own cache! That is, it's not possible to deal with more than ONE stream at the same time!!
651 // Currently do NOT mix with mfat_read_stream()!
652 int mfat_write_stream ( mfat_stream_t *p, void *buf, int size )
653 {
654  static Uint8 cache[512];
655  static Uint32 cached_cluster;
656  static int cached_cluster_block = -1;
657  int ret = 0;
658  if (p->eof)
659  return 0;
660  if (p->size_constraint >= 0 && p->file_pos + size > p->size_constraint)
661  size = p->size_constraint - p->file_pos;
662  while (size > 0) {
663  if (p->cluster != cached_cluster || p->in_cluster_block != cached_cluster_block) {
664  if (mfat_read_cluster(p->cluster, p->in_cluster_block, cache))
665  goto error;
666  cached_cluster = p->cluster;
667  cached_cluster_block = p->in_cluster_block;
668  } else
669  FATDEBUG("WOW, data block has been cached for cluster %d, block %d within cluster" NL, cached_cluster, cached_cluster_block);
670  int piece = 512 - p->in_block_pos;
671  if (size < piece)
672  piece = size;
673  if (piece == 0)
674  goto eof;
675  memcpy(buf, cache + p->in_block_pos, piece);
676  ret += piece;
677  p->file_pos += piece;
678  size -= piece;
679  buf = (Uint8*)buf + piece;
680  p->in_block_pos += piece;
681  if (p->in_block_pos == 512) {
682  p->in_block_pos = 0;
683  p->in_cluster_block++;
685  p->cluster = mfat_read_fat_chain(p->cluster);
686  if (p->cluster == 1)
687  goto error;
688  if (p->cluster == 0)
689  goto eof;
690  p->in_cluster_block = 0;
691  }
692  } else if (p->in_block_pos > 512) {
693  FATAL("FATFS: in_block_pos is greater than 512");
694  }
695  }
696  return ret;
697 eof:
698  p->eof = 1;
699  return ret;
700 error:
701  p->eof = 1;
702  return -1;
703 }
704 #endif
705 
706 
707 /* ---- functions for handling directories ---- */
708 
709 static mfat_stream_t dir_cur_item_stream;
710 
711 
712 static void repos_cur_dirent ( mfat_stream_t *p )
713 {
714  memcpy(p, &dir_cur_item_stream, sizeof(mfat_stream_t));
715 }
716 
717 
718 int mfat_read_directory ( mfat_dirent_t *p, int type_filter )
719 {
720  Uint8 buf[32];
721  do {
722  // FIXME: below, check if this really works on abnormal direcotry as well when it's not closed with a null entry!
723  memcpy(&dir_cur_item_stream, &p->stream, sizeof(mfat_stream_t));
724  int ret = mfat_read_stream(&p->stream, buf, 32);
725  if (ret <= 0) {
726  FATDEBUG("FATFS: INFO: %s getting ret %d" NL, __func__, ret);
727  return ret;
728  }
729  if (ret != 32) {
730  FATAL("FATFS: dirent read 32 is not 32" NL);
731  }
732  if (buf[0] == 0) { // marks end of directory, thus returning with no entry found at this point
733  FATDEBUG("FATFS: INFO: %s end of stream" NL, __func__);
734  p->stream.eof = 1;
735  return 0;
736  }
737  } while (
738  ((buf[0xB] & 0x0F) == 0x0F) || // LFN piece is ignored for now, not supported
739  ( buf[0x0] <= 0x20) || // strange character as the first char is not supported, and ignored, even space
740  ( buf[0x0] & 0x80) || // ... or any high-bit set byte (that includes 0xE5 too, which is deleted file)
741  // if it's a volume label but we haven't requested for volume label to be found, then ignore it
742  ((buf[0xB] & 0x08) && !(type_filter & MFAT_FIND_VOL )) ||
743  // if it's a directory but we haven't requested for directory to be found, then ignore it
744  ((buf[0xB] & 0x10) && !(type_filter & MFAT_FIND_DIR )) ||
745  // if it's a regular file (ie, not volume label, not directory) but we haven't requested for regular file to be found, then ignore it
746  ((buf[0xB] & 0x18) == 0 && !(type_filter & MFAT_FIND_FILE))
747  );
748  // Convert name into "string" format with BASE8.EXT3 ...
749  // Technically it's kinda valid if space is part of file name, but we don't support such a scenario and simply ignore the problem ...
750  int i = 0;
751  char *d = p->name;
752  while (buf[i] != 0x20 && i < 8)
753  *d++ = buf[i++];
754  if (buf[8] != 0x20) {
755  i = 0;
756  *d++ = '.';
757  while (buf[8 + i] != 0x20 && i < 3)
758  *d++ = buf[8 + (i++)];
759  }
760  *d = '\0';
761  // end of filename stuff. Also store (just in case) the FAT name as-is
762  memcpy(p->fat_name, buf, 8 + 3);
763  p->fat_name[8 + 3] = '\0';
764  // Also store start cluster, size, file type and date!
765  p->cluster = AS_WORD(buf, 0x1A) + (AS_WORD(buf, 0x14) << 16);
766  p->size = AS_DWORD(buf, 0x1C);
767  p->type = buf[0xB];
768  struct tm time;
769  time.tm_sec = (AS_WORD(buf, 0x16) & 31) << 1;
770  time.tm_min = (AS_WORD(buf, 0x16) >> 5) & 63;
771  time.tm_hour = (AS_WORD(buf, 0x16) >> 11) & 31;
772  time.tm_mday = AS_WORD(buf, 0x18) & 31;
773  time.tm_mon = (AS_WORD(buf, 0x18) >> 5) & 15;
774  time.tm_year = (AS_WORD(buf, 0x18) >> 9) + 80;
775  time.tm_wday = 0;
776  time.tm_yday = 0;
777  time.tm_isdst = -1;
778  p->time = mktime(&time);
779  return 1;
780 }
781 
782 
783 // int mfat_append_directory FIXME
784 
785 // "name" can be NULL to return with the first valid one according to type_filter!
786 int mfat_search_in_directory ( mfat_dirent_t *p, const char *name, int type_filter )
787 {
788  // normalize the search name, ie shorten to 8+3, all capitals, also checking for invalid chars, etc
789  char normalized_search_name[8 + 1 + 3 + 1];
790  if (name && mfat_normalize_name(normalized_search_name, name)) { // only do normalization if name != NULL, "short circuit" kind of solution
791  p->name[0] = 0;
792  return -1; // bad search name!
793  }
795  for (;;) {
796  int ret = mfat_read_directory(p, type_filter);
797  if (ret == 1) {
798  FATDEBUG("FATFS: INFO: %s considering file [%s] as [%s]" NL, __func__, p->name, name ? normalized_search_name : "<first>");
799  // FIXME: probably, later we want to allow joker characters?
800  if (!name)
801  return 1;
802  if (!strcmp(p->name, normalized_search_name))
803  return 1;
804  } else {
805  FATDEBUG("FATFS: INFO: %s returns because of ret being %d" NL, __func__, ret);
806  return ret;
807  }
808  }
809 }
810 
811 
812 // about the details how it returns, please reas the comments near the end of this functions. Zero return value = error
814 {
815  char fat_name[8 + 3 + 1];
816  mfat_fatize_name(fat_name, name);
817  int write_null_entry = 0;
818  int ret = mfat_search_in_directory(dirent, name, MFAT_FIND_FILE | MFAT_FIND_DIR); // we also want to find dirs so we avoid the collosion between the same name
819  if (ret == 1) {
820  // found the file
821  if (IS_MFAT_DIR(dirent->type)) {
822  // PROBLEM: the found item is a _DIRECTORY_, we can't overwrite that with a file!
823  ERROR_WINDOW("Problem: file %s already exists as a directory on the image\nNot possible to overwrite with a file\nSource file: %s", dirent->name, name);
824  return 0; // error
825  }
826  // let's be cheap and wasteful. We're just DELETE the old FAT chain, and allocate a new one, even if file existed before and would be enough for us, and also non-fragmented
827  // if the scenario above is true, probably it will allocate the same space then, so no harm is done, just slower ...
828  // however these stuffs are Xemu init time tasks, so does not matter a lot, to be slower a bit ...
829  mfat_free_fat_chain(dirent->cluster);
830  } else if (ret == 0) {
831  // not found the file
832  // ... so we should extend directory and allocate new chain of FAT as well
833  write_null_entry = 1; // TODO: this should be handled!
834  } else if (ret == -1) {
835  // some error occured
836  return 0; // ERROR?
837  } else {
838  FATAL("Unknown error code of %d in %s", ret, __func__);
839  }
840  // We assume that *NO* stream operation was done, so stream cache STILL holds the directory entry!!!!
841  // that is, we just modify the cache and write back ...
842  repos_cur_dirent(&dirent->stream); // sets "filepos" stuffs the current entry back
843  Uint8 *p = stream_cache.buf + dirent->stream.in_block_pos;
844  // Allocate FAT!!!!
846  if (!dirent->cluster) {
847  // ERROR: could not allocate chain!!!!
848  // We should delete the file (since its old chain is free'd ...) and give up :(
849  *p = 0xE5;
850  // if this one does not work, we can't do anything too much, anyway ...
851  mfat_write_cluster(stream_cache.cluster, stream_cache.cluster_block, stream_cache.buf);
852  return 0;
853  }
854  memcpy(p, fat_name, 8 + 3); // copy FAT style file name in
855  p[0x1A] = dirent->cluster & 0xFF;
856  p[0x1B] = (dirent->cluster >> 8) & 0xFF;
857  p[0x14] = (dirent->cluster >> 16) & 0xFF;
858  p[0x15] = (dirent->cluster >> 24) & 0xFF;
859  p[0x1C] = size & 0xFF;
860  p[0x1D] = (size >> 8) & 0xFF;
861  p[0x1E] = (size >> 16) & 0xFF;
862  p[0x1F] = (size >> 24) & 0xFF;
863  p[0x0B] = 0; // file type
864  time_t ts = time(NULL);
865  if (ts) {
866  struct tm *tm = localtime(&ts);
867  if (tm) {
868  p[0x16] = (tm->tm_sec >> 1) + ((tm->tm_min & 0xF) << 5);
869  p[0x17] = (tm->tm_min >> 3) + (tm->tm_hour << 3);
870  p[0x18] = tm->tm_mday + (((tm->tm_mon + 1) & 7) << 5);
871  p[0x19] = ((tm->tm_mon + 1) >> 3) + ((tm->tm_year - 80) << 1);
872  memcpy(p + 0x0E, p + 0x16, 4);
873  }
874  }
875  if (mfat_write_cluster(stream_cache.cluster, stream_cache.cluster_block, stream_cache.buf)) {
876  return 0; // ERROR
877  }
878  // RETURN VALUE:
879  // just calculate a DEVICE dependent block offset of the cluster.
880  // Now it's the caller responsibility to simply copy anything (do NOT exceed the specified size this function was called with!)
881  FATDEBUG("FAT32: allocated cluster chain from cluster %u for file %s" NL, dirent->cluster, name);
883 }
884 
885 
886 
888 {
889  if (p->cluster < 2 || p->cluster >= p->stream.partition->clusters)
890  return -1;
892  if (!(p->type & (0x10|0x08|0x40|0x80)))
893  p->stream.size_constraint = p->size;
894  return 0;
895 }
896 
897 
898 int mfat_open_file_by_name ( mfat_dirent_t *p, const char *name, int type_filter )
899 {
900  int ret = mfat_search_in_directory(p, name, type_filter);
901  if (ret != 1)
902  return ret;
903  return mfat_open_file_by_dirent(p);
904 }
905 
906 
907 int mfat_read_file ( mfat_dirent_t *p, void *buf, int size )
908 {
909  return mfat_read_stream(&p->stream, buf, size);
910 }
911 
912 
913 /* ----- UTILS ------- */
914 
915 static const char *allowed_dos_name_chars = "!#$%&'()-@^_`{}~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // we don't allow space, even if it's technically not illegal
916 
917 #if 0
918 static char fat_dirent_name[8 + 3 + 1];
919 #endif
920 
921 int mfat_fatize_name ( char *d, const char *s )
922 {
923  int len = 8;
924  int in_ext = 0;
925  //if (!d)
926  // d = fat_dirent_name;
927  memset(d, 0x20, 8 + 3);
928  d[8 + 3] = 0;
929  for (;;) {
930  char c = *s++;
931  if (c == 0) {
932  return 0;
933  } else if (c == '.') {
934  if (in_ext)
935  return -1; // more than one dot in FN
936  if (len == 8)
937  return -1; // file name begins with dot (we don't allow '.' and '..' reference in this func, btw)
938  in_ext = 1;
939  d += len;
940  len = 3;
941  } else {
942  if (len) {
943  if (c >= 'a' && c <= 'z')
944  c -= 'a' - 'A';
945  if (!strchr(allowed_dos_name_chars, c))
946  return -1; // not allowd character in filename
947  *d++ = c;
948  len--;
949  }
950  }
951  }
952 }
953 
954 
955 // Again this function does not support scenario, where name contains a space
956 int mfat_normalize_name ( char *d, const char *s )
957 {
958  int len = 8;
959  int in_ext = 0;
960  char *d_orig = d;
961  for (;;) {
962  char c = *s++;
963  if (c == 0) {
964  *d = '\0';
965  return 0;
966  } else if (c == '.') {
967  if (in_ext)
968  break; // more than one dot in FN
969  if (len == 8)
970  break; // file name begins with dot (we don't allow '.' and '..' reference in this func, btw)
971  in_ext = 1;
972  *d++ = '.';
973  len = 3;
974  } else {
975  if (len) {
976  if (c >= 'a' && c <= 'z')
977  c -= 'a' - 'A';
978  if (!strchr(allowed_dos_name_chars, c))
979  break; // not allowd character in filename
980  *d++ = c;
981  len--;
982  }
983  }
984  }
985  d_orig[0] = '?';
986  d_orig[1] = '\0';
987  return -1;
988 }
989 
990 
991 #ifndef XEMU_BUILD
992 /* ----- TEST SUITE ----- */
993 static int fd;
994 
995 static int raw_reader ( Uint32 block, Uint8 *buf )
996 {
997  off_t ofs = (off_t)block << 9; // convert to byte offset
998  if (lseek(fd, ofs, SEEK_SET) != ofs) {
999  perror("Host seek error");
1000  return -1;
1001  }
1002  if (read(fd, buf, 512) != 512) {
1003  perror("Host read error");
1004  return -1;
1005  }
1006  return 0;
1007 }
1008 
1009 int main ( int argc, char **argv )
1010 {
1011  // static const char default_fn[] = "/home/lgb/.local/share/xemu-lgb/mega65/mega65.img";
1012  static const char default_fn[] = "hyppo.disk";
1013  const char *fn = (argc > 1) ? argv[1] : default_fn;
1014  FATDEBUG("Disk image: %s" NL, fn);
1015  fd = open(fn, O_RDONLY);
1016  if (fd < 0) {
1017  perror("Open disk image");
1018  return 1;
1019  }
1020  off_t devsize = lseek(fd, 0, SEEK_END);
1021  if (devsize == OFF_T_ERROR) {
1022  perror("Host lseek to get device size");
1023  close(fd);
1024  return 1;
1025  }
1026  if (devsize & 511) {
1027  FATDEBUGPRINT("FATFS: WARNING: Host size error: size is not 512 byte aligned" NL);
1028  close(fd);
1029  return 1;
1030  }
1031  if (devsize < 16*1024*1024) {
1032  FATDEBUGPRINT("FATFS: WARNING: Host size error: image is too small (<16Mbytes)" NL);
1033  close(fd);
1034  return 1;
1035  }
1036  devsize >>= 9;
1037  if (devsize > 0x2000000) {
1038  FATDEBUGPRINT("FATFS: WARNING: Host size error: image is too large (>16Gbytes)" NL);
1039  close(fd);
1040  return 1;
1041  }
1042  mfat_init( raw_reader, NULL, devsize );
1043  int part = mfat_init_mbr();
1044  if (part < 0) {
1045  FATDEBUGPRINT("FATFS: WARNING: No partition could be detected in MBR for usage" NL);
1046  close(fd);
1047  return 1;
1048  }
1049  if (mfat_use_part(part)) {
1050  FATDEBUGPRINT("FATFS: WARNING: Cannot make partition #%d to be selected" NL, part);
1051  close(fd);
1052  return 1;
1053  }
1054  // STREAM TEST
1055  //Uint8 dirent[32];
1056  mfat_dirent_t dirent;
1057  //mfat_open_stream(&dirent.stream, mfat_partitions[part].root_dir_cluster);
1058  mfat_open_rootdir(&dirent.stream);
1059  int ret;
1060  int fragmented;
1061  ret = mfat_get_real_size(&dirent.stream, &fragmented);
1062  FATDEBUG("GET REAL SIZE = %d, fragmented = %d" NL, ret, fragmented);
1063  ret = mfat_search_in_directory(&dirent, NULL, MFAT_FIND_VOL);
1064  FATDEBUG("VOLUME NAME: \"%s\"" NL, ret == 1 ? dirent.fat_name : "???");
1065  for (int x = 0; x < 2; x++) {
1066  mfat_rewind_stream(&dirent.stream);
1067  for (;;) {
1068  int ret = mfat_read_directory(&dirent, 0xFF);
1069  if (ret != 1)
1070  break;
1071  FATDEBUG("FILE: \"%s\" size = %u cluster=%u" NL, dirent.name, dirent.size, dirent.cluster);
1072  }
1073  }
1074  // SEARCH TEST
1075  ret = mfat_search_in_directory(&dirent, "FOOD", 0xFF);
1076  FATDEBUG("SEARCH FOR FOOD: %d" NL, ret);
1077  if (!ret) {
1078  ret = mfat_search_in_directory(&dirent, "MEGA65.ROM", 0xFF);
1079  FATDEBUG("RESULT of searching file : %d" NL, ret);
1080  ret = mfat_search_in_directory(&dirent, "MEGA65.ROM", 0xFF);
1081  FATDEBUG("RESULT of searching file (AGAIN!): %d" NL, ret);
1082  }
1083  close(fd);
1084  puts("WOW :-)");
1085  return 0;
1086 }
1087 #endif
1088 
1089 #endif
mfat_dirent_t
Definition: fat32.h:60
AS_DWORD
#define AS_DWORD(p, o)
Definition: fat32.c:151
FATDEBUG
#define FATDEBUG
Definition: fat32.c:38
AS_WORD
#define AS_WORD(p, o)
Definition: fat32.c:150
mfat_init
void mfat_init(mfat_io_callback_func_t reader, mfat_io_callback_func_t writer, Uint32 device_size)
Definition: fat32.c:323
FATDEBUGPRINT
#define FATDEBUGPRINT
Definition: fat32.c:39
mfat_normalize_name
int mfat_normalize_name(char *d, const char *s)
Definition: fat32.c:956
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: fat32.c:41
cluster
Uint32 cluster
Definition: fat32.c:581
emutools.h
mfat_stream_t::start_cluster
Uint32 start_cluster
Definition: fat32.h:57
mfat_open_rootdir
void mfat_open_rootdir(mfat_stream_t *p)
Definition: fat32.c:542
mfat_part_st::part_type
int part_type
Definition: fat32.h:37
mfat_part_st::data_area_fake_ofs
Uint32 data_area_fake_ofs
Definition: fat32.h:41
mfat_dirent_t::type
Uint8 type
Definition: fat32.h:66
mfat_part_st
Definition: fat32.h:32
OFF_T_ERROR
#define OFF_T_ERROR
Definition: fat32.c:42
mfat_stream_t::in_cluster_block
int in_cluster_block
Definition: fat32.h:51
mfat_stream_t::file_pos
int file_pos
Definition: fat32.h:55
mfat_part_st::cluster_size_in_blocks
Uint32 cluster_size_in_blocks
Definition: fat32.h:42
mfat_stream_t::partition
struct mfat_part_st * partition
Definition: fat32.h:56
mfat_partitions
struct mfat_part_st mfat_partitions[4]
Definition: fat32.c:78
mfat_get_real_size
Uint32 mfat_get_real_size(mfat_stream_t *p, int *fragmented)
Definition: fat32.c:560
mfat_use_part
int mfat_use_part(int part)
Definition: fat32.c:406
reader
mfat_io_callback_func_t reader
Definition: fat32.c:72
mfat_dirent_t::fat_name
char fat_name[8+3+1]
Definition: fat32.h:63
mfat_stream_t::size_constraint
int size_constraint
Definition: fat32.h:54
mfat_init_mbr
int mfat_init_mbr(void)
Definition: fat32.c:339
mfat_dirent_t::time
time_t time
Definition: fat32.h:67
mfat_stream_t::eof
int eof
Definition: fat32.h:53
mfat_search_in_directory
int mfat_search_in_directory(mfat_dirent_t *p, const char *name, int type_filter)
Definition: fat32.c:786
fn
const char * fn
Definition: roms.c:42
Uint32
uint32_t Uint32
Definition: fat32.c:49
writer
mfat_io_callback_func_t writer
Definition: fat32.c:72
mfat_part_st::fs_validated
int fs_validated
Definition: fat32.h:39
Uint8
uint8_t Uint8
Definition: fat32.c:51
block
Uint32 block
Definition: fat32.c:156
mfat_part_st::fs_info_block_number
Uint32 fs_info_block_number
Definition: fat32.h:45
emutools_files.h
mfat_dirent_t::size
Uint32 size
Definition: fat32.h:65
x
int x
Definition: console.c:27
next
Uint8 next
Definition: input_devices.c:77
mfat_dirent_t::name
char name[8+3+1+1]
Definition: fat32.h:62
mfat_io_callback_func_t
int(* mfat_io_callback_func_t)(Uint32 block, Uint8 *data)
Definition: fat32.h:30
mfat_open_stream
void mfat_open_stream(mfat_stream_t *p, Uint32 cluster)
Definition: fat32.c:529
mfat_stream_t
Definition: fat32.h:49
mfat_fatize_name
int mfat_fatize_name(char *d, const char *s)
Definition: fat32.c:921
MFAT_FIND_DIR
#define MFAT_FIND_DIR
Definition: fat32.h:23
cluster_block
int cluster_block
Definition: fat32.c:582
mfat_part_st::valid
int valid
Definition: fat32.h:38
mfat_read_file
int mfat_read_file(mfat_dirent_t *p, void *buf, int size)
Definition: fat32.c:907
NL
#define NL
Definition: fat32.c:37
mfat_part_st::root_dir_cluster
Uint32 root_dir_cluster
Definition: fat32.h:44
FS_MINIMAL_SIZE_IN_BLOCKS
#define FS_MINIMAL_SIZE_IN_BLOCKS
Definition: fat32.c:66
FATAL
#define FATAL(...)
Definition: fat32.c:40
blocks
Uint32 blocks
Definition: fat32.c:73
mfat_dirent_t::stream
mfat_stream_t stream
Definition: fat32.h:61
MFAT_FIND_FILE
#define MFAT_FIND_FILE
Definition: fat32.h:24
size
int size
Definition: inject.c:37
dirty
int dirty
Definition: fat32.c:157
mfat_part_st::fat2_start
Uint32 fat2_start
Definition: fat32.h:43
mfat_allocate_linear_fat_chunk
Uint32 mfat_allocate_linear_fat_chunk(Uint32 size)
Definition: fat32.c:277
mfat_part_st::last_block
Uint32 last_block
Definition: fat32.h:34
mfat_open_file_by_dirent
int mfat_open_file_by_dirent(mfat_dirent_t *p)
Definition: fat32.c:887
mfat_overwrite_file_with_direct_linear_device_block_write
Uint32 mfat_overwrite_file_with_direct_linear_device_block_write(mfat_dirent_t *dirent, const char *name, Uint32 size)
Definition: fat32.c:813
mfat_open_file_by_name
int mfat_open_file_by_name(mfat_dirent_t *p, const char *name, int type_filter)
Definition: fat32.c:898
main
int main(int argc, char **argv)
Definition: fat32.c:1009
MFAT_FIND_VOL
#define MFAT_FIND_VOL
Definition: fat32.h:22
mfat_read_directory
int mfat_read_directory(mfat_dirent_t *p, int type_filter)
Definition: fat32.c:718
Uint16
uint16_t Uint16
Definition: fat32.c:50
fat32.h
mfat_stream_t::cluster
Uint32 cluster
Definition: fat32.h:50
name
const char * name
Definition: joystick.c:46
mfat_part_st::eoc_marker
Uint32 eoc_marker
Definition: fat32.h:46
mfat_stream_t::in_block_pos
int in_block_pos
Definition: fat32.h:52
mfat_part_st::fat1_start
Uint32 fat1_start
Definition: fat32.h:43
mfat_part_st::blocks
Uint32 blocks
Definition: fat32.h:35
ofs
int ofs
Definition: fat32.c:158
mfat_dirent_t::cluster
Uint32 cluster
Definition: fat32.h:64
mfat_part_st::first_block
Uint32 first_block
Definition: fat32.h:33
mfat_part_st::clusters
Uint32 clusters
Definition: fat32.h:40
part
int part
Definition: fat32.c:74
mfat_rewind_stream
void mfat_rewind_stream(mfat_stream_t *p)
Definition: fat32.c:548
IS_MFAT_DIR
#define IS_MFAT_DIR(p)
Definition: fat32.h:26
buf
Uint8 buf[512]
Definition: fat32.c:155
mfat_read_stream
int mfat_read_stream(mfat_stream_t *p, void *buf, int size)
Definition: fat32.c:593