ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541d64.cpp
(Generate patch)

Comparing Frodo4/Src/1541d64.cpp (file contents):
Revision 1.2 by cebix, 2003-07-01T17:51:17Z vs.
Revision 1.6 by cebix, 2004-01-12T14:31:57Z

# Line 1 | Line 1
1   /*
2 < *  1541d64.cpp - 1541 emulation in .d64 file
2 > *  1541d64.cpp - 1541 emulation in disk image files (.d64/.x64/zipcode)
3   *
4   *  Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 + *  zipcode decoding routines (C) 1993-1997 Marko Mäkelä, Paul David Doherty
6   *
7   *  This program is free software; you can redistribute it and/or modify
8   *  it under the terms of the GNU General Public License as published by
# Line 19 | Line 20
20   */
21  
22   /*
23 < * Incompatibilities:
24 < * ------------------
25 < *
26 < *  - Only read accesses possible
26 < *  - Not all commands implemented
27 < *  - The .d64 error info is read, but unused
23 > *  Incompatibilities:
24 > *   - No support for relative files
25 > *   - Unimplemented commands: P
26 > *   - Impossible to implement: B-E, M-E
27   */
28  
29   #include "sysdeps.h"
# Line 32 | Line 31
31   #include "1541d64.h"
32   #include "IEC.h"
33   #include "Prefs.h"
34 + #include "C64.h"
35 + #include "main.h"
36  
37  
38   // Channel modes (IRC users listen up :-)
39   enum {
40          CHMOD_FREE,                     // Channel free
41          CHMOD_COMMAND,          // Command/error channel
42 <        CHMOD_DIRECTORY,        // Reading directory
43 <        CHMOD_FILE,                     // Sequential file open
44 <        CHMOD_DIRECT            // Direct buffer access ('#')
42 >        CHMOD_DIRECTORY,        // Reading directory, using large allocated buffer
43 >        CHMOD_FILE,                     // Sequential file open, using buffer in 1541 RAM
44 >        CHMOD_REL,                      // Relative file open, using buffer in 1541 RAM
45 >        CHMOD_DIRECT            // Direct buffer access ('#'), using buffer in 1541 RAM
46   };
47  
48 < // Access modes
48 > // Directory track
49 > const int DIR_TRACK = 18;
50 >
51 > // BAM structure
52   enum {
53 <        FMODE_READ, FMODE_WRITE, FMODE_APPEND
53 >        BAM_DIR_TRACK = 0,              // Track...
54 >        BAM_DIR_SECTOR = 1,             // ...and sector of first directory block (unused)
55 >        BAM_FMT_TYPE = 2,               // Format type
56 >        BAM_BITMAP = 4,                 // Sector allocation map
57 >        BAM_DISK_NAME = 144,    // Disk name
58 >        BAM_DISK_ID = 162,              // Disk ID
59 >        BAM_FMT_CHAR = 165              // Format characters
60   };
61  
62 < // File types
62 > // Directory structure
63   enum {
64 <        FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
64 >        DIR_NEXT_TRACK = 0,             // Track...
65 >        DIR_NEXT_SECTOR = 1,    // ... and sector of next directory block
66 >        DIR_ENTRIES = 2,                // Start of directory entries (8)
67 >
68 >        DE_TYPE = 0,                    // File type/flags
69 >        DE_TRACK = 1,                   // Track...
70 >        DE_SECTOR = 2,                  // ...and sector of first data block
71 >        DE_NAME = 3,                    // File name
72 >        DE_SIDE_TRACK = 19,             // Track...
73 >        DE_SIDE_SECTOR = 20,    // ...and sector of first side sector
74 >        DE_REC_LEN = 21,                // Record length
75 >        DE_OVR_TRACK = 26,              // Track...
76 >        DE_OVR_SECTOR = 27,             // ...and sector on overwrite (@)
77 >        DE_NUM_BLOCKS_L = 28,   // Number of blocks, LSB
78 >        DE_NUM_BLOCKS_H = 29,   // Number of blocks, MSB
79 >
80 >        SIZEOF_DE = 32                  // Size of directory entry
81   };
82  
83 < // Number of tracks/sectors
84 < const int NUM_TRACKS = 35;
85 < const int NUM_SECTORS = 683;
83 > // Interleave of directory and data blocks
84 > const int DIR_INTERLEAVE = 3;
85 > const int DATA_INTERLEAVE = 10;
86 >
87 > // Number of sectors per track, for all tracks
88 > const int num_sectors[41] = {
89 >        0,
90 >        21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
91 >        19,19,19,19,19,19,19,
92 >        18,18,18,18,18,18,
93 >        17,17,17,17,17,
94 >        17,17,17,17,17          // Tracks 36..40
95 > };
96 >
97 > // Accumulated number of sectors
98 > const int accum_num_sectors[41] = {
99 >        0,
100 >        0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
101 >        357,376,395,414,433,452,471,
102 >        490,508,526,544,562,580,
103 >        598,615,632,649,666,
104 >        683,700,717,734,751     // Tracks 36..40
105 > };
106  
107   // Prototypes
108 < static bool match(uint8 *p, uint8 *n);
108 > static bool match(const uint8 *p, int p_len, const uint8 *n);
109  
110  
111   /*
112 < *  Constructor: Prepare emulation, open .d64 file
112 > *  Constructor: Prepare emulation, open image file
113   */
114  
115 < D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec)
115 > ImageDrive::ImageDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL), bam(ram + 0x700), bam_dirty(false)
116   {
117 <        the_file = NULL;
118 <        ram = NULL;
119 <
73 <        Ready = false;
74 <        strcpy(orig_d64_name, filepath);
75 <        for (int i=0; i<=14; i++) {
76 <                chan_mode[i] = CHMOD_FREE;
77 <                chan_buf[i] = NULL;
117 >        for (int i=0; i<18; i++) {
118 >                ch[i].mode = CHMOD_FREE;
119 >                ch[i].buf = NULL;
120          }
121 <        chan_mode[15] = CHMOD_COMMAND;
121 >        ch[15].mode = CHMOD_COMMAND;
122  
123 <        // Open .d64 file
82 <        open_close_d64_file(filepath);
83 <        if (the_file != NULL) {
123 >        Reset();
124  
125 <                // Allocate 1541 RAM
126 <                ram = new uint8[0x800];
87 <                bam = (BAM *)(ram + 0x700);
88 <
89 <                Reset();
125 >        // Open image file
126 >        if (change_image(filepath))
127                  Ready = true;
91        }
128   }
129  
130  
# Line 96 | Line 132 | D64Drive::D64Drive(IEC *iec, char *filep
132   *  Destructor
133   */
134  
135 < D64Drive::~D64Drive()
135 > ImageDrive::~ImageDrive()
136   {
137 <        // Close .d64 file
102 <        open_close_d64_file("");
103 <
104 <        delete[] ram;
105 <        Ready = false;
137 >        close_image();
138   }
139  
140  
141   /*
142 < *  Open/close the .d64 file
142 > *  Close the image file
143   */
144  
145 < void D64Drive::open_close_d64_file(char *d64name)
145 > void ImageDrive::close_image(void)
146   {
147 <        long size;
116 <        uint8 magic[4];
117 <
118 <        // Close old .d64, if open
119 <        if (the_file != NULL) {
147 >        if (the_file) {
148                  close_all_channels();
149 +                if (bam_dirty) {
150 +                        write_sector(DIR_TRACK, 0, bam);
151 +                        bam_dirty = false;
152 +                }
153                  fclose(the_file);
154                  the_file = NULL;
155          }
156 + }
157  
125        // Open new .d64 file
126        if (d64name[0]) {
127                if ((the_file = fopen(d64name, "rb")) != NULL) {
128
129                        // Check length
130                        fseek(the_file, 0, SEEK_END);
131                        if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
132                                fclose(the_file);
133                                the_file = NULL;
134                                return;
135                        }
158  
159 <                        // x64 image?
160 <                        rewind(the_file);
161 <                        fread(&magic, 4, 1, the_file);
140 <                        if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
141 <                                image_header = 64;
142 <                        else
143 <                                image_header = 0;
159 > /*
160 > *  Open the image file
161 > */
162  
163 <                        // Preset error info (all sectors no error)
164 <                        memset(error_info, 1, NUM_SECTORS);
163 > bool ImageDrive::change_image(const char *path)
164 > {
165 >        // Close old image file
166 >        close_image();
167  
168 <                        // Load sector error info from .d64 file, if present
169 <                        if (!image_header && size == NUM_SECTORS * 257) {
170 <                                fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
171 <                                fread(&error_info, NUM_SECTORS, 1, the_file);
172 <                        }
173 <                }
168 >        // Open new image file (try write access first, then read-only)
169 >        write_protected = false;
170 >        the_file = open_image_file(path, true);
171 >        if (the_file == NULL) {
172 >                write_protected = true;
173 >                the_file = open_image_file(path, false);
174          }
175 +        if (the_file) {
176 +
177 +                // Determine file type and fill in image_file_desc structure
178 +                if (!parse_image_file(the_file, desc)) {
179 +                        fclose(the_file);
180 +                        the_file = false;
181 +                        return false;
182 +                }
183 +
184 +                // Read BAM
185 +                read_sector(DIR_TRACK, 0, bam);
186 +                bam_dirty = false;
187 +                return true;
188 +        } else
189 +                return false;
190   }
191  
192  
# Line 159 | Line 194 | void D64Drive::open_close_d64_file(char
194   *  Open channel
195   */
196  
197 < uint8 D64Drive::Open(int channel, char *filename)
197 > uint8 ImageDrive::Open(int channel, const uint8 *name, int name_len)
198   {
199          set_error(ERR_OK);
200  
201          // Channel 15: execute file name as command
202          if (channel == 15) {
203 <                execute_command(filename);
203 >                execute_cmd(name, name_len);
204                  return ST_OK;
205          }
206  
207 <        if (chan_mode[channel] != CHMOD_FREE) {
207 >        if (ch[channel].mode != CHMOD_FREE) {
208                  set_error(ERR_NOCHANNEL);
209                  return ST_OK;
210          }
211  
212 <        if (filename[0] == '$')
212 >        if (name[0] == '$')
213                  if (channel)
214 <                        return open_file_ts(channel, 18, 0);
214 >                        return open_file_ts(channel, DIR_TRACK, 0);
215                  else
216 <                        return open_directory(filename+1);
216 >                        return open_directory(name + 1, name_len - 1);
217  
218 <        if (filename[0] == '#')
219 <                return open_direct(channel, filename);
218 >        if (name[0] == '#')
219 >                return open_direct(channel, name);
220  
221 <        return open_file(channel, filename);
221 >        return open_file(channel, name, name_len);
222   }
223  
224  
# Line 191 | Line 226 | uint8 D64Drive::Open(int channel, char *
226   *  Open file
227   */
228  
229 < uint8 D64Drive::open_file(int channel, char *filename)
229 > uint8 ImageDrive::open_file(int channel, const uint8 *name, int name_len)
230   {
231 <        char plainname[256];
232 <        int filemode = FMODE_READ;
233 <        int filetype = FTYPE_PRG;
234 <        int track, sector;
235 <
236 <        convert_filename(filename, plainname, &filemode, &filetype);
237 <
238 <        // Channel 0 is READ PRG, channel 1 is WRITE PRG
239 <        if (!channel) {
240 <                filemode = FMODE_READ;
241 <                filetype = FTYPE_PRG;
242 <        }
243 <        if (channel == 1) {
244 <                filemode = FMODE_WRITE;
245 <                filetype = FTYPE_PRG;
231 >        uint8 plain_name[NAMEBUF_LENGTH];
232 >        int plain_name_len;
233 >        int mode = FMODE_READ;
234 >        int type = FTYPE_DEL;
235 >        int rec_len = 0;
236 >        parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len);
237 >        if (plain_name_len > 16)
238 >                plain_name_len = 16;
239 >
240 >        // Channel 0 is READ, channel 1 is WRITE
241 >        if (channel == 0 || channel == 1) {
242 >                mode = channel ? FMODE_WRITE : FMODE_READ;
243 >                if (type == FTYPE_DEL)
244 >                        type = FTYPE_PRG;
245 >        }
246 >
247 >        ch[channel].writing = (mode == FMODE_WRITE || mode == FMODE_APPEND);
248 >
249 >        // Wildcards are only allowed on reading
250 >        if (ch[channel].writing && (strchr((const char *)plain_name, '*') || strchr((const char *)plain_name, '?'))) {
251 >                set_error(ERR_SYNTAX33);
252 >                return ST_OK;
253          }
254  
255 <        // Allow only read accesses
256 <        if (filemode != FMODE_READ) {
255 >        // Check for write-protection if writing
256 >        if (ch[channel].writing && write_protected) {
257                  set_error(ERR_WRITEPROTECT);
258                  return ST_OK;
259          }
260  
261 <        // Find file in directory and open it
262 <        if (find_file(plainname, &track, &sector))
263 <                return open_file_ts(channel, track, sector);
264 <        else
265 <                set_error(ERR_FILENOTFOUND);
261 >        // Relative files are not supported
262 >        if (type == FTYPE_REL) {
263 >                set_error(ERR_UNIMPLEMENTED);
264 >                return ST_OK;
265 >        }
266  
267 <        return ST_OK;
268 < }
267 >        // Find file in directory
268 >        int dir_track, dir_sector, entry;
269 >        if (find_first_file(plain_name, plain_name_len, dir_track, dir_sector, entry)) {
270  
271 +                // File exists
272 +                ch[channel].dir_track = dir_track;
273 +                ch[channel].dir_sector = dir_sector;
274 +                ch[channel].entry = entry;
275 +                uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
276  
277 < /*
278 < *  Analyze file name, get access mode and type
279 < */
277 >                // Get file type from existing file if not specified in file name
278 >                if (type == FTYPE_DEL)
279 >                        type = de[DE_TYPE] & 7;
280  
281 < void D64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype)
234 < {
235 <        char *p;
281 >                if ((de[DE_TYPE] & 7) != type) {
282  
283 <        // Search for ':', p points to first character after ':'
284 <        if ((p = strchr(srcname, ':')) != NULL)
239 <                p++;
240 <        else
241 <                p = srcname;
283 >                        // File type doesn't match
284 >                        set_error(ERR_FILETYPE);
285  
286 <        // Remaining string -> destname
244 <        strncpy(destname, p, NAMEBUF_LENGTH);
286 >                } else if (mode == FMODE_WRITE) {
287  
288 <        // Search for ','
247 <        p = destname;
248 <        while (*p && (*p != ',')) p++;
288 >                        if (name[0] == '@') {
289  
290 <        // Look for mode parameters seperated by ','
291 <        p = destname;
252 <        while ((p = strchr(p, ',')) != NULL) {
290 >                                // Open old file for overwriting (save-replace)
291 >                                return create_file(channel, plain_name, plain_name_len, type, true);
292  
293 <                // Cut string after the first ','
255 <                *p++ = 0;
293 >                        } else {
294  
295 <                switch (*p) {
296 <                        case 'P':
297 <                                *filetype = FTYPE_PRG;
260 <                                break;
261 <                        case 'S':
262 <                                *filetype = FTYPE_SEQ;
263 <                                break;
264 <                        case 'U':
265 <                                *filetype = FTYPE_USR;
266 <                                break;
267 <                        case 'L':
268 <                                *filetype = FTYPE_REL;
269 <                                break;
270 <                        case 'R':
271 <                                *filemode = FMODE_READ;
272 <                                break;
273 <                        case 'W':
274 <                                *filemode = FMODE_WRITE;
275 <                                break;
276 <                        case 'A':
277 <                                *filemode = FMODE_APPEND;
278 <                                break;
279 <                }
280 <        }
281 < }
295 >                                // File to be written already exists, error
296 >                                set_error(ERR_FILEEXISTS);
297 >                        }
298  
299 +                } else if (mode == FMODE_APPEND) {
300  
301 < /*
302 < *  Search file in directory, find first track and sector
286 < *  false: not found, true: found
287 < */
301 >                        // Open old file for appending
302 >                        open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]);
303  
304 < bool D64Drive::find_file(char *filename, int *track, int *sector)
305 < {
306 <        int i, j;
307 <        uint8 *p, *q;
308 <        DirEntry *de;
304 >                        // Seek to end of file
305 >                        int track = 0, sector = 0, num_blocks = 0;
306 >                        while (ch[channel].buf[0]) {
307 >                                if (!read_sector(track = ch[channel].buf[0], sector = ch[channel].buf[1], ch[channel].buf))
308 >                                        return ST_OK;
309 >                                num_blocks++;
310 >                        }
311  
312 <        // Scan all directory blocks
313 <        dir.next_track = bam->dir_track;
314 <        dir.next_sector = bam->dir_sector;
312 >                        // Change channel mode to writing, adjust buffer pointer
313 >                        ch[channel].writing = true;
314 >                        ch[channel].buf_len = ch[channel].buf[1] + 1;
315 >                        ch[channel].buf_ptr = ch[channel].buf + ch[channel].buf_len;
316 >                        ch[channel].track = track;
317 >                        ch[channel].sector = sector;
318 >                        ch[channel].num_blocks = num_blocks;
319 >
320 >                } else if (mode == FMODE_M) {
321 >
322 >                        // Open old file for reading, even if it is not closed
323 >                        return open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]);
324 >
325 >                } else {
326 >
327 >                        // Open old file for reading, error if file is open
328 >                        if (de[DE_TYPE] & 0x80)
329 >                                return open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]);
330 >                        else
331 >                                set_error(ERR_WRITEFILEOPEN);
332 >                }
333  
334 <        while (dir.next_track) {
300 <                if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
301 <                        return false;
334 >        } else {
335  
336 <                // Scan all 8 entries of a block
337 <                for (j=0; j<8; j++) {
338 <                        de = &dir.entry[j];
339 <                        *track = de->track;
307 <                        *sector = de->sector;
308 <
309 <                        if (de->type) {
310 <                                p = (uint8 *)filename;
311 <                                q = de->name;
312 <                                for (i=0; i<16 && *p; i++, p++, q++) {
313 <                                        if (*p == '*')  // Wildcard '*' matches all following characters
314 <                                                return true;
315 <                                        if (*p != *q) {
316 <                                                if (*p != '?') goto next_entry; // Wildcard '?' matches single character
317 <                                                if (*q == 0xa0) goto next_entry;
318 <                                        }
319 <                                }
336 >                // File doesn't exist
337 >                // Set file type to SEQ if not specified in file name
338 >                if (type == FTYPE_DEL)
339 >                        type = FTYPE_SEQ;
340  
341 <                                if (i == 16 || *q == 0xa0)
322 <                                        return true;
323 <                        }
324 < next_entry: ;
325 <                }
326 <        }
341 >                if (mode == FMODE_WRITE) {
342  
343 <        return false;
343 >                        // Create new file for writing
344 >                        return create_file(channel, plain_name, plain_name_len, type);
345 >
346 >                } else
347 >                        set_error(ERR_FILENOTFOUND);
348 >        }
349 >        return ST_OK;
350   }
351  
352  
353   /*
354 < *  Open file given track/sector of first block
354 > *  Open channel for reading from file given track/sector of first block
355   */
356  
357 < uint8 D64Drive::open_file_ts(int channel, int track, int sector)
357 > uint8 ImageDrive::open_file_ts(int channel, int track, int sector)
358   {
359 <        chan_buf[channel] = new uint8[256];
360 <        chan_mode[channel] = CHMOD_FILE;
359 >        // Allocate buffer and set channel mode
360 >        int buf = alloc_buffer(-1);
361 >        if (buf == -1) {
362 >                set_error(ERR_NOCHANNEL);
363 >                return ST_OK;
364 >        }
365 >        ch[channel].buf_num = buf;
366 >        ch[channel].buf = ram + 0x300 + buf * 0x100;
367 >        ch[channel].mode = CHMOD_FILE;
368  
369          // On the next call to Read, the first block will be read
370 <        chan_buf[channel][0] = track;
371 <        chan_buf[channel][1] = sector;
372 <        buf_len[channel] = 0;
370 >        ch[channel].buf[0] = track;
371 >        ch[channel].buf[1] = sector;
372 >        ch[channel].buf_len = 0;
373  
374          return ST_OK;
375   }
376  
377  
378   /*
379 < *  Prepare directory as BASIC program (channel 0)
379 > *  Create file and open channel for writing to file
380   */
381  
382 < const char type_char_1[] = "DSPUREERSELQGRL?";
355 < const char type_char_2[] = "EERSELQGRL??????";
356 < const char type_char_3[] = "LQGRL???????????";
357 <
358 < // Return true if name 'n' matches pattern 'p'
359 < static bool match(uint8 *p, uint8 *n)
382 > uint8 ImageDrive::create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite)
383   {
384 <        if (!*p)                // Null pattern matches everything
385 <                return true;
384 >        // Allocate buffer
385 >        int buf = alloc_buffer(-1);
386 >        if (buf == -1) {
387 >                set_error(ERR_NOCHANNEL);
388 >                return ST_OK;
389 >        }
390 >        ch[channel].buf_num = buf;
391 >        ch[channel].buf = ram + 0x300 + buf * 0x100;
392  
393 <        do {
394 <                if (*p == '*')  // Wildcard '*' matches all following characters
395 <                        return true;
396 <                if ((*p != *n) && (*p != '?'))  // Wildcard '?' matches single character
397 <                        return false;
398 <                p++; n++;
399 <        } while (*p);
393 >        // Allocate new directory entry if not overwriting
394 >        if (!overwrite) {
395 >                if (!alloc_dir_entry(ch[channel].dir_track, ch[channel].dir_sector, ch[channel].entry)) {
396 >                        free_buffer(buf);
397 >                        return ST_OK;
398 >                }
399 >        }
400 >        uint8 *de = dir + DIR_ENTRIES + ch[channel].entry * SIZEOF_DE;
401  
402 <        return *n == 0xa0;
402 >        // Allocate first data block
403 >        ch[channel].track = DIR_TRACK - 1;
404 >        ch[channel].sector = -DATA_INTERLEAVE;
405 >        if (!alloc_next_block(ch[channel].track, ch[channel].sector, DATA_INTERLEAVE)) {
406 >                free_buffer(buf);
407 >                return ST_OK;
408 >        }
409 >        ch[channel].num_blocks = 1;
410 >
411 >        // Write directory entry
412 >        memset(de, 0, SIZEOF_DE);
413 >        de[DE_TYPE] = type;             // bit 7 not set -> open file
414 >        if (overwrite) {
415 >                de[DE_OVR_TRACK] = ch[channel].track;
416 >                de[DE_OVR_SECTOR] = ch[channel].sector;
417 >        } else {
418 >                de[DE_TRACK] = ch[channel].track;
419 >                de[DE_SECTOR] = ch[channel].sector;
420 >        }
421 >        memset(de + DE_NAME, 0xa0, 16);
422 >        memcpy(de + DE_NAME, name, name_len);
423 >        write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir);
424 >
425 >        // Set channel descriptor
426 >        ch[channel].mode = CHMOD_FILE;
427 >        ch[channel].writing = true;
428 >        ch[channel].buf_ptr = ch[channel].buf + 2;
429 >        ch[channel].buf_len = 2;
430 >        return ST_OK;
431   }
432  
375 uint8 D64Drive::open_directory(char *pattern)
376 {
377        int i, j, n, m;
378        uint8 *p, *q;
379        DirEntry *de;
380        uint8 c;
381        char *tmppat;
433  
434 + /*
435 + *  Prepare directory as BASIC program (channel 0)
436 + */
437 +
438 + const char type_char_1[] = "DSPUREER";
439 + const char type_char_2[] = "EERSELQG";
440 + const char type_char_3[] = "LQGRL???";
441 +
442 + uint8 ImageDrive::open_directory(const uint8 *pattern, int pattern_len)
443 + {
444          // Special treatment for "$0"
445 <        if (pattern[0] == '0' && pattern[1] == 0)
446 <                pattern += 1;
445 >        if (pattern[0] == '0' && pattern_len == 1) {
446 >                pattern++;
447 >                pattern_len--;
448 >        }
449  
450          // Skip everything before the ':' in the pattern
451 <        if ((tmppat = strchr(pattern, ':')) != NULL)
452 <                pattern = tmppat + 1;
451 >        uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len);
452 >        if (t) {
453 >                t++;
454 >                pattern_len -= t - pattern;
455 >                pattern = t;
456 >        }
457  
458 <        p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
459 <        chan_mode[0] = CHMOD_DIRECTORY;
458 >        ch[0].mode = CHMOD_DIRECTORY;
459 >        uint8 *p = ch[0].buf_ptr = ch[0].buf = new uint8[8192];
460  
461 <        // Create directory title
461 >        // Create directory title with disk name, ID and format type
462          *p++ = 0x01;    // Load address $0401 (from PET days :-)
463          *p++ = 0x04;
464          *p++ = 0x01;    // Dummy line link
# Line 401 | Line 468 | uint8 D64Drive::open_directory(char *pat
468          *p++ = 0x12;    // RVS ON
469          *p++ = '\"';
470  
471 <        q = bam->disk_name;
472 <        for (i=0; i<23; i++) {
471 >        uint8 *q = bam + BAM_DISK_NAME;
472 >        for (int i=0; i<23; i++) {
473 >                int c;
474                  if ((c = *q++) == 0xa0)
475                          *p++ = ' ';             // Replace 0xa0 by space
476                  else
# Line 412 | Line 480 | uint8 D64Drive::open_directory(char *pat
480          *p++ = 0;
481  
482          // Scan all directory blocks
483 <        dir.next_track = bam->dir_track;
484 <        dir.next_sector = bam->dir_sector;
483 >        dir[DIR_NEXT_TRACK] = DIR_TRACK;
484 >        dir[DIR_NEXT_SECTOR] = 1;
485  
486 <        while (dir.next_track) {
487 <                if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
486 >        int num_dir_blocks = 0;
487 >        while (dir[DIR_NEXT_TRACK] && num_dir_blocks < num_sectors[DIR_TRACK]) {
488 >                if (!read_sector(dir[DIR_NEXT_TRACK], dir[DIR_NEXT_SECTOR], dir))
489                          return ST_OK;
490 +                num_dir_blocks++;
491  
492                  // Scan all 8 entries of a block
493 <                for (j=0; j<8; j++) {
494 <                        de = &dir.entry[j];
493 >                uint8 *de = dir + DIR_ENTRIES;
494 >                for (int j=0; j<8; j++, de+=SIZEOF_DE) {
495 >                        if (de[DE_TYPE] && (pattern_len == 0 || match(pattern, pattern_len, de + DE_NAME))) {
496  
497 <                        if (de->type && match((uint8 *)pattern, de->name)) {
498 <                                *p++ = 0x01; // Dummy line link
497 >                                // Dummy line link
498 >                                *p++ = 0x01;
499                                  *p++ = 0x01;
500  
501 <                                *p++ = de->num_blocks_l; // Line number
502 <                                *p++ = de->num_blocks_h;
501 >                                // Line number = number of blocks
502 >                                *p++ = de[DE_NUM_BLOCKS_L];
503 >                                *p++ = de[DE_NUM_BLOCKS_H];
504  
505 +                                // Appropriate number of spaces to align file names
506                                  *p++ = ' ';
507 <                                n = (de->num_blocks_h << 8) + de->num_blocks_l;
507 >                                int n = (de[DE_NUM_BLOCKS_H] << 8) + de[DE_NUM_BLOCKS_L];
508                                  if (n<10) *p++ = ' ';
509                                  if (n<100) *p++ = ' ';
510  
511 +                                // File name enclosed in quotes
512                                  *p++ = '\"';
513 <                                q = de->name;
514 <                                m = 0;
515 <                                for (i=0; i<16; i++) {
513 >                                q = de + DE_NAME;
514 >                                uint8 c;
515 >                                bool m = false;
516 >                                for (int i=0; i<16; i++) {
517                                          if ((c = *q++) == 0xa0) {
518                                                  if (m)
519 <                                                        *p++ = ' ';             // Replace all 0xa0 by spaces
519 >                                                        *p++ = ' ';                     // Replace all 0xa0 by spaces
520                                                  else
521 <                                                        m = *p++ = '\"';        // But the first by a '"'
521 >                                                        m = (*p++ = '\"');      // But the first by a '"'
522                                          } else
523                                                  *p++ = c;
524                                  }
# Line 452 | Line 527 | uint8 D64Drive::open_directory(char *pat
527                                  else
528                                          *p++ = '\"';                    // No 0xa0, then append a space
529  
530 <                                if (de->type & 0x80)
530 >                                // Open files are marked by '*'
531 >                                if (de[DE_TYPE] & 0x80)
532                                          *p++ = ' ';
533                                  else
534                                          *p++ = '*';
535  
536 <                                *p++ = type_char_1[de->type & 0x0f];
537 <                                *p++ = type_char_2[de->type & 0x0f];
538 <                                *p++ = type_char_3[de->type & 0x0f];
536 >                                // File type
537 >                                *p++ = type_char_1[de[DE_TYPE] & 7];
538 >                                *p++ = type_char_2[de[DE_TYPE] & 7];
539 >                                *p++ = type_char_3[de[DE_TYPE] & 7];
540  
541 <                                if (de->type & 0x40)
541 >                                // Protected files are marked by '<'
542 >                                if (de[DE_TYPE] & 0x40)
543                                          *p++ = '<';
544                                  else
545                                          *p++ = ' ';
546  
547 +                                // Appropriate number of spaces at the end
548                                  *p++ = ' ';
549                                  if (n >= 10) *p++ = ' ';
550                                  if (n >= 100) *p++ = ' ';
# Line 474 | Line 553 | uint8 D64Drive::open_directory(char *pat
553                  }
554          }
555  
556 <        // Final line
557 <        q = p;
558 <        for (i=0; i<29; i++)
559 <                *q++ = ' ';
560 <
561 <        n = 0;
483 <        for (i=0; i<35; i++)
484 <                n += bam->bitmap[i*4];
556 >        // Final line, count number of free blocks
557 >        int n = 0;
558 >        for (int i=1; i<=35; i++) {
559 >                if (i != DIR_TRACK)     // exclude track 18
560 >                        n += num_free_blocks(i);
561 >        }
562  
563          *p++ = 0x01;            // Dummy line link
564          *p++ = 0x01;
# Line 501 | Line 578 | uint8 D64Drive::open_directory(char *pat
578          *p++ = 'E';
579          *p++ = '.';
580  
581 <        p = q;
581 >        memset(p, ' ', 13);
582 >        p += 13;
583 >
584          *p++ = 0;
585          *p++ = 0;
586          *p++ = 0;
587  
588 <        buf_len[0] = p - chan_buf[0];
510 <
588 >        ch[0].buf_len = p - ch[0].buf;
589          return ST_OK;
590   }
591  
# Line 516 | Line 594 | uint8 D64Drive::open_directory(char *pat
594   *  Open channel for direct buffer access
595   */
596  
597 < uint8 D64Drive::open_direct(int channel, char *filename)
597 > uint8 ImageDrive::open_direct(int channel, const uint8 *name)
598   {
599          int buf = -1;
600  
601 <        if (filename[1] == 0)
601 >        if (name[1] == 0)
602                  buf = alloc_buffer(-1);
603          else
604 <                if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0))
605 <                        buf = alloc_buffer(filename[1] - '0');
604 >                if ((name[1] >= '0') && (name[1] <= '3') && (name[2] == 0))
605 >                        buf = alloc_buffer(name[1] - '0');
606  
607          if (buf == -1) {
608                  set_error(ERR_NOCHANNEL);
# Line 532 | Line 610 | uint8 D64Drive::open_direct(int channel,
610          }
611  
612          // The buffers are in the 1541 RAM at $300 and are 256 bytes each
613 <        chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8);
614 <        chan_mode[channel] = CHMOD_DIRECT;
615 <        chan_buf_num[channel] = buf;
613 >        ch[channel].mode = CHMOD_DIRECT;
614 >        ch[channel].buf = ram + 0x300 + buf * 0x100;
615 >        ch[channel].buf_num = buf;
616  
617          // Store actual buffer number in buffer
618 <        *chan_buf[channel] = buf + '0';
619 <        buf_len[channel] = 1;
618 >        ch[channel].buf[1] = buf + '0';
619 >        ch[channel].buf_len = 1;
620 >        ch[channel].buf_ptr = ch[channel].buf + 1;
621  
622          return ST_OK;
623   }
# Line 548 | Line 627 | uint8 D64Drive::open_direct(int channel,
627   *  Close channel
628   */
629  
630 < uint8 D64Drive::Close(int channel)
630 > uint8 ImageDrive::Close(int channel)
631   {
632 <        if (channel == 15) {
554 <                close_all_channels();
555 <                return ST_OK;
556 <        }
557 <
558 <        switch (chan_mode[channel]) {
632 >        switch (ch[channel].mode) {
633                  case CHMOD_FREE:
634                          break;
635  
636 +                case CHMOD_COMMAND:
637 +                        close_all_channels();
638 +                        break;
639 +
640                  case CHMOD_DIRECT:
641 <                        free_buffer(chan_buf_num[channel]);
642 <                        chan_buf[channel] = NULL;
643 <                        chan_mode[channel] = CHMOD_FREE;
641 >                        free_buffer(ch[channel].buf_num);
642 >                        ch[channel].buf = NULL;
643 >                        ch[channel].mode = CHMOD_FREE;
644 >                        break;
645 >
646 >                case CHMOD_FILE:
647 >                        if (ch[channel].writing) {
648 >
649 >                                // Current block empty? Then write CR character
650 >                                if (ch[channel].buf_len == 2) {
651 >                                        ch[channel].buf[2] = 0x0d;
652 >                                        ch[channel].buf_len++;
653 >                                }
654 >
655 >                                // Write last data block
656 >                                ch[channel].buf[0] = 0;
657 >                                ch[channel].buf[1] = ch[channel].buf_len - 1;
658 >                                if (!write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf))
659 >                                        goto free;
660 >
661 >                                // Close write file in directory
662 >                                read_sector(ch[channel].dir_track, ch[channel].dir_sector, dir);
663 >                                uint8 *de = dir + DIR_ENTRIES + ch[channel].entry * SIZEOF_DE;
664 >                                de[DE_TYPE] |= 0x80;
665 >                                de[DE_NUM_BLOCKS_L] = ch[channel].num_blocks & 0xff;
666 >                                de[DE_NUM_BLOCKS_H] = ch[channel].num_blocks >> 8;
667 >                                if (de[DE_OVR_TRACK]) {
668 >                                        // Overwriting, free old data blocks and set pointer to new ones
669 >                                        free_block_chain(de[DE_TRACK], de[DE_SECTOR]);
670 >                                        de[DE_TRACK] = de[DE_OVR_TRACK];
671 >                                        de[DE_SECTOR] = de[DE_OVR_SECTOR];
672 >                                        de[DE_OVR_TRACK] = de[DE_OVR_SECTOR] = 0;
673 >                                }
674 >                                write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir);
675 >                        }
676 > free:           free_buffer(ch[channel].buf_num);
677 >                        ch[channel].buf = NULL;
678 >                        ch[channel].mode = CHMOD_FREE;
679                          break;
680  
681 <                default:
682 <                        delete[] chan_buf[channel];
683 <                        chan_buf[channel] = NULL;
684 <                        chan_mode[channel] = CHMOD_FREE;
681 >                case CHMOD_DIRECTORY:
682 >                        delete[] ch[channel].buf;
683 >                        ch[channel].buf = NULL;
684 >                        ch[channel].mode = CHMOD_FREE;
685                          break;
686          }
687  
# Line 580 | Line 693 | uint8 D64Drive::Close(int channel)
693   *  Close all channels
694   */
695  
696 < void D64Drive::close_all_channels()
696 > void ImageDrive::close_all_channels()
697   {
698          for (int i=0; i<15; i++)
699                  Close(i);
700 +        Close(16);
701 +        Close(17);
702  
703          cmd_len = 0;
704   }
# Line 593 | Line 708 | void D64Drive::close_all_channels()
708   *  Read from channel
709   */
710  
711 < uint8 D64Drive::Read(int channel, uint8 *byte)
711 > uint8 ImageDrive::Read(int channel, uint8 &byte)
712   {
713 <        switch (chan_mode[channel]) {
713 >        switch (ch[channel].mode) {
714 >                case CHMOD_FREE:
715 >                        if (current_error == ERR_OK)
716 >                                set_error(ERR_FILENOTOPEN);
717 >                        break;
718 >
719                  case CHMOD_COMMAND:
720 <                        *byte = *error_ptr++;
720 >                        // Read error channel
721 >                        byte = *error_ptr++;
722                          if (--error_len)
723                                  return ST_OK;
724                          else {
# Line 607 | Line 728 | uint8 D64Drive::Read(int channel, uint8
728                          break;
729  
730                  case CHMOD_FILE:
731 +                        if (ch[channel].writing)
732 +                                return ST_READ_TIMEOUT;
733 +                        if (current_error != ERR_OK)
734 +                                return ST_READ_TIMEOUT;
735 +
736                          // Read next block if necessary
737 <                        if (chan_buf[channel][0] && !buf_len[channel]) {
738 <                                if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel]))
737 >                        if (ch[channel].buf_len == 0 && ch[channel].buf[0]) {
738 >                                if (!read_sector(ch[channel].buf[0], ch[channel].buf[1], ch[channel].buf))
739                                          return ST_READ_TIMEOUT;
740 <                                buf_ptr[channel] = chan_buf[channel] + 2;
740 >                                ch[channel].buf_ptr = ch[channel].buf + 2;
741  
742                                  // Determine block length
743 <                                buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1;
743 >                                ch[channel].buf_len = ch[channel].buf[0] ? 254 : ch[channel].buf[1] - 1;
744                          }
745  
746 <                        if (buf_len[channel] > 0) {
747 <                                *byte = *buf_ptr[channel]++;
748 <                                if (!--buf_len[channel] && !chan_buf[channel][0])
746 >                        if (ch[channel].buf_len > 0) {
747 >                                byte = *(ch[channel].buf_ptr)++;
748 >                                if (--(ch[channel].buf_len) == 0 && ch[channel].buf[0] == 0)
749                                          return ST_EOF;
750                                  else
751                                          return ST_OK;
# Line 629 | Line 755 | uint8 D64Drive::Read(int channel, uint8
755  
756                  case CHMOD_DIRECTORY:
757                  case CHMOD_DIRECT:
758 <                        if (buf_len[channel] > 0) {
759 <                                *byte = *buf_ptr[channel]++;
760 <                                if (--buf_len[channel])
758 >                        if (ch[channel].buf_len > 0) {
759 >                                byte = *(ch[channel].buf_ptr)++;
760 >                                if (--(ch[channel].buf_len))
761                                          return ST_OK;
762                                  else
763                                          return ST_EOF;
# Line 647 | Line 773 | uint8 D64Drive::Read(int channel, uint8
773   *  Write byte to channel
774   */
775  
776 < uint8 D64Drive::Write(int channel, uint8 byte, bool eoi)
776 > uint8 ImageDrive::Write(int channel, uint8 byte, bool eoi)
777   {
778 <        switch (chan_mode[channel]) {
778 >        switch (ch[channel].mode) {
779                  case CHMOD_FREE:
780 <                        set_error(ERR_FILENOTOPEN);
780 >                        if (current_error == ERR_OK)
781 >                                set_error(ERR_FILENOTOPEN);
782                          break;
783  
784                  case CHMOD_COMMAND:
785                          // Collect characters and execute command on EOI
786 <                        if (cmd_len >= 40)
786 >                        if (cmd_len > 58) {
787 >                                set_error(ERR_SYNTAX32);
788                                  return ST_TIMEOUT;
789 +                        }
790  
791 <                        cmd_buffer[cmd_len++] = byte;
791 >                        cmd_buf[cmd_len++] = byte;
792  
793                          if (eoi) {
794 <                                cmd_buffer[cmd_len++] = 0;
794 >                                execute_cmd(cmd_buf, cmd_len);
795                                  cmd_len = 0;
667                                execute_command(cmd_buffer);
796                          }
797                          return ST_OK;
798  
799                  case CHMOD_DIRECTORY:
800                          set_error(ERR_WRITEFILEOPEN);
801                          break;
802 +
803 +                case CHMOD_FILE:
804 +                        if (!ch[channel].writing)
805 +                                return ST_TIMEOUT;
806 +                        if (current_error != ERR_OK)
807 +                                return ST_TIMEOUT;
808 +
809 +                        // Buffer full?
810 +                        if (ch[channel].buf_len >= 256) {
811 +
812 +                                // Yes, allocate new block
813 +                                int track = ch[channel].track, sector = ch[channel].sector;
814 +                                if (!alloc_next_block(track, sector, DATA_INTERLEAVE))
815 +                                        return ST_TIMEOUT;
816 +                                ch[channel].num_blocks++;
817 +
818 +                                // Write buffer with link to new block
819 +                                ch[channel].buf[0] = track;
820 +                                ch[channel].buf[1] = sector;
821 +                                write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf);
822 +
823 +                                // Reset buffer
824 +                                ch[channel].buf_ptr = ch[channel].buf + 2;
825 +                                ch[channel].buf_len = 2;
826 +                                ch[channel].track = track;
827 +                                ch[channel].sector = sector;
828 +                        }
829 +                        *(ch[channel].buf_ptr)++ = byte;
830 +                        ch[channel].buf_len++;
831 +                        return ST_OK;
832 +
833 +                case CHMOD_DIRECT:
834 +                        if (ch[channel].buf_len < 256) {
835 +                                *(ch[channel].buf_ptr)++ = byte;
836 +                                ch[channel].buf_len++;
837 +                                return ST_OK;
838 +                        } else
839 +                                return ST_TIMEOUT;
840 +                        break;
841          }
842          return ST_TIMEOUT;
843   }
844  
845  
846   /*
847 < *  Execute command string
847 > *  Reset drive
848   */
849  
850 < void D64Drive::execute_command(char *command)
850 > void ImageDrive::Reset(void)
851   {
852 <        uint16 adr;
686 <        int len;
852 >        close_all_channels();
853  
854 <        switch (command[0]) {
855 <                case 'B':
856 <                        if (command[1] != '-')
691 <                                set_error(ERR_SYNTAX30);
692 <                        else
693 <                                switch (command[2]) {
694 <                                        case 'R':
695 <                                                block_read_cmd(&command[3]);
696 <                                                break;
697 <
698 <                                        case 'P':
699 <                                                buffer_ptr_cmd(&command[3]);
700 <                                                break;
701 <
702 <                                        case 'A':
703 <                                        case 'F':
704 <                                        case 'W':
705 <                                                set_error(ERR_WRITEPROTECT);
706 <                                                break;
707 <
708 <                                        default:
709 <                                                set_error(ERR_SYNTAX30);
710 <                                                break;
711 <                                }
712 <                        break;
854 >        cmd_len = 0;
855 >        for (int i=0; i<4; i++)
856 >                buf_free[i] = true;
857  
858 <                case 'M':
859 <                        if (command[1] != '-')
860 <                                set_error(ERR_SYNTAX30);
861 <                        else
718 <                                switch (command[2]) {
719 <                                        case 'R':
720 <                                                adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
721 <                                                error_ptr = (char *)(ram + (adr & 0x07ff));
722 <                                                if (!(error_len = (uint8)command[5]))
723 <                                                        error_len = 1;
724 <                                                break;
725 <
726 <                                        case 'W':
727 <                                                adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
728 <                                                len = (uint8)command[5];
729 <                                                for (int i=0; i<len; i++)
730 <                                                        ram[adr+i] = (uint8)command[i+6];
731 <                                                break;
858 >        if (bam_dirty) {
859 >                write_sector(DIR_TRACK, 0, bam);
860 >                bam_dirty = false;
861 >        }
862  
863 <                                        default:
734 <                                                set_error(ERR_SYNTAX30);
735 <                                }
736 <                        break;
863 >        memset(ram, 0, sizeof(ram));
864  
865 <                case 'I':
866 <                        close_all_channels();
867 <                        read_sector(18, 0, (uint8 *)bam);
868 <                        set_error(ERR_OK);
869 <                        break;
865 >        read_sector(DIR_TRACK, 0, bam);
866 >
867 >        set_error(ERR_STARTUP);
868 > }
869 >
870 >
871 > /*
872 > *  Allocate floppy buffer
873 > *   -> Desired buffer number or -1
874 > *   <- Allocated buffer number or -1
875 > */
876  
877 <                case 'U':
878 <                        switch (command[1] & 0x0f) {
879 <                                case 1:         // U1/UA: Block-Read
880 <                                        block_read_cmd(&command[2]);
881 <                                        break;
882 <
883 <                                case 2:         // U2/UB: Block-Write
751 <                                        set_error(ERR_WRITEPROTECT);
752 <                                        break;
753 <
754 <                                case 10:        // U:/UJ: Reset
755 <                                        Reset();
756 <                                        break;
757 <
758 <                                default:
759 <                                        set_error(ERR_SYNTAX30);
760 <                                        break;
877 > int ImageDrive::alloc_buffer(int want)
878 > {
879 >        if (want == -1) {
880 >                for (want=3; want>=0; want--)
881 >                        if (buf_free[want]) {
882 >                                buf_free[want] = false;
883 >                                return want;
884                          }
885 <                        break;
885 >                return -1;
886 >        }
887  
888 <                case 'G':
889 <                        if (command[1] != ':')
890 <                                set_error(ERR_SYNTAX30);
891 <                        else
892 <                                chd64_cmd(&command[2]);
893 <                        break;
888 >        if (want < 4)
889 >                if (buf_free[want]) {
890 >                        buf_free[want] = false;
891 >                        return want;
892 >                } else
893 >                        return -1;
894 >        else
895 >                return -1;
896 > }
897  
771                case 'C':
772                case 'N':
773                case 'R':
774                case 'S':
775                case 'V':
776                        set_error(ERR_WRITEPROTECT);
777                        break;
898  
899 <                default:
900 <                        set_error(ERR_SYNTAX30);
901 <                        break;
899 > /*
900 > *  Free floppy buffer
901 > */
902 >
903 > void ImageDrive::free_buffer(int buf)
904 > {
905 >        buf_free[buf] = true;
906 > }
907 >
908 >
909 > /*
910 > *  Search file in directory, return directory track/sector and entry number
911 > *  false: not found, true: found
912 > */
913 >
914 > // Return true if name 'n' matches pattern 'p'
915 > static bool match(const uint8 *p, int p_len, const uint8 *n)
916 > {
917 >        if (p_len > 16)
918 >                p_len = 16;
919 >
920 >        int c = 0;
921 >        while (p_len-- > 0) {
922 >                if (*p == '*')  // Wildcard '*' matches all following characters
923 >                        return true;
924 >                if ((*p != *n) && (*p != '?'))  // Wildcard '?' matches single character
925 >                        return false;
926 >                p++; n++; c++;
927          }
928 +
929 +        return *n == 0xa0 || c == 16;
930 + }
931 +
932 + bool ImageDrive::find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont)
933 + {
934 +        // Counter to prevent cyclic directories from resulting in an infinite loop
935 +        int num_dir_blocks = 0;
936 +
937 +        // Pointer to current directory entry
938 +        uint8 *de = NULL;
939 +        if (cont)
940 +                de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
941 +        else {
942 +                dir[DIR_NEXT_TRACK] = DIR_TRACK;
943 +                dir[DIR_NEXT_SECTOR] = 1;
944 +                entry = 8;
945 +        }
946 +
947 +        while (num_dir_blocks < num_sectors[DIR_TRACK]) {
948 +
949 +                // Goto next entry
950 +                entry++; de += SIZEOF_DE;
951 +                if (entry >= 8) {
952 +
953 +                        // Read next directory block
954 +                        if (dir[DIR_NEXT_TRACK] == 0)
955 +                                return false;
956 +                        if (!read_sector(dir_track = dir[DIR_NEXT_TRACK], dir_sector = dir[DIR_NEXT_SECTOR], dir))
957 +                                return false;
958 +                        num_dir_blocks++;
959 +                        entry = 0;
960 +                        de = dir + DIR_ENTRIES;
961 +                }
962 +
963 +                // Does entry match pattern?
964 +                if (de[DE_TYPE] && match(pattern, pattern_len, de + DE_NAME))
965 +                        return true;
966 +        }
967 +        return false;
968 + }
969 +
970 + bool ImageDrive::find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry)
971 + {
972 +        return find_file(pattern, pattern_len, dir_track, dir_sector, entry, false);
973 + }
974 +
975 + bool ImageDrive::find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry)
976 + {
977 +        return find_file(pattern, pattern_len, dir_track, dir_sector, entry, true);
978   }
979  
980  
981   /*
982 < *  Execute B-R command
982 > *  Allocate new entry in directory, returns false on error (usually when
983 > *  all sectors of track 18 are allocated)
984 > *  The track/sector and entry numbers are returned
985   */
986  
987 < void D64Drive::block_read_cmd(char *command)
987 > bool ImageDrive::alloc_dir_entry(int &track, int &sector, int &entry)
988   {
989 <        int channel, drvnum, track, sector;
989 >        // First look for free entry in existing directory blocks
990 >        dir[DIR_NEXT_TRACK] = DIR_TRACK;
991 >        dir[DIR_NEXT_SECTOR] = 1;
992 >        while (dir[DIR_NEXT_TRACK]) {
993 >                if (!read_sector(track = dir[DIR_NEXT_TRACK], sector = dir[DIR_NEXT_SECTOR], dir))
994 >                        return false;
995  
996 <        if (parse_bcmd(command, &channel, &drvnum, &track, &sector)) {
997 <                if (chan_mode[channel] == CHMOD_DIRECT) {
998 <                        read_sector(track, sector, buf_ptr[channel] = chan_buf[channel]);
999 <                        buf_len[channel] = 256;
1000 <                        set_error(ERR_OK);
1001 <                } else
1002 <                        set_error(ERR_NOCHANNEL);
1003 <        } else
1004 <                set_error(ERR_SYNTAX30);
996 >                uint8 *de = dir + DIR_ENTRIES;
997 >                for (entry=0; entry<8; entry++, de+=SIZEOF_DE) {
998 >                        if (de[DE_TYPE] == 0)
999 >                                return true;
1000 >                }
1001 >        }
1002 >
1003 >        // No free entry found, allocate new directory block
1004 >        int last_track = track, last_sector = sector;
1005 >        if (!alloc_next_block(track, sector, DIR_INTERLEAVE))
1006 >                return false;
1007 >
1008 >        // Write link to new block to last block
1009 >        dir[DIR_NEXT_TRACK] = track;
1010 >        dir[DIR_NEXT_SECTOR] = sector;
1011 >        write_sector(last_track, last_sector, dir);
1012 >
1013 >        // Write new empty directory block and return first entry
1014 >        memset(dir, 0, 256);
1015 >        dir[DIR_NEXT_SECTOR] = 0xff;
1016 >        write_sector(track, sector, dir);
1017 >        entry = 0;
1018 >        return true;
1019 > }
1020 >
1021 > /*
1022 > *  Test if block is free in BAM (track/sector are not checked for validity)
1023 > */
1024 >
1025 > bool ImageDrive::is_block_free(int track, int sector)
1026 > {
1027 >        uint8 *p = bam + BAM_BITMAP + (track - 1) * 4;
1028 >        int byte = sector / 8 + 1;
1029 >        int bit = sector & 7;
1030 >        return p[byte] & (1 << bit);
1031   }
1032  
1033  
1034   /*
1035 < *  Execute B-P command
1035 > *  Get number of free blocks on a track
1036   */
1037  
1038 < void D64Drive::buffer_ptr_cmd(char *command)
1038 > int ImageDrive::num_free_blocks(int track)
1039   {
1040 <        int channel, pointer, i;
1040 >        return bam[BAM_BITMAP + (track - 1) * 4];
1041 > }
1042 >
1043 >
1044 > /*
1045 > *  Clear BAM, mark all blocks as free
1046 > */
1047 >
1048 > static void clear_bam(uint8 *bam)
1049 > {
1050 >        for (int track=1; track<=35; track++) {
1051 >                static const uint8 num2bits[8] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
1052 >                (bam + BAM_BITMAP)[(track-1) * 4 + 0] = num_sectors[track];
1053 >                (bam + BAM_BITMAP)[(track-1) * 4 + 1] = 0xff;
1054 >                (bam + BAM_BITMAP)[(track-1) * 4 + 2] = 0xff;
1055 >                (bam + BAM_BITMAP)[(track-1) * 4 + 3] = num2bits[num_sectors[track] - 16];
1056 >        }
1057 > }
1058 >
1059 >
1060 > /*
1061 > *  Allocate block in BAM, returns error code
1062 > */
1063 >
1064 > int ImageDrive::alloc_block(int track, int sector)
1065 > {
1066 >        if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track])
1067 >                return ERR_ILLEGALTS;
1068 >
1069 >        uint8 *p = bam + BAM_BITMAP + (track - 1) * 4;
1070 >        int byte = sector / 8 + 1;
1071 >        int bit = sector & 7;
1072 >
1073 >        // Block free?
1074 >        if (p[byte] & (1 << bit)) {
1075 >
1076 >                // Yes, allocate and decrement free block count
1077 >                p[byte] &= ~(1 << bit);
1078 >                p[0]--;
1079 >                bam_dirty = true;
1080 >                return ERR_OK;
1081  
814        if (parse_bcmd(command, &channel, &pointer, &i, &i)) {
815                if (chan_mode[channel] == CHMOD_DIRECT) {
816                        buf_ptr[channel] = chan_buf[channel] + pointer;
817                        buf_len[channel] = 256 - pointer;
818                        set_error(ERR_OK);
819                } else
820                        set_error(ERR_NOCHANNEL);
1082          } else
1083 <                set_error(ERR_SYNTAX30);
1083 >                return ERR_NOBLOCK;
1084   }
1085  
1086  
1087   /*
1088 < *  Parse block command parameters
828 < *  true: OK, false: error
1088 > *  Free block in BAM, returns error code
1089   */
1090  
1091 < bool D64Drive::parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4)
1091 > int ImageDrive::free_block(int track, int sector)
1092   {
1093 <        int i;
1093 >        if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track])
1094 >                return ERR_ILLEGALTS;
1095  
1096 <        if (*cmd == ':') cmd++;
1096 >        uint8 *p = bam + BAM_BITMAP + (track - 1) * 4;
1097 >        int byte = sector / 8 + 1;
1098 >        int bit = sector & 7;
1099  
1100 <        // Read four parameters separated by space, cursor right or comma
1101 <        while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
839 <        if (!*cmd) return false;
1100 >        // Block allocated?
1101 >        if (!(p[byte] & (1 << bit))) {
1102  
1103 <        i = 0;
1104 <        while (*cmd >= 0x30 && *cmd < 0x40) {
1105 <                i *= 10;
1106 <                i += *cmd++ & 0x0f;
1103 >                // Yes, free and increment free block count
1104 >                p[byte] |= (1 << bit);
1105 >                p[0]++;
1106 >                bam_dirty = true;
1107          }
1108 <        *arg1 = i & 0xff;
1108 >        return ERR_OK;
1109 > }
1110  
848        while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
849        if (!*cmd) return false;
1111  
1112 <        i = 0;
1113 <        while (*cmd >= 0x30 && *cmd < 0x40) {
1114 <                i *= 10;
1115 <                i += *cmd++ & 0x0f;
1112 > /*
1113 > *  Allocate chain of data blocks in BAM
1114 > */
1115 >
1116 > bool ImageDrive::alloc_block_chain(int track, int sector)
1117 > {
1118 >        uint8 buf[256];
1119 >        while (alloc_block(track, sector) == ERR_OK) {
1120 >                if (!read_sector(track, sector, buf))
1121 >                        return false;
1122 >                track = buf[0];
1123 >                sector = buf[1];
1124          }
1125 <        *arg2 = i & 0xff;
1125 >        return true;
1126 > }
1127  
858        while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
859        if (!*cmd) return false;
1128  
1129 <        i = 0;
1130 <        while (*cmd >= 0x30 && *cmd < 0x40) {
1131 <                i *= 10;
1132 <                i += *cmd++ & 0x0f;
1129 > /*
1130 > *  Free chain of data blocks in BAM
1131 > */
1132 >
1133 > bool ImageDrive::free_block_chain(int track, int sector)
1134 > {
1135 >        uint8 buf[256];
1136 >        while (free_block(track, sector) == ERR_OK) {
1137 >                if (!read_sector(track, sector, buf))
1138 >                        return false;
1139 >                track = buf[0];
1140 >                sector = buf[1];
1141          }
1142 <        *arg3 = i & 0xff;
1142 >        return true;
1143 > }
1144  
868        while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
869        if (!*cmd) return false;
1145  
1146 <        i = 0;
1147 <        while (*cmd >= 0x30 && *cmd < 0x40) {
1148 <                i *= 10;
1149 <                i += *cmd++ & 0x0f;
1146 > /*
1147 > *  Search and allocate next free block, returns false if no more blocks
1148 > *  are free (ERR_DISKFULL is also set in this case)
1149 > *  "track" and "sector" must be set to the block where the search should
1150 > *  begin
1151 > */
1152 >
1153 > bool ImageDrive::alloc_next_block(int &track, int &sector, int interleave)
1154 > {
1155 >        // Find track with free blocks
1156 >        bool side_changed = false;
1157 >        while (num_free_blocks(track) == 0) {
1158 >                if (track == DIR_TRACK) {       // Directory doesn't grow to other tracks
1159 > full:           track = sector = 0;
1160 >                        set_error(ERR_DISKFULL);
1161 >                        return false;
1162 >                } else if (track > DIR_TRACK) {
1163 >                        track++;
1164 >                        if (track > 35) {
1165 >                                if (!side_changed)
1166 >                                        side_changed = true;
1167 >                                else
1168 >                                        goto full;
1169 >                                track = DIR_TRACK - 1;
1170 >                                sector = 0;
1171 >                        }
1172 >                } else {
1173 >                        track--;
1174 >                        if (track < 1) {
1175 >                                if (!side_changed)
1176 >                                        side_changed = true;
1177 >                                else
1178 >                                        goto full;
1179 >                                track = DIR_TRACK + 1;
1180 >                                sector = 0;
1181 >                        }
1182 >                }
1183 >        }
1184 >
1185 >        // Find next free block on track
1186 >        int num = num_sectors[track];
1187 >        sector = sector + interleave;
1188 >        if (sector >= num) {
1189 >                sector -= num;
1190 >                if (sector)
1191 >                        sector--;
1192 >        }
1193 >        while (!is_block_free(track, sector)) {
1194 >                sector++;
1195 >                if (sector >= num_sectors[track]) {
1196 >                        sector = 0;
1197 >                        while (!is_block_free(track, sector)) {
1198 >                                sector++;
1199 >                                if (sector >= num_sectors[track]) {
1200 >                                        // Something is wrong: the BAM free block count for this
1201 >                                        // track was >0, but we found no free blocks
1202 >                                        track = sector = 0;
1203 >                                        set_error(ERR_DIRERROR);
1204 >                                        return false;
1205 >                                }
1206 >                        }
1207 >                }
1208          }
876        *arg4 = i & 0xff;
1209  
1210 +        alloc_block(track, sector);
1211          return true;
1212   }
1213  
1214  
1215   /*
1216 < *  Execute 'G' command
1216 > *  Sector reading/writing routines
1217   */
1218  
1219 < void D64Drive::chd64_cmd(char *d64name)
1219 > static long offset_from_ts(const image_file_desc &desc, int track, int sector)
1220 > {
1221 >        if ((track < 1) || (track > desc.num_tracks)
1222 >         || (sector < 0) || (sector >= num_sectors[track]))
1223 >                return -1;
1224 >
1225 >        return ((accum_num_sectors[track] + sector) << 8) + desc.header_size;
1226 > }
1227 >
1228 > // Get number of sectors per given track
1229 > int sectors_per_track(const image_file_desc &desc, int track)
1230   {
1231 <        char str[NAMEBUF_LENGTH];
1232 <        char *p = str;
1231 >        return num_sectors[track];
1232 > }
1233  
1234 <        // Convert .d64 file name
1235 <        for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*d64name++, false)); i++) ;
1234 > // Get reference to error info byte of given track/sector
1235 > uint8 &error_info_for_sector(image_file_desc &desc, int track, int sector)
1236 > {
1237 >        return desc.error_info[accum_num_sectors[track] + sector];
1238 > }
1239  
1240 <        close_all_channels();
1240 > static inline const uint8 &error_info_for_sector(const image_file_desc &desc, int track, int sector)
1241 > {
1242 >        return desc.error_info[accum_num_sectors[track] + sector];
1243 > }
1244 >
1245 > const int conv_job_error[16] = {
1246 >        ERR_OK,                         // 0 -> 00 OK
1247 >        ERR_OK,                         // 1 -> 00 OK
1248 >        ERR_READ20,                     // 2 -> 20 READ ERROR
1249 >        ERR_READ21,                     // 3 -> 21 READ ERROR
1250 >        ERR_READ22,                     // 4 -> 22 READ ERROR
1251 >        ERR_READ23,                     // 5 -> 23 READ ERROR
1252 >        ERR_READ24,                     // 6 -> 24 READ ERROR (undetected by 1541)
1253 >        ERR_WRITE25,            // 7 -> 25 WRITE ERROR
1254 >        ERR_WRITEPROTECT,       // 8 -> 26 WRITE PROTECT ON
1255 >        ERR_READ27,                     // 9 -> 27 READ ERROR
1256 >        ERR_WRITE28,            // 10 -> 28 WRITE ERROR
1257 >        ERR_DISKID,                     // 11 -> 29 DISK ID MISMATCH
1258 >        ERR_OK,                         // 12 -> 00 OK
1259 >        ERR_OK,                         // 13 -> 00 OK
1260 >        ERR_OK,                         // 14 -> 00 OK
1261 >        ERR_NOTREADY            // 15 -> 74 DRIVE NOT READY
1262 > };
1263 >
1264 > // Read sector, return error code
1265 > int read_sector(FILE *f, const image_file_desc &desc, int track, int sector, uint8 *buffer)
1266 > {
1267 >        // Convert track/sector to byte offset in file
1268 >        long offset = offset_from_ts(desc, track, sector);
1269 >        if (offset < 0)
1270 >                return ERR_ILLEGALTS;
1271 >
1272 >        if (f == NULL)
1273 >                return ERR_NOTREADY;
1274 >
1275 >        fseek(f, offset, SEEK_SET);
1276 >        if (fread(buffer, 1, 256, f) != 256)
1277 >                return ERR_READ22;
1278 >        else {
1279 >                unsigned int error = error_info_for_sector(desc, track, sector);
1280 >                return conv_job_error[error & 0x0f];
1281 >        }
1282 > }
1283  
1284 <        // G:. resets the .d64 file name to its original setting
1285 <        if (str[0] == '.' && str[1] == 0)
1286 <                open_close_d64_file(orig_d64_name);
1284 > // Write sector, return error code
1285 > int write_sector(FILE *f, const image_file_desc &desc, int track, int sector, uint8 *buffer)
1286 > {
1287 >        // Convert track/sector to byte offset in file
1288 >        long offset = offset_from_ts(desc, track, sector);
1289 >        if (offset < 0)
1290 >                return ERR_ILLEGALTS;
1291 >
1292 >        if (f == NULL)
1293 >                return ERR_NOTREADY;
1294 >
1295 >        fseek(f, offset, SEEK_SET);
1296 >        if (fwrite(buffer, 1, 256, f) != 256)
1297 >                return ERR_WRITE25;
1298          else
1299 <                open_close_d64_file(str);
1299 >                return ERR_OK;
1300 > }
1301  
1302 <        // Read BAM
1303 <        read_sector(18, 0, (uint8 *)bam);
1302 > // Read sector and set error message, returns false on error
1303 > bool ImageDrive::read_sector(int track, int sector, uint8 *buffer)
1304 > {
1305 >        int error = ::read_sector(the_file, desc, track, sector, buffer);
1306 >        if (error)
1307 >                set_error(error, track, sector);
1308 >        return error == ERR_OK;
1309   }
1310  
1311 + // Write sector and set error message, returns false on error
1312 + bool ImageDrive::write_sector(int track, int sector, uint8 *buffer)
1313 + {
1314 +        int error = ::write_sector(the_file, desc, track, sector, buffer);
1315 +        if (error)
1316 +                set_error(error, track, sector);
1317 +        return error == ERR_OK;
1318 + }
1319  
1320 < /*
1321 < *  Reset drive
1322 < */
1320 > // Write error info back to image file
1321 > void write_back_error_info(FILE *f, const image_file_desc &desc)
1322 > {
1323 >        if (desc.type == TYPE_D64 && desc.has_error_info) {
1324 >                int num_sectors = desc.num_tracks == 40 ? NUM_SECTORS_40 : NUM_SECTORS_35;
1325 >                fseek(f, num_sectors * 256, SEEK_SET);
1326 >                fwrite(desc.error_info, num_sectors, 1, f);
1327 >        }
1328 > }
1329  
1330 < void D64Drive::Reset(void)
1330 > // Format disk image
1331 > bool format_image(FILE *f, image_file_desc &desc, bool lowlevel, uint8 id1, uint8 id2, const uint8 *disk_name, int disk_name_len)
1332   {
1333 <        close_all_channels();
1333 >        uint8 p[256];
1334  
1335 <        read_sector(18, 0, (uint8 *)bam);
1335 >        if (lowlevel) {
1336  
1337 <        cmd_len = 0;
1338 <        for (int i=0; i<4; i++)
1339 <                buf_free[i] = true;
1337 >                // Fill buffer with 1541 empty sector pattern (4b 01 01 ...,
1338 >                // except on track 1 where it's 01 01 01 ...)
1339 >                memset(p, 1, 256);
1340  
1341 <        set_error(ERR_STARTUP);
1341 >                // Overwrite all blocks
1342 >                for (int track=1; track<=35; track++) {
1343 >                        if (track == 2)
1344 >                                p[0] = 0x4b;
1345 >                        for (int sector=0; sector<num_sectors[track]; sector++) {
1346 >                                if (write_sector(f, desc, track, sector, p) != ERR_OK)
1347 >                                        return false;
1348 >                        }
1349 >                }
1350 >
1351 >                // Clear and write error info
1352 >                memset(desc.error_info, 1, sizeof(desc.error_info));
1353 >                write_back_error_info(f, desc);
1354 >
1355 >                // Clear BAM
1356 >                memset(p, 0, 256);
1357 >
1358 >        } else {
1359 >
1360 >                // Read BAM
1361 >                if (read_sector(f, desc, DIR_TRACK, 0, p) != ERR_OK)
1362 >                        return false;
1363 >        }
1364 >
1365 >        // Create and write empty BAM
1366 >        p[BAM_DIR_TRACK] = DIR_TRACK;
1367 >        p[BAM_DIR_SECTOR] = 1;
1368 >        p[BAM_FMT_TYPE] = 'A';
1369 >        clear_bam(p);
1370 >        p[BAM_BITMAP + (DIR_TRACK - 1) * 4 + 0] -= 2;   // Allocate BAM and first directory block
1371 >        p[BAM_BITMAP + (DIR_TRACK - 1) * 4 + 1] &= 0xfc;
1372 >        memset(p + BAM_DISK_NAME, 0xa0, 27);
1373 >        if (disk_name_len > 16)
1374 >                disk_name_len = 16;
1375 >        memcpy(p + BAM_DISK_NAME, disk_name, disk_name_len);
1376 >        p[BAM_DISK_ID] = id1;
1377 >        p[BAM_DISK_ID + 1] = id2;
1378 >        p[BAM_FMT_CHAR] = '2';
1379 >        p[BAM_FMT_CHAR + 1] = 'A';
1380 >        if (write_sector(f, desc, DIR_TRACK, 0, p) != ERR_OK)
1381 >                return false;
1382 >
1383 >        // Create and write empty directory
1384 >        memset(p, 0, 256);
1385 >        p[1] = 255;
1386 >        return write_sector(f, desc, DIR_TRACK, 1, p) == ERR_OK;
1387   }
1388  
1389  
1390   /*
1391 < *  Allocate floppy buffer
927 < *   -> Desired buffer number or -1
928 < *   <- Allocated buffer number or -1
1391 > *  Execute drive commands
1392   */
1393  
1394 < int D64Drive::alloc_buffer(int want)
1394 > // BLOCK-READ:channel,0,track,sector
1395 > void ImageDrive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
1396   {
1397 <        if (want == -1) {
1398 <                for (want=3; want>=0; want--)
1399 <                        if (buf_free[want]) {
1400 <                                buf_free[want] = false;
1401 <                                return want;
1397 >        if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) {
1398 >                set_error(ERR_NOCHANNEL);
1399 >                return;
1400 >        }
1401 >        if (!read_sector(track, sector, ch[channel].buf))
1402 >                return;
1403 >        if (user_cmd) {
1404 >                ch[channel].buf_len = 256;
1405 >                ch[channel].buf_ptr = ch[channel].buf;
1406 >        } else {
1407 >                ch[channel].buf_len = ch[channel].buf[0];
1408 >                ch[channel].buf_ptr = ch[channel].buf + 1;
1409 >        }
1410 > }
1411 >
1412 > // BLOCK-WRITE:channel,0,track,sector
1413 > void ImageDrive::block_write_cmd(int channel, int track, int sector, bool user_cmd)
1414 > {
1415 >        if (write_protected) {
1416 >                set_error(ERR_WRITEPROTECT);
1417 >                return;
1418 >        }
1419 >        if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) {
1420 >                set_error(ERR_NOCHANNEL);
1421 >                return;
1422 >        }
1423 >        if (!user_cmd)
1424 >                ch[channel].buf[0] = ch[channel].buf_len ? ch[channel].buf_len - 1 : 1;
1425 >        if (!write_sector(track, sector, ch[channel].buf))
1426 >                return;
1427 >        if (!user_cmd) {
1428 >                ch[channel].buf_len = 1;
1429 >                ch[channel].buf_ptr = ch[channel].buf + 1;
1430 >        }
1431 > }
1432 >
1433 > // BLOCK-ALLOCATE:0,track,sector
1434 > void ImageDrive::block_allocate_cmd(int track, int sector)
1435 > {
1436 >        int err = alloc_block(track, sector);
1437 >        if (err) {
1438 >                if (err == ERR_NOBLOCK) {
1439 >                        // Find next free block and return its track/sector address in the
1440 >                        // error message (only look on higher tracks)
1441 >                        for (;;) {
1442 >                                sector++;
1443 >                                if (sector >= num_sectors[track]) {
1444 >                                        track++;
1445 >                                        sector = 0;
1446 >                                        if (track > 35) {
1447 >                                                set_error(ERR_NOBLOCK, 0, 0);
1448 >                                                return;
1449 >                                        }
1450 >                                }
1451 >                                if (is_block_free(track, sector)) {
1452 >                                        set_error(ERR_NOBLOCK, track, sector);
1453 >                                        return;
1454 >                                }
1455                          }
1456 <                return -1;
1456 >                } else
1457 >                        set_error(err, track, sector);
1458          }
1459 + }
1460  
1461 <        if (want < 4)
1462 <                if (buf_free[want]) {
1463 <                        buf_free[want] = false;
1464 <                        return want;
1461 > // BLOCK-FREE:0,track,sector
1462 > void ImageDrive::block_free_cmd(int track, int sector)
1463 > {
1464 >        int err = free_block(track, sector);
1465 >        if (err)
1466 >                set_error(err, track, sector);
1467 > }
1468 >
1469 > // BUFFER-POINTER:channel,pos
1470 > void ImageDrive::buffer_pointer_cmd(int channel, int pos)
1471 > {
1472 >        if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) {
1473 >                set_error(ERR_NOCHANNEL);
1474 >                return;
1475 >        }
1476 >        ch[channel].buf_ptr = ch[channel].buf + pos;
1477 >        ch[channel].buf_len = 256 - pos;
1478 > }
1479 >
1480 > // M-R<adr low><adr high>[<number>]
1481 > void ImageDrive::mem_read_cmd(uint16 adr, uint8 len)
1482 > {
1483 >        error_len = len;
1484 >        if (adr >= 0x300 && adr < 0x1000) {
1485 >                // Read from RAM
1486 >                error_ptr = (char *)ram + (adr & 0x7ff);
1487 >        } else if (adr >= 0xc000) {
1488 >                // Read from ROM
1489 >                error_ptr = (char *)(TheC64->ROM1541) + (adr - 0xc000);
1490 >        } else {
1491 >                unsupp_cmd();
1492 >                memset(error_buf, 0, len);
1493 >                error_ptr = error_buf;
1494 >        }
1495 > }
1496 >
1497 > // M-W<adr low><adr high><number><data...>
1498 > void ImageDrive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
1499 > {
1500 >        while (len) {
1501 >                if (adr >= 0x300 && adr < 0x1000) {
1502 >                        // Write to RAM
1503 >                        ram[adr & 0x7ff] = *p;
1504 >                } else if (adr < 0xc000) {
1505 >                        unsupp_cmd();
1506 >                        return;
1507 >                }
1508 >                len--; adr++; p++;
1509 >        }
1510 > }
1511 >
1512 > //   COPY:new=file1,file2,...
1513 > //        ^   ^
1514 > // new_file   old_files
1515 > void ImageDrive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len)
1516 > {
1517 >        // Check if destination file is already present
1518 >        int dir_track, dir_sector, entry;
1519 >        if (find_first_file(new_file, new_file_len, dir_track, dir_sector, entry)) {
1520 >                set_error(ERR_FILEEXISTS);
1521 >                return;
1522 >        }
1523 >
1524 >        // Loop for all source files
1525 >        bool first = true;
1526 >        while (old_files_len > 0) {
1527 >                uint8 *comma = (uint8 *)memchr(old_files, ',', old_files_len);
1528 >                int name_len = comma ? comma - old_files : old_files_len;
1529 >
1530 >                // Check if source file is present
1531 >                if (!find_first_file(old_files, name_len, dir_track, dir_sector, entry)) {
1532 >                        set_error(ERR_FILENOTFOUND);
1533 >                        Close(17);
1534 >                        return;
1535 >                }
1536 >                uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1537 >                uint8 type = de[DE_TYPE] & 7, track = de[DE_TRACK], sector = de[DE_SECTOR];
1538 >
1539 >                // If this is the first source file, open internal write channel for destination file
1540 >                if (first) {
1541 >                        create_file(17, new_file, new_file_len, type, false);
1542 >                        if (ch[17].mode == CHMOD_FREE)
1543 >                                return;
1544 >                        first = false;
1545 >                }
1546 >
1547 >                // Open internal read channel for source file
1548 >                open_file_ts(16, track, sector);
1549 >                if (ch[16].mode == CHMOD_FREE) {
1550 >                        Close(17);
1551 >                        return;
1552 >                }
1553 >
1554 >                // Copy file
1555 >                uint8 byte, st;
1556 >                do {
1557 >                        st = Read(16, byte);
1558 >                        Write(17, byte, false);
1559 >                } while (st == ST_OK);
1560 >                Close(16);
1561 >                if (st != ST_EOF) {
1562 >                        Close(17);
1563 >                        return;
1564 >                }
1565 >
1566 >                if (comma) {
1567 >                        old_files_len -= name_len + 1;
1568 >                        old_files = comma + 1;
1569                  } else
1570 <                        return -1;
1571 <        else
1572 <                return -1;
1570 >                        old_files_len = 0;
1571 >        }
1572 >        Close(17);
1573 > }
1574 >
1575 > // RENAME:new=old
1576 > //        ^   ^
1577 > // new_file   old_file
1578 > void ImageDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
1579 > {
1580 >        // Check if destination file is already present
1581 >        int dir_track, dir_sector, entry;
1582 >        if (find_first_file(new_file, new_file_len, dir_track, dir_sector, entry)) {
1583 >                set_error(ERR_FILEEXISTS);
1584 >                return;
1585 >        }
1586 >
1587 >        // Check if source file is present
1588 >        if (!find_first_file(old_file, old_file_len, dir_track, dir_sector, entry)) {
1589 >                set_error(ERR_FILENOTFOUND);
1590 >                return;
1591 >        }
1592 >
1593 >        // Check for write-protection
1594 >        if (write_protected) {
1595 >                set_error(ERR_WRITEPROTECT);
1596 >                return;
1597 >        }
1598 >
1599 >        // Rename file in directory entry
1600 >        uint8 *p = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1601 >        memset(p + DE_NAME, 0xa0, 16);
1602 >        memcpy(p + DE_NAME, new_file, new_file_len);
1603 >        write_sector(dir_track, dir_sector, dir);
1604 > }
1605 >
1606 > // SCRATCH:file1,file2,...
1607 > //         ^
1608 > //         files
1609 > void ImageDrive::scratch_cmd(const uint8 *files, int files_len)
1610 > {
1611 >        // Check for write-protection
1612 >        if (write_protected) {
1613 >                set_error(ERR_WRITEPROTECT);
1614 >                return;
1615 >        }
1616 >
1617 >        // Loop for all files
1618 >        int num_files = 0;
1619 >        while (files_len > 0) {
1620 >                uint8 *comma = (uint8 *)memchr(files, ',', files_len);
1621 >                int name_len = comma ? comma - files : files_len;
1622 >
1623 >                int dir_track, dir_sector, entry;
1624 >                if (find_first_file(files, name_len, dir_track, dir_sector, entry)) {
1625 >                        do {
1626 >                                uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1627 >
1628 >                                // File protected? Then skip
1629 >                                if (de[DE_TYPE] & 0x40)
1630 >                                        continue;
1631 >
1632 >                                // Free allocated data blocks and side sectors
1633 >                                free_block_chain(de[DE_TRACK], de[DE_SECTOR]);
1634 >                                free_block_chain(de[DE_SIDE_TRACK], de[DE_SIDE_SECTOR]);
1635 >
1636 >                                // Clear file type
1637 >                                de[DE_TYPE] = 0;
1638 >
1639 >                                // Write directory block back
1640 >                                write_sector(dir_track, dir_sector, dir);
1641 >                                num_files++;
1642 >                        } while (find_next_file(files, name_len, dir_track, dir_sector, entry));
1643 >                }
1644 >
1645 >                if (comma) {
1646 >                        files_len -= name_len + 1;
1647 >                        files = comma + 1;
1648 >                } else
1649 >                        files_len = 0;
1650 >        }
1651 >
1652 >        // Report number of files scratched
1653 >        set_error(ERR_SCRATCHED, num_files);
1654 > }
1655 >
1656 > // INITIALIZE
1657 > void ImageDrive::initialize_cmd(void)
1658 > {
1659 >        // Close all channels and re-read BAM
1660 >        close_all_channels();
1661 >        if (bam_dirty) {
1662 >                write_sector(DIR_TRACK, 0, bam);
1663 >                bam_dirty = false;
1664 >        }
1665 >        read_sector(DIR_TRACK, 0, bam);
1666 > }
1667 >
1668 > // NEW:name,id
1669 > //     ^   ^
1670 > //  name   comma (or NULL)
1671 > void ImageDrive::new_cmd(const uint8 *name, int name_len, const uint8 *comma)
1672 > {
1673 >        // Check for write-protection
1674 >        if (write_protected) {
1675 >                set_error(ERR_WRITEPROTECT);
1676 >                return;
1677 >        }
1678 >
1679 >        // Remember current ID
1680 >        uint8 id1 = bam[BAM_DISK_ID], id2 = bam[BAM_DISK_ID + 1];
1681 >
1682 >        // Formatting with ID?
1683 >        if (comma) {
1684 >
1685 >                close_all_channels();
1686 >
1687 >                // Clear BAM buffer
1688 >                memset(bam, 0, 256);
1689 >
1690 >                // Get ID from command
1691 >                if (comma[1]) {
1692 >                        id1 = comma[1];
1693 >                        id2 = comma[2] ? comma[2] : ' ';
1694 >                } else {
1695 >                        id1 = id2 = ' ';
1696 >                }
1697 >        }
1698 >
1699 >        // Format disk image
1700 >        format_image(the_file, desc, comma, id1, id2, name, name_len);
1701 >
1702 >        // Re-read BAM
1703 >        read_sector(DIR_TRACK, 0, bam);
1704 >        bam_dirty = false;
1705 > }
1706 >
1707 > // VALIDATE
1708 > void ImageDrive::validate_cmd(void)
1709 > {
1710 >        // Backup of old BAM in case something goes amiss
1711 >        uint8 old_bam[256];
1712 >        memcpy(old_bam, bam, 256);
1713 >
1714 >        // Clear BAM
1715 >        clear_bam(bam);
1716 >        bam_dirty = true;
1717 >
1718 >        // Allocate BAM and directory
1719 >        if (!alloc_block_chain(DIR_TRACK, 0)) {
1720 >                memcpy(bam, old_bam, 256);
1721 >                return;
1722 >        }
1723 >
1724 >        // Allocate all file data and side sector blocks
1725 >        int dir_track, dir_sector, entry;
1726 >        if (find_first_file((uint8 *)"*", 1, dir_track, dir_sector, entry)) {
1727 >                do {
1728 >                        uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1729 >
1730 >                        if (de[DE_TYPE] & 0x80) {
1731 >                                // Closed file, allocate all file data and side sector blocks
1732 >                                if (!alloc_block_chain(de[DE_TRACK], de[DE_SECTOR]) || !alloc_block_chain(de[DE_SIDE_TRACK], de[DE_SIDE_SECTOR])) {
1733 >                                        memcpy(bam, old_bam, 256);
1734 >                                        return;
1735 >                                }
1736 >                        } else {
1737 >                                // Open file, delete it
1738 >                                de[DE_TYPE] = 0;
1739 >                                write_sector(dir_track, dir_sector, dir);
1740 >                        }
1741 >                } while (find_next_file((uint8 *)"*", 1, dir_track, dir_sector, entry));
1742 >        }
1743   }
1744  
1745  
1746   /*
1747 < *  Free floppy buffer
1747 > *  Check whether file with given header (64 bytes) and size looks like one
1748 > *  of the file types supported by this module
1749   */
1750  
1751 < void D64Drive::free_buffer(int buf)
1751 > static bool is_d64_file(const uint8 *header, long size)
1752   {
1753 <        buf_free[buf] = true;
1753 >        return size == NUM_SECTORS_35 * 256 || size == NUM_SECTORS_35 * 257
1754 >            || size == NUM_SECTORS_40 * 256 || size == NUM_SECTORS_40 * 257;
1755   }
1756  
1757 + static bool is_ed64_file(const uint8 *header, long size)
1758 + {
1759 +        // 35-track d64 file with header ID at the end (only used internally for
1760 +        // converted zipcode files)
1761 +        return size == NUM_SECTORS_35 * 256 + 2;
1762 + }
1763  
1764 + static bool is_x64_file(const uint8 *header, long size)
1765 + {
1766 +        return memcmp(header, "C\x15\x41\x64\x01\x02", 6) == 0;
1767 + }
1768 +
1769 + static bool is_zipcode_file(const char *path)
1770 + {
1771 + #if 0
1772 +        string base, part;
1773 +        SplitPath(path, base, part);
1774 +        return part.length() > 2 && part[0] >= '1' && part[0] <= '4' && part[1] == '!';
1775 + #else
1776 +        return false;
1777 + #endif
1778 + }
1779 +
1780 + bool IsImageFile(const char *path, const uint8 *header, long size)
1781 + {
1782 +        return is_d64_file(header, size) || is_x64_file(header, size) || is_zipcode_file(path);
1783 + }
1784 +
1785 +
1786 + #if 0
1787   /*
1788 < *  Read sector (256 bytes)
965 < *  true: success, false: error
1788 > *  Convert zipcode file to extended d64 file (d64 file with header ID)
1789   */
1790  
1791 < bool D64Drive::read_sector(int track, int sector, uint8 *buffer)
1791 > static FILE *open_zipcode_file(FILE *old, int num, const string &base, string &part, uint8 &id1, uint8 &id2)
1792   {
1793 <        int offset;
1794 <
1795 <        // Convert track/sector to byte offset in file
1796 <        if ((offset = offset_from_ts(track, sector)) < 0) {
1797 <                set_error(ERR_ILLEGALTS);
1798 <                return false;
1793 >        if (old)
1794 >                fclose(old);
1795 >        part[0] = num + '1';
1796 >        FILE *f = fopen(AddToPath(base, part).c_str(), "rb");
1797 >        if (f == NULL)
1798 >                return NULL;
1799 >        if (fseek(f, 2, SEEK_SET) < 0) {
1800 >                fclose(f);
1801 >                return NULL;
1802          }
1803 <
1804 <        if (the_file == NULL) {
1805 <                set_error(ERR_NOTREADY);
980 <                return false;
1803 >        if (num == 0) {
1804 >                id1 = getc(f);
1805 >                id2 = getc(f);
1806          }
1807 +        return f;
1808 + }
1809  
1810 < #ifdef AMIGA
1811 <        if (offset != ftell(the_file))
1812 <                fseek(the_file, offset + image_header, SEEK_SET);
1813 < #else
1814 <        fseek(the_file, offset + image_header, SEEK_SET);
1810 > static FILE *convert_zipcode_to_ed64(const string &path)
1811 > {
1812 >        FILE *in = NULL, *out = NULL;
1813 >        uint8 id1, id2;
1814 >
1815 >        // Split input file name
1816 >        string base, part;
1817 >        SplitPath(path, base, part);
1818 >
1819 >        // Open output file
1820 >        out = tmpfile();
1821 >        if (out == NULL)
1822 >                goto error;
1823 >
1824 >        // Decode all tracks
1825 >        for (int track=1; track<=35; track++) {
1826 >                int max_sect = 17 + ((track < 31) ? 1 : 0) + ((track < 25) ? 1 : 0) + ((track < 18) ? 2 : 0);
1827 >
1828 >                // Select appropriate input file
1829 >                switch (track) {
1830 >                        case 1:
1831 >                                if ((in = open_zipcode_file(NULL, 0, base, part, id1, id2)) == NULL)
1832 >                                        goto error;
1833 >                                break;
1834 >                        case 9:
1835 >                                if ((in = open_zipcode_file(in, 1, base, part, id1, id2)) == NULL)
1836 >                                        goto error;
1837 >                                break;
1838 >                        case 17:
1839 >                                if ((in = open_zipcode_file(in, 2, base, part, id1, id2)) == NULL)
1840 >                                        goto error;
1841 >                                break;
1842 >                        case 26:
1843 >                                if ((in = open_zipcode_file(in, 3, base, part, id1, id2)) == NULL)
1844 >                                        goto error;
1845 >                                break;
1846 >                }
1847 >
1848 >                // Clear "sector read" flags
1849 >                bool sect_flag[21];
1850 >                for (int i=0; i<max_sect; i++)
1851 >                        sect_flag[i] = false;
1852 >
1853 >                // Read track
1854 >                uint8 act_track[21 * 256];
1855 >                for (int i=0; i<max_sect; i++) {
1856 >
1857 >                        // Read and verify track/sector number
1858 >                        uint8 t = getc(in);
1859 >                        uint8 s = getc(in);
1860 >                        if ((t & 0x3f) != track || s >= max_sect || sect_flag[s] || feof(in))
1861 >                                goto error;
1862 >                        sect_flag[s] = true;
1863 >                        uint8 *p = act_track + s * 256;
1864 >
1865 >                        // Uncompress sector
1866 >                        if (t & 0x80) {
1867 >                                // Run-length encoded sector
1868 >                                uint8 len = getc(in);
1869 >                                uint8 rep = getc(in);
1870 >                                int count = 0;
1871 >                                for (int j=0; j<len; j++) {
1872 >                                        if (feof(in))
1873 >                                                goto error;
1874 >                                        uint8 c = getc(in);
1875 >                                        if (c != rep)
1876 >                                                p[count++] = c;
1877 >                                        else {
1878 >                                                uint8 repnum = getc(in);
1879 >                                                if (feof(in))
1880 >                                                        goto error;
1881 >                                                c = getc(in);
1882 >                                                j += 2;
1883 >                                                for (int k=0; k<repnum; k++)
1884 >                                                        p[count++] = c;
1885 >                                        }
1886 >                                }
1887 >                        } else if (t & 0x40) {
1888 >                                // Sector filled with constant byte
1889 >                                if (feof(in))
1890 >                                        goto error;
1891 >                                uint8 c = getc(in);
1892 >                                memset(p, c, 256);
1893 >                        } else {
1894 >                                // Plain sector
1895 >                                if (fread(p, 1, 256, in) != 256)
1896 >                                        goto error;
1897 >                        }
1898 >                }
1899 >
1900 >                // Write track
1901 >                if (fwrite(act_track, 256, max_sect, out) != (size_t)max_sect)
1902 >                        goto error;
1903 >        }
1904 >
1905 >        // Write header ID
1906 >        putc(id1, out);
1907 >        putc(id2, out);
1908 >
1909 >        // Done
1910 >        fclose(in);
1911 >        fseek(out, 0, SEEK_SET);
1912 >        return out;
1913 >
1914 > error:
1915 >        if (in)
1916 >                fclose(in);
1917 >        if (out)
1918 >                fclose(out);
1919 >        return NULL;
1920 > }
1921   #endif
1922 <        fread(buffer, 256, 1, the_file);
1923 <        return true;
1922 >
1923 >
1924 > /*
1925 > *  Open disk image file, return file handle
1926 > */
1927 >
1928 > FILE *open_image_file(const char *path, bool write_mode)
1929 > {
1930 > #if 0
1931 >        if (is_zipcode_file(path)) {
1932 >                if (write_mode)
1933 >                        return NULL;
1934 >                else
1935 >                        return convert_zipcode_to_ed64(path);
1936 >        } else
1937 > #endif
1938 >                return fopen(path, write_mode ? "r+b" : "rb");
1939   }
1940  
1941  
1942   /*
1943 < *  Convert track/sector to offset
1943 > *  Parse image file and fill in image_file_desc structure
1944   */
1945  
1946 < const int num_sectors[41] = {
1947 <        0,
1948 <        21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
1949 <        19,19,19,19,19,19,19,
1950 <        18,18,18,18,18,18,
1003 <        17,17,17,17,17,
1004 <        17,17,17,17,17          // Tracks 36..40
1005 < };
1946 > static bool parse_d64_file(FILE *f, image_file_desc &desc, bool has_header_id)
1947 > {
1948 >        // .d64 files have no header
1949 >        desc.type = has_header_id ? TYPE_ED64 : TYPE_D64;
1950 >        desc.header_size = 0;
1951  
1952 < const int sector_offset[41] = {
1953 <        0,
1954 <        0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
1955 <        357,376,395,414,433,452,471,
1956 <        490,508,526,544,562,580,
1957 <        598,615,632,649,666,
1958 <        683,700,717,734,751     // Tracks 36..40
1959 < };
1952 >        // Determine number of tracks
1953 >        fseek(f, 0, SEEK_END);
1954 >        long size = ftell(f);
1955 >        if (size == NUM_SECTORS_40 * 256 || size == NUM_SECTORS_40 * 257)
1956 >                desc.num_tracks = 40;
1957 >        else
1958 >                desc.num_tracks = 35;
1959 >
1960 >        if (has_header_id) {
1961 >                // Read header ID from image file (last 2 bytes)
1962 >                fseek(f, -2, SEEK_END);
1963 >                desc.id1 = getc(f);
1964 >                desc.id2 = getc(f);
1965 >        } else {
1966 >                // Read header ID from BAM (use error_info as buffer)
1967 >                fseek(f, accum_num_sectors[18] * 256, SEEK_SET);
1968 >                fread(desc.error_info, 1, 256, f);
1969 >                desc.id1 = desc.error_info[BAM_DISK_ID];
1970 >                desc.id2 = desc.error_info[BAM_DISK_ID + 1];
1971 >        }
1972 >
1973 >        // Read error info
1974 >        memset(desc.error_info, 1, sizeof(desc.error_info));
1975 >        if (size == NUM_SECTORS_35 * 257) {
1976 >                fseek(f, NUM_SECTORS_35 * 256, SEEK_SET);
1977 >                fread(desc.error_info, NUM_SECTORS_35, 1, f);
1978 >                desc.has_error_info = true;
1979 >        } else if (size == NUM_SECTORS_40 * 257) {
1980 >                fseek(f, NUM_SECTORS_40 * 256, SEEK_SET);
1981 >                fread(desc.error_info, NUM_SECTORS_40, 1, f);
1982 >                desc.has_error_info = true;
1983 >        } else
1984 >                desc.has_error_info = false;
1985 >
1986 >        return true;
1987 > }
1988  
1989 < int D64Drive::offset_from_ts(int track, int sector)
1989 > static bool parse_x64_file(FILE *f, image_file_desc &desc)
1990   {
1991 <        if ((track < 1) || (track > NUM_TRACKS) ||
1992 <                (sector < 0) || (sector >= num_sectors[track]))
1993 <                return -1;
1991 >        desc.type = TYPE_X64;
1992 >        desc.header_size = 64;
1993 >
1994 >        // Read number of tracks
1995 >        fseek(f, 7, SEEK_SET);
1996 >        desc.num_tracks = getc(f);
1997 >        if (desc.num_tracks < 35 || desc.num_tracks > 40)
1998 >                return false;
1999  
2000 <        return (sector_offset[track] + sector) << 8;
2000 >        // Read header ID from BAM (use error_info as buffer)
2001 >        fseek(f, desc.header_size + accum_num_sectors[18] * 256, SEEK_SET);
2002 >        fread(desc.error_info, 1, 256, f);
2003 >        desc.id1 = desc.error_info[BAM_DISK_ID];
2004 >        desc.id2 = desc.error_info[BAM_DISK_ID + 1];
2005 >
2006 >        // .x64 files have no error info
2007 >        memset(desc.error_info, 1, sizeof(desc.error_info));
2008 >        desc.has_error_info = false;
2009 >        return true;
2010 > }
2011 >
2012 > bool parse_image_file(FILE *f, image_file_desc &desc)
2013 > {
2014 >        // Read header
2015 >        uint8 header[64];
2016 >        fread(header, 1, sizeof(header), f);
2017 >
2018 >        // Determine file size
2019 >        fseek(f, 0, SEEK_END);
2020 >        long size = ftell(f);
2021 >
2022 >        // Determine file type and fill in image_file_desc structure
2023 >        if (is_x64_file(header, size))
2024 >                return parse_x64_file(f, desc);
2025 >        else if (is_d64_file(header, size))
2026 >                return parse_d64_file(f, desc, false);
2027 >        else if (is_ed64_file(header, size))
2028 >                return parse_d64_file(f, desc, true);
2029 >        else
2030 >                return false;
2031   }
2032  
2033  
2034   /*
2035 < *  Conversion PETSCII->ASCII
2035 > *  Create new blank disk image file, returns false on error
2036   */
2037  
2038 < uint8 D64Drive::conv_from_64(uint8 c, bool map_slash)
2038 > bool CreateImageFile(const char *path)
2039   {
2040 <        if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
2041 <                return c ^ 0x20;
2042 <        if ((c >= 0xc1) && (c <= 0xda))
2043 <                return c ^ 0x80;
2044 <        if ((c == '/') && map_slash && ThePrefs.MapSlash)
2045 <                return '\\';
2046 <        return c;
2040 >        // Open file for writing
2041 >        FILE *f = fopen(path, "wb");
2042 >        if (f == NULL)
2043 >                return false;
2044 >
2045 >        // Create descriptor
2046 >        image_file_desc desc;
2047 >        desc.type = TYPE_D64;
2048 >        desc.header_size = 0;
2049 >        desc.num_tracks = 35;
2050 >        desc.id1 = 'F';
2051 >        desc.id1 = 'R';
2052 >        memset(desc.error_info, 1, sizeof(desc.error_info));
2053 >        desc.has_error_info = false;
2054 >
2055 >        // Format image file
2056 >        if (!format_image(f, desc, true, 'F', 'R', (uint8 *)"D64 FILE", 8)) {
2057 >                fclose(f);
2058 >                remove(path);
2059 >                return false;
2060 >        }
2061 >
2062 >        // Close file
2063 >        fclose(f);
2064 >        return true;
2065   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines