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.1 by cebix, 2003-07-01T17:09:43Z vs.
Revision 1.8 by cebix, 2004-01-12T15:13:20Z

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines