/****************************************************************/ /* Apple //gs emulator */ /* Copyright 1996 Kent Dickey */ /* */ /* This code may not be used in a commercial product */ /* without prior written permission of the author. */ /* */ /* You may freely distribute this code. */ /* */ /* You can contact the author at kentd@cup.hp.com. */ /* HP has nothing to do with this software. */ /****************************************************************/ #include "defc.h" #include #include "prodos.h" #include #define DEF_DISK_SIZE (800*1024) #define MAX_FILE_NAMES 51 char out_name[] = "POOF1"; int g_def_file_type = -1; int g_def_aux_type = -1; void make_legal_prodos_name(char *new_name, char *old_name); int main(int argc, char **argv) { char *files[MAX_FILE_NAMES]; char *name; char *new_name_end; int new_name_len; struct stat stat_buf; int in; int in_size; int ret; ProDisk *disk; char new_name[128]; byte in_buf[1024]; int disk_size; int i; int done; int pos; int size; int num_files; int file_type; int aux_type; disk_size = DEF_DISK_SIZE; if(argc < 2) { fprintf(stderr, "%s: {-[size in K]} {unix_files}\n", argv[0]); exit(1); } num_files = 0; for(i = 1; i < argc; i++) { if(argv[i][0] == '-') { /* I smell a -size_in_K */ disk_size = strtoul(&(argv[i][1]), 0, 10) * 1024; printf("disk_size: %d, 0x%x\n", disk_size, disk_size); if(disk_size < 40*1024) { printf("Too small!\n"); exit(1); } } else { files[num_files] = argv[i]; num_files++; if(num_files >= MAX_FILE_NAMES) { printf("Too many filenames: %d\n", num_files); exit(2); } } } disk = allocate_memdisk(out_name, disk_size); format_memdisk(disk, out_name); for(i = 0; i < num_files; i++) { name = files[i]; in = open(name, O_RDONLY | O_BINARY); if(in < 0) { fprintf(stderr, "opening %s returned %d, errno: %d\n", name, in, errno); exit(1); } ret = fstat(in, &stat_buf); if(ret != 0) { fprintf(stderr, "fstat returned %d, errno: %d\n", ret, errno); } in_size = stat_buf.st_size; printf("in size: %d\n", in_size); if(in_size > disk->disk_bytes_left) { printf("File bigger than %d, too big!\n", disk_size); exit(2); } make_legal_prodos_name(new_name, name); new_name_len = strlen(new_name); new_name_end = new_name + new_name_len; file_type = g_def_file_type; aux_type = g_def_aux_type; while(g_def_file_type < 0) { /* choose file type */ if(new_name_len >= 5) { if(strcmp(new_name_end - 4, ".SHK") == 0) { file_type = 0xe0; aux_type = 0x8002; break; } if(strcmp(new_name_end - 4, ".SDK") == 0) { file_type = 0xe0; aux_type = 0x8002; break; } } file_type = 0x04; /* TXT */ aux_type = 0; break; } create_new_file(disk, 2, 1, new_name, file_type, 0, 0, 0, 0xc3, aux_type, 0, in_size); done = 0; pos = 0; while(pos < in_size) { size = 512; if(pos + size > in_size) { size = in_size - pos; } ret = read(in, in_buf, size); if(ret != size || ret <= 0) { fprintf(stderr, "read returned %d, errno: %d\n", ret, errno); exit(2); } ret = pro_write_file(disk, in_buf, pos, size); if(ret != 0) { printf("pro_write returned %d!\n", ret); exit(3); } pos += size; } close_file(disk); close(in); } flush_disk(disk); return 0; } void make_legal_prodos_name(char *new_name, char *old_name) { char *ptr; int start_len, start_char; int len; int pos; int c; int j; for(j = 0; j < 16; j++) { /* make sure it ends with null == 15 + 1 */ new_name[j] = 0; } start_char = 0; start_len = strlen(old_name); len = 0; ptr = &old_name[start_len - 1]; for(j = start_len - 1; j >= 0; j--) { if(*ptr == '/' || *ptr == ':') { break; } ptr--; len++; } ptr++; if(len <= 0) { printf("Filename: %s has len: %d!\n", old_name, len); exit(1); } printf("mid_name: %s, len:%d\n", ptr, len); pos = 0; for(j = 0; j < 15; j++) { c = ptr[pos]; if(isalnum(c)) { c = toupper(c); } else if(c != 0) { c = '.'; } if(j == 0 && !isalpha(c)) { c = 'A'; } new_name[j] = c; pos++; if(pos == 7 && len > 15) { pos = len - 8; } } printf("new_name: %s\n", new_name); } void flush_disk(ProDisk *disk) { disk_write_data(disk, 6, disk->bitmap_ptr, disk->size_bitmap_blocks * 512); close(disk->fd); disk->fd = -1; } void close_file(ProDisk *disk) { write_ind_block(disk); write_master_ind_block(disk); disk_write_data(disk, disk->dir_blk_num, &(disk->dir_blk_data[0]), 512); disk->file_ptr = 0; } ProDisk * allocate_memdisk(char *out_name, int size) { ProDisk *disk; int out; out = open(out_name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0x1b6); if(out < 0) { fprintf(stderr, "opening %s returned %d, errno: %d\n", out_name, out, errno); exit(1); } disk = (ProDisk *)malloc(sizeof(ProDisk)); if(disk == 0) { printf("allocate_memdisk failed, errno: %d\n", errno); } disk->fd = out; disk->total_blocks = (size + 511) / 512; disk->bitmap_ptr = 0; disk->disk_bytes_left = 0; disk->size_bitmap_blocks = 0; disk->dir_blk_num = -1; disk->ind_blk_num = -1; disk->master_ind_blk_num = -1; return disk; } void format_memdisk(ProDisk *disk, char *name) { byte zero_buf[1024]; int total_blocks; byte *bitmap_ptr; Vol_hdr *vol_hdr; Directory *dir; int size_bitmap_bytes; int size_bitmap_blocks; int disk_blocks_left; int i, j; total_blocks = disk->total_blocks; /* Zero out blocks 0 and 1 */ for(i = 0; i < 2*512; i++) { zero_buf[i] = 0; } disk_write_data(disk, 0x00000, zero_buf, 2*512); /* and make the image the right size */ disk_write_data(disk, total_blocks - 1, zero_buf, 512); dir = disk_read_dir(disk, 2); set_l2byte(&(dir->prev_blk), 0); set_l2byte(&(dir->next_blk), 3); vol_hdr = (Vol_hdr *)&(dir->file_entries[0]); vol_hdr->storage_type_name_len = 0xf0 + strlen(name); strncpy((char *)vol_hdr->vol_name, name, strlen(name)); vol_hdr->version = 0; vol_hdr->min_version = 0; vol_hdr->access = 0xc3; vol_hdr->entry_length = 0x27; vol_hdr->entries_per_block = 0x0d; set_l2byte(&(vol_hdr->file_count), 0); vol_hdr->entries_per_block = 0x0d; set_l2byte(&(vol_hdr->bit_map), 6); set_l2byte(&(vol_hdr->total_blocks), total_blocks); for(i = 1; i < 13; i++) { set_file_entry(&(dir->file_entries[i]), 0, "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } disk_write_dir(disk, 2); for(i = 3; i < 6; i++) { dir = disk_read_dir(disk, i); set_l2byte(&(dir->prev_blk), i - 1); set_l2byte(&(dir->next_blk), i + 1); if(i == 5) { set_l2byte(&(dir->next_blk), 0); } for(j = 0; j < 13; j++) { set_file_entry(&(dir->file_entries[j]), 0, "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } disk_write_dir(disk, i); } size_bitmap_bytes = (total_blocks + 7)/ 8; size_bitmap_blocks = (size_bitmap_bytes + 511)/ 512; bitmap_ptr = (byte *)malloc(size_bitmap_blocks * 512); for(i = 0; i < 6+size_bitmap_blocks; i++) { set_bitmap_used(bitmap_ptr, i); } for(i = 6+size_bitmap_blocks; i < total_blocks; i++) { set_bitmap_free(bitmap_ptr, i); } for(i = total_blocks; i < size_bitmap_blocks*512*8; i++) { set_bitmap_used(bitmap_ptr, i); } disk_write_data(disk, 6, bitmap_ptr, size_bitmap_blocks * 512); disk->bitmap_ptr = bitmap_ptr; disk->size_bitmap_blocks = size_bitmap_blocks; disk->bitmap_bytes = size_bitmap_blocks * 512; disk->bitmap_cur_pos = 0; disk_blocks_left = total_blocks - 6 - size_bitmap_blocks; disk->disk_bytes_left = disk_blocks_left * 512; } void disk_write_data(ProDisk *disk, int blk_num, byte *buf, int size) { int size_in_blocks; int ret; #if 0 printf("Writing blk %04x from buf: %08x, %03x\n", blk_num, buf, size); #endif size_in_blocks = size >> 9; if(size_in_blocks * 512 != size) { printf("disk_write: blk: %04x, buf: %08x, size: %08x\n", blk_num, (word32)buf, size); exit(1); } ret = lseek(disk->fd, 512*blk_num, SEEK_SET); if(ret != 512*blk_num) { printf("disk_write: seek: %d, errno: %d, blk: %04x, buf: " "%08x, sz: %08x\n", ret, errno, blk_num, (word32)buf, size); exit(1); } ret = write(disk->fd, buf, size); if(ret != size) { printf("disk_write: write: %d, errno: %d, blk: %04x, buf: " "%08x, sz: %08x\n", ret, errno, blk_num, (word32)buf, size); exit(1); } } void disk_read_data(ProDisk *disk, int blk_num, byte *buf, int size) { int size_in_blocks; int ret; int i; size_in_blocks = size >> 9; if(size_in_blocks * 512 != size) { printf("disk_read: blk: %04x, buf: %08x, size: %08x\n", blk_num, (word32)buf, size); exit(1); } ret = lseek(disk->fd, 512*blk_num, SEEK_SET); if(ret != 512*blk_num) { printf("disk_read: seek: %d, errno: %d, blk: %04x, buf: " "%08x, sz: %08x\n", ret, errno, blk_num, (word32)buf, size); exit(1); } ret = read(disk->fd, buf, size); if(ret != size) { printf("disk_read: read: %d, errno: %d, blk: %04x, buf: " "%08x, sz: %08x\n", ret, errno, blk_num, (word32)buf, size); for(i = 0; i < size; i++) { buf[i] = 0; } } } Directory * disk_read_dir(ProDisk *disk, int blk_num) { disk_write_dir(disk, blk_num); disk->dir_blk_num = blk_num; disk_read_data(disk, blk_num, &(disk->dir_blk_data[0]), 512); return (Directory *)&(disk->dir_blk_data[0]); } void disk_write_dir(ProDisk *disk, int blk_num) { if(disk->dir_blk_num >= 0) { if(disk->dir_blk_num != blk_num) { printf("disk_write_dir: %04x != %04x\n", disk->dir_blk_num, blk_num); } disk_write_data(disk, disk->dir_blk_num, &(disk->dir_blk_data[0]), 512); disk->dir_blk_num = -1; } } void create_new_file(ProDisk *disk, int dir_block, int storage_type, char *name, int file_type, word32 creation_time, int version, int min_version, int access, int aux_type, word32 last_mod, word32 eof) { Vol_hdr *vol_ptr; int val; Directory *dir_ptr; File_entry *file_ptr; int file_count; int pos; int last_pos; int done; int next_blk; int name_len; name_len = strlen(name); dir_ptr = disk_read_dir(disk, dir_block); next_blk = dir_block; val = dir_ptr->file_entries[0].storage_type_name_len; last_pos = 13; pos = 0; if(((val & 0xf0) == 0xf0) || ((val & 0xf0) == 0xe0)) { /* vol dir or subdir header */ vol_ptr = (Vol_hdr *)&(dir_ptr->file_entries[0]); file_count = get_l2byte(&(vol_ptr->file_count)); pos = 1; last_pos = vol_ptr->entries_per_block; } else { printf("dir_block: %04x not subdir or voldir\n", dir_block); exit(6); } vol_ptr = 0; done = 0; while(!done) { file_ptr = &(dir_ptr->file_entries[pos]); if(((file_ptr->storage_type_name_len) & 0xf0) == 0) { /* Got it! */ file_ptr->storage_type_name_len = (storage_type << 4) | name_len; strncpy((char *)file_ptr->file_name, name, 15); file_ptr->file_type = file_type; set_l2byte(&(file_ptr->key_pointer), find_next_free_block(disk)); set_l2byte(&(file_ptr->blocks_used), 0); set_pro_time(&(file_ptr->creation_time), creation_time); file_ptr->version = version; file_ptr->min_version = min_version; file_ptr->access = access; set_l2byte(&(file_ptr->aux_type), aux_type); set_pro_time(&(file_ptr->last_mod), last_mod); set_l2byte(&(file_ptr->header_pointer), dir_block); set_l3byte(&(file_ptr->eof), eof); disk_write_dir(disk, next_blk); dir_ptr = disk_read_dir(disk, dir_block); vol_ptr = (Vol_hdr *)&(dir_ptr->file_entries[0]); set_l2byte(&(vol_ptr->file_count), file_count+1); disk_write_dir(disk, dir_block); disk_read_dir(disk, next_blk); /* re-read dir so that ptrs are set up right */ disk->file_ptr = file_ptr; disk->file_open = 1; disk->ind_blk_num = -1; disk->master_ind_blk_num = -1; done = 1; break; } else { /* check to make sure name is unique */ if((file_ptr->storage_type_name_len & 0x0f)== name_len){ if(!memcmp(file_ptr->file_name, name,name_len)){ printf("Name %s already on disk!\n", name); exit(8); } } pos++; if(pos >= last_pos) { /* Go to next block */ next_blk = get_l2byte(&(dir_ptr->next_blk)); if(next_blk) { dir_ptr = disk_read_dir(disk, next_blk); pos = 0; } else { printf("Top directory full!\n"); exit(2); } } } } } int pro_write_file(ProDisk *disk, byte *in_buf, int pos, int size) { int block; int i; block = get_disk_block(disk, pos, 1); if(block < 7) { printf("pro_write_file, get_disk_block: %d\n", block); exit(3); } if(size < 512) { for(i = size; i < 512; i++) { in_buf[i] = 0; } } else if(size > 512) { printf("error, pro_write_file size: %d too big\n", size); exit(4); } disk_write_data(disk, block, in_buf, 512); return 0; } int get_disk_block(ProDisk *disk, int pos, int create) { File_entry *file_ptr; int storage_type; word32 eof; int lo, hi; int offset; int master_ind_block, ind_block; int ret_block; int key_block; if(pos >= 128*256*512) { printf("offset too big\n"); exit(3); } file_ptr = disk->file_ptr; eof = get_l3byte(&(file_ptr->eof)); storage_type = (file_ptr->storage_type_name_len) >> 4; key_block = get_l2byte(&(file_ptr->key_pointer)); if(storage_type == 1 && pos >= 512) { /* make it sapling */ get_new_ind_block(disk); inc_l2byte(&(file_ptr->blocks_used)); disk->ind_blk_data[0] = key_block & 0xff; disk->ind_blk_data[0x100] = key_block >> 8; key_block = disk->ind_blk_num; set_l2byte(&(file_ptr->key_pointer), key_block); file_ptr->storage_type_name_len += 0x10; storage_type++; } if(storage_type == 2 && pos >= 256*512) { /* make it tree */ get_new_master_ind_block(disk); inc_l2byte(&(file_ptr->blocks_used)); disk->master_ind_blk_data[0] = key_block & 0xff; disk->master_ind_blk_data[0x100] = key_block >> 8; key_block = disk->master_ind_blk_num; set_l2byte(&(file_ptr->key_pointer), key_block); file_ptr->storage_type_name_len += 0x10; storage_type++; } switch(storage_type) { case 1: if(pos >= 512) { printf("Error1!\n"); exit(3); } ret_block = key_block; if(ret_block == 0) { ret_block = find_next_free_block(disk); inc_l2byte(&(file_ptr->blocks_used)); set_l2byte(&(file_ptr->key_pointer), ret_block); } return ret_block; case 2: ind_block = key_block; if(ind_block <= 0) { printf("write failure, ind_block: %d!\n", ind_block); exit(3); } offset = pos >> 9; if(offset >= 256) { printf("pos too big!\n"); exit(3); } lo = disk->ind_blk_data[offset]; hi = disk->ind_blk_data[offset + 0x100]; ret_block = hi*256 + lo; if(ret_block == 0) { /* Need to alloc a block for this guy */ ret_block = find_next_free_block(disk); inc_l2byte(&(file_ptr->blocks_used)); disk->ind_blk_data[offset] = ret_block & 0xff; disk->ind_blk_data[offset + 0x100] = ((ret_block >> 8) & 0xff); } return ret_block; case 3: /* tree */ master_ind_block = key_block; if(master_ind_block <= 0) { printf("write failure, master_ind_block: %d!\n", master_ind_block); exit(3); } offset = pos >> 17; if(offset >= 128) { printf("master too big!\n"); exit(4); } lo = disk->master_ind_blk_data[offset]; hi = disk->master_ind_blk_data[offset + 0x100]; ind_block = hi*256 + lo; if(ind_block == 0) { /* Need to alloc an ind block */ get_new_ind_block(disk); ind_block = disk->ind_blk_num; inc_l2byte(&(file_ptr->blocks_used)); disk->master_ind_blk_data[offset] = ind_block & 0xff; disk->master_ind_blk_data[offset + 0x100] = ((ind_block >> 8) & 0xff); } offset = (pos >> 9) & 0xff; lo = disk->ind_blk_data[offset]; hi = disk->ind_blk_data[offset + 0x100]; ret_block = hi*256 + lo; if(ret_block == 0) { /* Need to alloc a block for this guy */ ret_block = find_next_free_block(disk); inc_l2byte(&(file_ptr->blocks_used)); disk->ind_blk_data[offset] = ret_block & 0xff; disk->ind_blk_data[offset + 0x100] = ((ret_block >> 8) & 0xff); } return ret_block; default: printf("unknown storage type: %d\n", storage_type); exit(4); } printf("Can't get here!\n"); exit(5); } void get_new_ind_block(ProDisk *disk) { int ind_blk_num; int i; write_ind_block(disk); ind_blk_num = find_next_free_block(disk); for(i = 0; i < 512; i++) { disk->ind_blk_data[i] = 0; } disk->ind_blk_num = ind_blk_num; } void write_ind_block(ProDisk *disk) { int ind_blk_num; ind_blk_num = disk->ind_blk_num; if(ind_blk_num > 0) { printf("Write ind block: %04x\n", ind_blk_num); disk_write_data(disk, ind_blk_num, &(disk->ind_blk_data[0]), 512); disk->ind_blk_num = -1; } } void get_new_master_ind_block(ProDisk *disk) { int master_ind_blk_num; int i; write_master_ind_block(disk); master_ind_blk_num = find_next_free_block(disk); for(i = 0; i < 512; i++) { disk->master_ind_blk_data[i] = 0; } disk->master_ind_blk_num = master_ind_blk_num; } void write_master_ind_block(ProDisk *disk) { int master_ind_blk_num; master_ind_blk_num = disk->master_ind_blk_num; if(master_ind_blk_num > 0) { printf("Write master_ind block: %04x\n", master_ind_blk_num); disk_write_data(disk, master_ind_blk_num, &(disk->master_ind_blk_data[0]), 512); disk->master_ind_blk_num = -1; } } int find_next_free_block(ProDisk *disk) { byte *bitmap_ptr; int pos; int bitmap_bytes; int i, j; word32 val; bitmap_ptr = disk->bitmap_ptr; bitmap_bytes = disk->bitmap_bytes; pos = disk->bitmap_cur_pos; for(i = pos; i < bitmap_bytes; i++) { val = bitmap_ptr[i]; if(val == 0) { continue; } for(j = 0; j < 8; j++) { if(val & (0x80 >> j)) { set_bitmap_used(bitmap_ptr, 8*i+j); disk->bitmap_cur_pos = i; return 8*i + j; } } return -1; } return -1; } void set_bitmap_used(byte *ptr, int i) { word32 offset, bit; word32 val; offset = i >> 3; bit = i & 7; val = ~(0x80 >> bit); ptr[offset] &= val; } void set_bitmap_free(byte *ptr, int i) { int offset, bit; int val; offset = i >> 3; bit = i & 7; val = (0x80 >> bit); ptr[offset] |= val; } void set_file_entry(File_entry *entry, int storage_type_name_len, char *file_name, int file_type, int key_pointer, int blocks_used, int eof, word32 creation_time, int version, int min_version, int access, int aux_type, word32 last_mod, int header_pointer) { entry->storage_type_name_len = storage_type_name_len; strncpy((char *)entry->file_name, file_name, 15); entry->file_type = file_type; set_l2byte(&(entry->key_pointer), key_pointer); set_l2byte(&(entry->blocks_used), blocks_used); set_l3byte(&(entry->eof), eof); set_pro_time(&(entry->creation_time), creation_time); entry->version = version; entry->min_version = min_version; entry->access = access; set_l2byte(&(entry->aux_type), aux_type); set_pro_time(&(entry->last_mod), last_mod); set_l2byte(&(entry->aux_type), header_pointer); } void set_l2byte(L2byte *ptr, int val) { ptr->low = (val & 0xff); ptr->hi = ((val >> 8) & 0xff); } void set_l3byte(L3byte *ptr, int val) { ptr->low = (val & 0xff); ptr->hi = ((val >> 8) & 0xff); ptr->higher = ((val >> 16) & 0xff); } void set_pro_time(Pro_time *ptr, word32 val) { ptr->times[0] = ((val >> 24) & 0xff); ptr->times[1] = ((val >> 16) & 0xff); ptr->times[2] = ((val >> 8) & 0xff); ptr->times[3] = ((val) & 0xff); } int get_l2byte(L2byte *ptr) { int val; val = ((ptr->hi) * 256) + ptr->low; return val; } int get_l3byte(L3byte *ptr) { int val; val = ((ptr->higher) * 65536) + ((ptr->hi) * 256) + ptr->low; return val; } void inc_l2byte(L2byte *ptr) { set_l2byte(ptr, get_l2byte(ptr) + 1); }