mirror of https://github.com/digarok/gsplus.git
577 lines
10 KiB
C
577 lines
10 KiB
C
#define _BSD_SOURCE
|
|
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "defc.h"
|
|
#include "gsos.h"
|
|
#include "fst.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#define strcasecmp stricmp
|
|
#define strncasecmp strnicmp
|
|
#endif
|
|
|
|
|
|
#include "host_common.h"
|
|
|
|
|
|
char *host_root = NULL;
|
|
|
|
char *g_cfg_host_path = ""; // must not be null.
|
|
int g_cfg_host_read_only = 0;
|
|
int g_cfg_host_crlf = 1;
|
|
int g_cfg_host_merlin = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* simple malloc pool to simplify code. Should never need > 4 allocations.
|
|
*
|
|
*/
|
|
static void *gc[16];
|
|
static void **gc_ptr = &gc[0];
|
|
|
|
void *host_gc_malloc(size_t size) {
|
|
if (gc_ptr == &gc[16]) {
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
void *ptr = malloc(size);
|
|
if (ptr) {
|
|
*gc_ptr++ = ptr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
|
|
void host_gc_free(void) {
|
|
|
|
while (gc_ptr > gc) free(*--gc_ptr);
|
|
|
|
}
|
|
|
|
char *host_gc_strdup(const char *src) {
|
|
if (!src) return "";
|
|
if (!*src) return "";
|
|
int len = strlen(src) + 1;
|
|
char *cp = host_gc_malloc(len);
|
|
memcpy(cp, src, len);
|
|
return cp;
|
|
}
|
|
|
|
char *host_gc_append_path(const char *a, const char *b) {
|
|
|
|
/* strip all leading /s from b) */
|
|
while (*b == '/') ++b;
|
|
|
|
int aa = strlen(a);
|
|
int bb = strlen(b);
|
|
|
|
char *cp = host_gc_malloc(aa + bb + 2);
|
|
if (!cp) return NULL;
|
|
memcpy(cp, a, aa);
|
|
int len = aa;
|
|
|
|
/* strip all trailing /s from b */
|
|
while (len > 2 && cp[len-1] == '/') --len;
|
|
cp[len++] = '/';
|
|
memcpy(cp + len, b, bb);
|
|
len += bb;
|
|
cp[len] = 0;
|
|
return cp;
|
|
}
|
|
|
|
|
|
char *host_gc_append_string(const char *a, const char *b) {
|
|
int aa = strlen(a);
|
|
int bb = strlen(b);
|
|
|
|
char *cp = host_gc_malloc(aa + bb + 2);
|
|
if (!cp) return NULL;
|
|
memcpy(cp, a, aa);
|
|
int len = aa;
|
|
memcpy(cp + len, b, bb);
|
|
len += bb;
|
|
cp[len] = 0;
|
|
return cp;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* text conversion.
|
|
*/
|
|
|
|
void host_cr_to_lf(byte *buffer, size_t size) {
|
|
size_t i;
|
|
for (i = 0; i < size; ++i) {
|
|
if (buffer[i] == '\r') buffer[i] = '\n';
|
|
}
|
|
}
|
|
|
|
void host_lf_to_cr(byte *buffer, size_t size) {
|
|
size_t i;
|
|
for (i = 0; i < size; ++i) {
|
|
if (buffer[i] == '\n') buffer[i] = '\r';
|
|
}
|
|
}
|
|
|
|
void host_merlin_to_text(byte *buffer, size_t size) {
|
|
size_t i;
|
|
for (i = 0; i < size; ++i) {
|
|
byte b = buffer[i];
|
|
if (b == 0xa0) b = '\t';
|
|
b &= 0x7f;
|
|
if (b == '\r') b = '\n';
|
|
buffer[i] = b;
|
|
}
|
|
}
|
|
|
|
void host_text_to_merlin(byte *buffer, size_t size) {
|
|
size_t i;
|
|
for (i = 0; i < size; ++i) {
|
|
byte b = buffer[i];
|
|
if (b == '\t') b = 0xa0;
|
|
if (b == '\n') b = '\r';
|
|
if (b != ' ') b |= 0x80;
|
|
buffer[i] = b;
|
|
}
|
|
}
|
|
|
|
|
|
const char *host_error_name(word16 error) {
|
|
static char *errors[] = {
|
|
"",
|
|
"badSystemCall",
|
|
"",
|
|
"",
|
|
"invalidPcount",
|
|
"",
|
|
"",
|
|
"gsosActive",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
// 0x10
|
|
"devNotFound",
|
|
"invalidDevNum",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
// 0x20
|
|
"drvrBadReq",
|
|
"drvrBadCode",
|
|
"drvrBadParm",
|
|
"drvrNotOpen",
|
|
"drvrPriorOpen",
|
|
"irqTableFull",
|
|
"drvrNoResrc",
|
|
"drvrIOError",
|
|
"drvrNoDevice",
|
|
"drvrBusy",
|
|
"",
|
|
"drvrWrtProt",
|
|
"drvrBadCount",
|
|
"drvrBadBlock",
|
|
"drvrDiskSwitch",
|
|
"drvrOffLine",
|
|
// 0x30
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
// 0x40
|
|
"badPathSyntax",
|
|
"",
|
|
"tooManyFilesOpen",
|
|
"invalidRefNum",
|
|
"pathNotFound",
|
|
"volNotFound",
|
|
"fileNotFound",
|
|
"dupPathname",
|
|
"volumeFull",
|
|
"volDirFull",
|
|
"badFileFormat",
|
|
"badStoreType",
|
|
"eofEncountered",
|
|
"outOfRange",
|
|
"invalidAccess",
|
|
"buffTooSmall",
|
|
// 0x50
|
|
"fileBusy",
|
|
"dirError",
|
|
"unknownVol",
|
|
"paramRangeErr",
|
|
"outOfMem",
|
|
"",
|
|
"badBufferAddress", /* P8 MLI only */
|
|
"dupVolume",
|
|
"notBlockDev",
|
|
"invalidLevel",
|
|
"damagedBitMap",
|
|
"badPathNames",
|
|
"notSystemFile",
|
|
"osUnsupported",
|
|
"",
|
|
"stackOverflow",
|
|
// 0x60
|
|
"dataUnavail",
|
|
"endOfDir",
|
|
"invalidClass",
|
|
"resForkNotFound",
|
|
"invalidFSTID",
|
|
"invalidFSTop",
|
|
"fstCaution",
|
|
"devNameErr",
|
|
"defListFull",
|
|
"supListFull",
|
|
"fstError",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
//0x70
|
|
"resExistsErr",
|
|
"resAddErr",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
//0x80
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"networkError"
|
|
};
|
|
|
|
if (error < sizeof(errors) / sizeof(errors[0]))
|
|
return errors[error];
|
|
return "";
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* File info.
|
|
*/
|
|
|
|
|
|
static int hex(byte c) {
|
|
if (c >= '0' && c <= '9') return c - '0';
|
|
if (c >= 'a' && c <= 'f') return c + 10 - 'a';
|
|
if (c >= 'A' && c <= 'F') return c + 10 - 'A';
|
|
return 0;
|
|
}
|
|
|
|
|
|
int host_finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) {
|
|
|
|
if (!memcmp("pdos", buffer + 4, 4))
|
|
{
|
|
if (buffer[0] == 'p') {
|
|
*file_type = buffer[1];
|
|
*aux_type = (buffer[2] << 8) | buffer[3];
|
|
return 0;
|
|
}
|
|
if (!memcmp("PSYS", buffer, 4)) {
|
|
*file_type = 0xff;
|
|
*aux_type = 0x0000;
|
|
return 0;
|
|
}
|
|
if (!memcmp("PS16", buffer, 4)) {
|
|
*file_type = 0xb3;
|
|
*aux_type = 0x0000;
|
|
return 0;
|
|
}
|
|
|
|
// old mpw method for encoding.
|
|
if (!isxdigit(buffer[0]) && isxdigit(buffer[1]) && buffer[2] == ' ' && buffer[3] == ' ')
|
|
{
|
|
*file_type = (hex(buffer[0]) << 8) | hex(buffer[1]);
|
|
*aux_type = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
if (!memcmp("TEXT", buffer, 4)) {
|
|
*file_type = 0x04;
|
|
*aux_type = 0x0000;
|
|
return 0;
|
|
}
|
|
if (!memcmp("BINA", buffer, 4)) {
|
|
*file_type = 0x00;
|
|
*aux_type = 0x0000;
|
|
return 0;
|
|
}
|
|
if (!memcmp("dImgdCpy", buffer, 8)) {
|
|
*file_type = 0xe0;
|
|
*aux_type = 0x0005;
|
|
return 0;
|
|
}
|
|
|
|
if (!memcmp("MIDI", buffer, 4)) {
|
|
*file_type = 0xd7;
|
|
*aux_type = 0x0000;
|
|
return 0;
|
|
}
|
|
|
|
if (!memcmp("AIFF", buffer, 4)) {
|
|
*file_type = 0xd8;
|
|
*aux_type = 0x0000;
|
|
return 0;
|
|
}
|
|
|
|
if (!memcmp("AIFC", buffer, 4)) {
|
|
*file_type = 0xd8;
|
|
*aux_type = 0x0001;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int host_file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type) {
|
|
if (file_type > 0xff || aux_type > 0xffff) return -1;
|
|
|
|
if (!file_type && aux_type == 0x0000) {
|
|
memcpy(buffer, "BINApdos", 8);
|
|
return 0;
|
|
}
|
|
|
|
if (file_type == 0x04 && aux_type == 0x0000) {
|
|
memcpy(buffer, "TEXTpdos", 8);
|
|
return 0;
|
|
}
|
|
|
|
if (file_type == 0xff && aux_type == 0x0000) {
|
|
memcpy(buffer, "PSYSpdos", 8);
|
|
return 0;
|
|
}
|
|
|
|
if (file_type == 0xb3 && aux_type == 0x0000) {
|
|
memcpy(buffer, "PS16pdos", 8);
|
|
return 0;
|
|
}
|
|
|
|
if (file_type == 0xd7 && aux_type == 0x0000) {
|
|
memcpy(buffer, "MIDIpdos", 8);
|
|
return 0;
|
|
}
|
|
if (file_type == 0xd8 && aux_type == 0x0000) {
|
|
memcpy(buffer, "AIFFpdos", 8);
|
|
return 0;
|
|
}
|
|
if (file_type == 0xd8 && aux_type == 0x0001) {
|
|
memcpy(buffer, "AIFCpdos", 8);
|
|
return 0;
|
|
}
|
|
if (file_type == 0xe0 && aux_type == 0x0005) {
|
|
memcpy(buffer, "dImgdCpy", 8);
|
|
return 0;
|
|
}
|
|
|
|
|
|
memcpy(buffer, "p pdos", 8);
|
|
buffer[1] = (file_type) & 0xff;
|
|
buffer[2] = (aux_type >> 8) & 0xff;
|
|
buffer[3] = (aux_type) & 0xff;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#undef _
|
|
#define _(a, b, c) { a, sizeof(a) - 1, b, c }
|
|
struct ftype_entry {
|
|
char *ext;
|
|
unsigned length;
|
|
unsigned file_type;
|
|
unsigned aux_type;
|
|
};
|
|
|
|
static struct ftype_entry suffixes[] = {
|
|
_("c", 0xb0, 0x0008),
|
|
_("cc", 0xb0, 0x0008),
|
|
_("h", 0xb0, 0x0008),
|
|
_("rez", 0xb0, 0x0015),
|
|
_("asm", 0xb0, 0x0003),
|
|
_("mac", 0xb0, 0x0003),
|
|
_("pas", 0xb0, 0x0005),
|
|
_("txt", 0x04, 0x0000),
|
|
_("text", 0x04, 0x0000),
|
|
_("s", 0x04, 0x0000),
|
|
{ 0, 0, 0, 0}
|
|
};
|
|
|
|
static struct ftype_entry prefixes[] = {
|
|
_("m16.", 0xb0, 0x0003),
|
|
_("e16.", 0xb0, 0x0003),
|
|
{ 0, 0, 0, 0}
|
|
};
|
|
|
|
#undef _
|
|
|
|
void host_synthesize_file_xinfo(const char *path, struct file_info *fi) {
|
|
|
|
/* guess the file type / auxtype based on extension */
|
|
int n;
|
|
const char *dot = NULL;
|
|
const char *slash = NULL;
|
|
|
|
for(n = 0;; ++n) {
|
|
char c = path[n];
|
|
if (c == 0) break;
|
|
else if (c == '/') { slash = path + n + 1; dot = NULL; }
|
|
else if (c == '.') dot = path + n + 1;
|
|
}
|
|
|
|
if (dot && *dot) {
|
|
for (n = 0; n < sizeof(suffixes) / sizeof(suffixes[0]); ++n) {
|
|
if (!suffixes[n].ext) break;
|
|
if (!strcasecmp(dot, suffixes[n].ext)) {
|
|
fi->file_type = suffixes[n].file_type;
|
|
fi->aux_type = suffixes[n].aux_type;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void host_hexdump(word32 address, int size) {
|
|
const char *HexMap = "0123456789abcdef";
|
|
|
|
char buffer1[16 * 3 + 1 + 1];
|
|
char buffer2[16 + 1];
|
|
unsigned i, j;
|
|
|
|
printf("\n");
|
|
while (size > 0) {
|
|
memset(buffer1, ' ', sizeof(buffer1));
|
|
memset(buffer2, ' ', sizeof(buffer2));
|
|
|
|
int linelen = size;
|
|
if (linelen > 16) linelen = 16;
|
|
|
|
for (i = 0, j = 0; i < linelen; i++) {
|
|
unsigned x = get_memory_c(address + i, 0);
|
|
buffer1[j++] = HexMap[x >> 4];
|
|
buffer1[j++] = HexMap[x & 0x0f];
|
|
j++;
|
|
if (i == 7) j++;
|
|
x &= 0x7f;
|
|
// isascii not part of std:: and may be a macro.
|
|
buffer2[i] = isascii(x) && isprint(x) ? x : '.';
|
|
}
|
|
|
|
buffer1[sizeof(buffer1) - 1] = 0;
|
|
buffer2[sizeof(buffer2) - 1] = 0;
|
|
|
|
printf("%06x:\t%s\t%s\n",
|
|
address, buffer1, buffer2);
|
|
address += 16;
|
|
size -= 16;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
void host_hexdump_native(void *data, unsigned address, int size) {
|
|
const char *HexMap = "0123456789abcdef";
|
|
|
|
char buffer1[16 * 3 + 1 + 1];
|
|
char buffer2[16 + 1];
|
|
unsigned i, j;
|
|
|
|
printf("\n");
|
|
while (size > 0) {
|
|
memset(buffer1, ' ', sizeof(buffer1));
|
|
memset(buffer2, ' ', sizeof(buffer2));
|
|
|
|
int linelen = size;
|
|
if (linelen > 16) linelen = 16;
|
|
|
|
for (i = 0, j = 0; i < linelen; i++) {
|
|
unsigned x = ((byte *)data)[address + i];
|
|
buffer1[j++] = HexMap[x >> 4];
|
|
buffer1[j++] = HexMap[x & 0x0f];
|
|
j++;
|
|
if (i == 7) j++;
|
|
x &= 0x7f;
|
|
// isascii not part of std:: and may be a macro.
|
|
buffer2[i] = isascii(x) && isprint(x) ? x : '.';
|
|
}
|
|
|
|
buffer1[sizeof(buffer1) - 1] = 0;
|
|
buffer2[sizeof(buffer2) - 1] = 0;
|
|
|
|
printf("%06x:\t%s\t%s\n",
|
|
address, buffer1, buffer2);
|
|
address += 16;
|
|
size -= 16;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
|
|
void host_free_directory(char **data, size_t count) {
|
|
for (int i = 0; i < count; ++i) free(data[i]);
|
|
free(data);
|
|
}
|