From: beeanyew Date: Tue, 18 May 2021 11:09:45 +0000 (+0200) Subject: Adapt a314fs to work on PiStorm + A314 emulation X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=e9b74306a577f539e6d91d3139db7d462f13910a;p=pistorm Adapt a314fs to work on PiStorm + A314 emulation Requires a314fs in L:, a314fs-mountlist in DEVS:DosDrivers and somehow a314fs.py started after the emulator but before mounting the drive. Untested. --- diff --git a/a314/files_pi/a314fs.py b/a314/files_pi/a314fs.py new file mode 100644 index 0000000..75e0a27 --- /dev/null +++ b/a314/files_pi/a314fs.py @@ -0,0 +1,817 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2018-2021 Niklas Ekström + +import select +import sys +import socket +import time +import os +import struct +import glob +import logging +import json + +logging.basicConfig(format = '%(levelname)s, %(asctime)s, %(name)s, line %(lineno)d: %(message)s') +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +CONFIG_FILE_PATH = './a314fs.conf' + +SHARED_DIRECTORY = '/home/pi/a314shared' +METAFILE_EXTENSION = ':a314' + +with open(CONFIG_FILE_PATH, encoding='utf-8') as f: + cfg = json.load(f) + devs = cfg['devices'] + dev = devs['PI0'] + SHARED_DIRECTORY = dev['path'] + +MSG_REGISTER_REQ = 1 +MSG_REGISTER_RES = 2 +MSG_DEREGISTER_REQ = 3 +MSG_DEREGISTER_RES = 4 +MSG_READ_MEM_REQ = 5 +MSG_READ_MEM_RES = 6 +MSG_WRITE_MEM_REQ = 7 +MSG_WRITE_MEM_RES = 8 +MSG_CONNECT = 9 +MSG_CONNECT_RESPONSE = 10 +MSG_DATA = 11 +MSG_EOS = 12 +MSG_RESET = 13 + +def wait_for_msg(): + header = b'' + while len(header) < 9: + data = drv.recv(9 - len(header)) + if not data: + logger.error('Connection to a314d was closed, terminating.') + exit(-1) + header += data + (plen, stream_id, ptype) = struct.unpack('=IIB', header) + payload = b'' + while len(payload) < plen: + data = drv.recv(plen - len(payload)) + if not data: + logger.error('Connection to a314d was closed, terminating.') + exit(-1) + payload += data + return (stream_id, ptype, payload) + +def send_register_req(name): + m = struct.pack('=IIB', len(name), 0, MSG_REGISTER_REQ) + name + drv.sendall(m) + +def send_read_mem_req(address, length): + m = struct.pack('=IIBII', 8, 0, MSG_READ_MEM_REQ, address, length) + drv.sendall(m) + +def read_mem(address, length): + send_read_mem_req(address, length) + stream_id, ptype, payload = wait_for_msg() + if ptype != MSG_READ_MEM_RES: + logger.error('Expected MSG_READ_MEM_RES but got %s. Shutting down.', ptype) + exit(-1) + return payload + +def send_write_mem_req(address, data): + m = struct.pack('=IIBI', 4 + len(data), 0, MSG_WRITE_MEM_REQ, address) + data + drv.sendall(m) + +def write_mem(address, data): + send_write_mem_req(address, data) + stream_id, ptype, payload = wait_for_msg() + if ptype != MSG_WRITE_MEM_RES: + logger.error('Expected MSG_WRITE_MEM_RES but got %s. Shutting down.', ptype) + exit(-1) + +def send_connect_response(stream_id, result): + m = struct.pack('=IIBB', 1, stream_id, MSG_CONNECT_RESPONSE, result) + drv.sendall(m) + +def send_data(stream_id, data): + m = struct.pack('=IIB', len(data), stream_id, MSG_DATA) + data + drv.sendall(m) + +def send_eos(stream_id): + m = struct.pack('=IIB', 0, stream_id, MSG_EOS) + drv.sendall(m) + +def send_reset(stream_id): + m = struct.pack('=IIB', 0, stream_id, MSG_RESET) + drv.sendall(m) + +ACTION_NIL = 0 +ACTION_GET_BLOCK = 2 +ACTION_SET_MAP = 4 +ACTION_DIE = 5 +ACTION_EVENT = 6 +ACTION_CURRENT_VOLUME = 7 +ACTION_LOCATE_OBJECT = 8 +ACTION_RENAME_DISK = 9 +ACTION_WRITE = ord('W') +ACTION_READ = ord('R') +ACTION_FREE_LOCK = 15 +ACTION_DELETE_OBJECT = 16 +ACTION_RENAME_OBJECT = 17 +ACTION_MORE_CACHE = 18 +ACTION_COPY_DIR = 19 +ACTION_WAIT_CHAR = 20 +ACTION_SET_PROTECT = 21 +ACTION_CREATE_DIR = 22 +ACTION_EXAMINE_OBJECT = 23 +ACTION_EXAMINE_NEXT = 24 +ACTION_DISK_INFO = 25 +ACTION_INFO = 26 +ACTION_FLUSH = 27 +ACTION_SET_COMMENT = 28 +ACTION_PARENT = 29 +ACTION_TIMER = 30 +ACTION_INHIBIT = 31 +ACTION_DISK_TYPE = 32 +ACTION_DISK_CHANGE = 33 +ACTION_SET_DATE = 34 +ACTION_SAME_LOCK = 40 +ACTION_SCREEN_MODE = 994 +ACTION_READ_RETURN = 1001 +ACTION_WRITE_RETURN = 1002 +ACTION_FINDUPDATE = 1004 +ACTION_FINDINPUT = 1005 +ACTION_FINDOUTPUT = 1006 +ACTION_END = 1007 +ACTION_SEEK = 1008 +ACTION_TRUNCATE = 1022 +ACTION_WRITE_PROTECT = 1023 +ACTION_EXAMINE_FH = 1034 +ACTION_UNSUPPORTED = 65535 + +ERROR_NO_FREE_STORE = 103 +ERROR_TASK_TABLE_FULL = 105 +ERROR_LINE_TOO_LONG = 120 +ERROR_FILE_NOT_OBJECT = 121 +ERROR_INVALID_RESIDENT_LIBRARY = 122 +ERROR_NO_DEFAULT_DIR = 201 +ERROR_OBJECT_IN_USE = 202 +ERROR_OBJECT_EXISTS = 203 +ERROR_DIR_NOT_FOUND = 204 +ERROR_OBJECT_NOT_FOUND = 205 +ERROR_BAD_STREAM_NAME = 206 +ERROR_OBJECT_TOO_LARGE = 207 +ERROR_ACTION_NOT_KNOWN = 209 +ERROR_INVALID_COMPONENT_NAME = 210 +ERROR_INVALID_LOCK = 211 +ERROR_OBJECT_WRONG_TYPE = 212 +ERROR_DISK_NOT_VALIDATED = 213 +ERROR_DISK_WRITE_PROTECTED = 214 +ERROR_RENAME_ACROSS_DEVICES = 215 +ERROR_DIRECTORY_NOT_EMPTY = 216 +ERROR_TOO_MANY_LEVELS = 217 +ERROR_DEVICE_NOT_MOUNTED = 218 +ERROR_SEEK_ERROR = 219 +ERROR_COMMENT_TOO_BIG = 220 +ERROR_DISK_FULL = 221 +ERROR_DELETE_PROTECTED = 222 +ERROR_WRITE_PROTECTED = 223 +ERROR_READ_PROTECTED = 224 +ERROR_NOT_A_DOS_DISK = 225 +ERROR_NO_DISK = 226 +ERROR_NO_MORE_ENTRIES = 232 + +SHARED_LOCK = -2 +EXCLUSIVE_LOCK = -1 + +LOCK_DIFFERENT = -1 +LOCK_SAME = 0 +LOCK_SAME_VOLUME = 1 + +MODE_OLDFILE = 1005 # Open existing file read/write positioned at beginning of file. +MODE_NEWFILE = 1006 # Open freshly created file (delete old file) read/write, exclusive lock. +MODE_READWRITE = 1004 # Open old file w/shared lock, creates file if doesn't exist. + +OFFSET_BEGINNING = -1 # Relative to Begining Of File. +OFFSET_CURRENT = 0 # Relative to Current file position. +OFFSET_END = 1 # relative to End Of File. + +ST_ROOT = 1 +ST_USERDIR = 2 +ST_SOFTLINK = 3 # looks like dir, but may point to a file! +ST_LINKDIR = 4 # hard link to dir +ST_FILE = -3 # must be negative for FIB! +ST_LINKFILE = -4 # hard link to file +ST_PIPEFILE = -5 + +current_stream_id = 0 + +class ObjectLock(object): + def __init__(self, key, mode, path): + self.key = key + self.mode = mode + self.path = path + self.entry_it = None + +locks = {} + +next_key = 1 + +def get_key(): + global next_key + key = next_key + next_key = 1 if next_key == 0x7fffffff else (next_key + 1) + while key in locks: + key += 1 + return key + +def find_path(key, name): + i = name.find(':') + if i != -1: + vol = name[:i].lower() + if vol == '' or vol == 'pi0' or vol == 'pidisk': + key = 0 + name = name[i + 1:] + + if key == 0: + cp = () + else: + cp = locks[key].path + + while name: + i = name.find('/') + if i == -1: + comp = name + name = '' + else: + comp = name[:i] + name = name[i + 1:] + + if len(comp) == 0: + if len(cp) == 0: + return None + cp = cp[:-1] + else: + p = '.' if len(cp) == 0 else '/'.join(cp) + entries = os.listdir(p) + found = False + for e in entries: + if comp.lower() == e.lower(): + cp = cp + (e,) + found = True + break + if not found: + if len(name) == 0: + cp = cp + (comp,) + else: + return None + + return cp + +def read_metadata(path): + protection = 0 + comment = '' + + if not os.path.isfile(path + METAFILE_EXTENSION): + return (protection, comment) + + try: + f = open(path + METAFILE_EXTENSION, 'r') + for line in f: + if line[0] == 'p': + try: + protection = int(line[1:].strip()) + except ValueError: + pass + elif line[0] == 'c': + comment = line[1:].strip()[:79] + f.close() + except FileNotFoundError: + pass + return (protection, comment) + +def write_metadata(path, protection=None, comment=None): + p, c = read_metadata(path) + + if protection == None: + protection = p + if comment == None: + comment = c + + if (p, c) == (protection, comment): + return True + + try: + f = open(path + METAFILE_EXTENSION, 'w') + f.write('p' + str(protection) + '\n') + f.write('c' + comment + '\n') + f.close() + except FileNotFoundError as e: + logger.warning('Failed to write metadata for file %s: %s', path, e) + return False + return True + +def process_locate_object(key, mode, name): + logger.debug('ACTION_LOCATE_OBJECT, key: %s, mode: %s, name: %s', key, mode, name) + + cp = find_path(key, name) + + if cp is None or not (len(cp) == 0 or os.path.exists('/'.join(cp))): + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + # TODO: Must check if there is already a lock for this path, + # and if so, if the locks are compatible. + + key = get_key() + locks[key] = ObjectLock(key, mode, cp) + return struct.pack('>HHI', 1, 0, key) + +def process_free_lock(key): + logger.debug('ACTION_FREE_LOCK, key: %s', key) + if key in locks: + del locks[key] + return struct.pack('>HH', 1, 0) + +def process_copy_dir(prev_key): + logger.debug('ACTION_COPY_DIR, prev_key: %s', prev_key) + ol = locks[prev_key] + key = get_key() + locks[key] = ObjectLock(key, ol.mode, ol.path) + return struct.pack('>HHI', 1, 0, key) + +def process_parent(prev_key): + logger.debug('ACTION_PARENT, prev_key: %s', prev_key) + ol = locks[prev_key] + if len(ol.path) == 0: + key = 0 + else: + key = get_key() + locks[key] = ObjectLock(key, SHARED_LOCK, ol.path[:-1]) + return struct.pack('>HHI', 1, 0, key) + +def mtime_to_dmt(mtime): + mtime = int(mtime) + days = mtime // 86400 + left = mtime - days * 86400 + mins = left // 60 + secs = left - mins * 60 + ticks = secs * 50 + days -= 2922 # Days between 1970-01-01 and 1978-01-01 + days = max(0, days) # If days are before Amiga epoc + return (days, mins, ticks) + +def process_examine_object(key): + logger.debug('ACTION_EXAMINE_OBJECT, key: %s', key) + ol = locks[key] + + if len(ol.path) == 0: + fn = 'PiDisk' + path = '.' + else: + fn = ol.path[-1] + path = '/'.join(ol.path) + + days, mins, ticks = mtime_to_dmt(os.path.getmtime(path)) + protection, comment = read_metadata(path) + + if os.path.isfile(path): + size = os.path.getsize(path) + type_ = ST_FILE + else: + size = 0 + type_ = ST_USERDIR + ol.entry_it = os.scandir(path) + + size = min(size, 2 ** 31 - 1) + fn = (chr(len(fn)) + fn).encode('latin-1', 'ignore') + comment = (chr(len(comment)) + comment).encode('latin-1', 'ignore') + return struct.pack('>HHHhIIIII', 1, 0, 666, type_, size, protection, days, mins, ticks) + fn + comment + +def process_examine_next(key, disk_key): + logger.debug('ACTION_EXAMINE_NEXT, key: %s, disk_key: %s', key, disk_key) + ol = locks[key] + + if len(ol.path) == 0: + path = '.' + else: + path = '/'.join(ol.path) + + if not os.path.isdir(path): + return struct.pack('>HH', 0, ERROR_OBJECT_WRONG_TYPE) + + disk_key += 1 + + entry = next(ol.entry_it, None) + while entry and entry.name.endswith(METAFILE_EXTENSION): + entry = next(ol.entry_it, None) + + if not entry: + return struct.pack('>HH', 0, ERROR_NO_MORE_ENTRIES) + + fn = entry.name + path = ('/'.join(ol.path + (fn,))) + + days, mins, ticks = mtime_to_dmt(entry.stat().st_mtime) + protection, comment = read_metadata(path) + + if os.path.isfile(path): + size = os.path.getsize(path) + type_ = ST_FILE + else: + size = 0 + type_ = ST_USERDIR + + size = min(size, 2 ** 31 - 1) + fn = (chr(len(fn)) + fn).encode('latin-1', 'ignore') + comment = (chr(len(comment)) + comment).encode('latin-1', 'ignore') + return struct.pack('>HHHhIIIII', 1, 0, disk_key, type_, size, protection, days, mins, ticks) + fn + comment + +def process_examine_fh(arg1): + logger.debug('ACTION_EXAMINE_FH, arg1: %s', arg1) + + fn = open_file_handles[arg1].f.name + path = os.path.realpath(fn) + days, mins, ticks = mtime_to_dmt(os.path.getmtime(path)) + protection, comment = read_metadata(path) + + if os.path.isfile(path): + size = os.path.getsize(path) + type_ = ST_FILE + else: + size = 0 + type_ = ST_USERDIR + + size = min(size, 2 ** 31 - 1) + fn = (chr(len(fn)) + fn).encode('latin-1', 'ignore') + comment = (chr(len(comment)) + comment).encode('latin-1', 'ignore') + return struct.pack('>HHHhIIIII', 1, 0, 666, type_, size, protection, days, mins, ticks) + fn + comment + + +next_fp = 1 + +open_file_handles = {} + +def get_file_ptr(): + global next_fp + fp = next_fp + next_fp = 1 if next_fp == 0x7fffffff else next_fp + 1 + while fp in open_file_handles: + fp += 1 + return fp + +class OpenFileHandle(object): + def __init__(self, fp, f, p): + self.fp = fp + self.f = f + self.p = p + +def process_findxxx(mode, key, name): + if mode == ACTION_FINDINPUT: + logger.debug('ACTION_FINDINPUT, key: %s, name: %s', key, name) + elif mode == ACTION_FINDOUTPUT: + logger.debug('ACTION_FINDOUTPUT, key: %s, name: %s', key, name) + elif mode == ACTION_FINDUPDATE: + logger.debug('ACTION_FINDUPDATE, key: %s, name: %s', key, name) + + cp = find_path(key, name) + if cp is None: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + path = '/'.join(cp) + if len(cp) == 0 or os.path.isdir(path): + return struct.pack('>HH', 0, ERROR_OBJECT_WRONG_TYPE) + + # TODO: Must check if there already exists a non-compatible lock for this path. + + # TODO: This must be handled better. Especially error reporting. + + protection, _ = read_metadata(path) + try: + if mode == MODE_OLDFILE: + f = open(path, 'r+b') + elif mode == MODE_READWRITE: + f = open(path, 'r+b') + if protection & 16: + protection = protection & 0b11101111 + write_metadata(path, protection=protection) + elif mode == MODE_NEWFILE: + if protection & 0x1: + return struct.pack('>HH', 0, ERROR_DELETE_PROTECTED) + elif protection & 0x4: + return struct.pack('>HH', 0, ERROR_WRITE_PROTECTED) + f = open(path, 'w+b') + if protection & 16: + protection = protection & 0b11101111 + write_metadata(path, protection=protection) + except IOError: + if mode == MODE_READWRITE: + try: + f = open(path, 'w+b') + if protection & 16: + protection = protection & 0b11101111 + write_metadata(path, protection=protection) + except IOError: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + else: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + fp = get_file_ptr() + ofh = OpenFileHandle(fp, f, protection) + open_file_handles[fp] = ofh + + return struct.pack('>HHI', 1, 0, fp) + +def process_read(arg1, address, length): + logger.debug('ACTION_READ, arg1: %s, address: %s, length: %s', arg1, address, length) + protection = open_file_handles[arg1].p + if protection & 0x8: + return struct.pack('>HH', 0, ERROR_READ_PROTECTED) + f = open_file_handles[arg1].f + data = f.read(length) + if len(data) != 0: + write_mem(address, data) + return struct.pack('>HHI', 1, 0, len(data)) + +def process_write(arg1, address, length): + logger.debug('ACTION_WRITE, arg1: %s, address: %s, length: %s', arg1, address, length) + protection = open_file_handles[arg1].p + if protection & 0x4: + return struct.pack('>HH', 0, ERROR_WRITE_PROTECTED) + data = read_mem(address, length) + f = open_file_handles[arg1].f + f.seek(0, 1) + try: + f.write(data) + except IOError: + return struct.pack('>HH', 0, ERROR_DISK_FULL) + return struct.pack('>HHI', 1, 0, length) + +def process_seek(arg1, new_pos, mode): + logger.debug('ACTION_SEEK, arg1: %s, new_pos: %s, mode: %s', arg1, new_pos, mode) + + f = open_file_handles[arg1].f + old_pos = f.tell() + + from_what = 0 + if mode == OFFSET_CURRENT: + from_what = 1 + elif mode == OFFSET_END: + from_what = 2 + + f.seek(new_pos, from_what) + + return struct.pack('>HHi', 1, 0, old_pos) + +def process_end(arg1): + logger.debug('ACTION_END, arg1: %s', arg1) + + if arg1 in open_file_handles: + f = open_file_handles.pop(arg1).f + f.close() + + return struct.pack('>HH', 1, 0) + +def process_delete_object(key, name): + logger.debug('ACTION_DELETE_OBJECT, key: %s, name: %s', key, name) + + cp = find_path(key, name) + if cp is None or len(cp) == 0: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + path = '/'.join(cp) + is_dir = os.path.isdir(path) + + protection, _ = read_metadata(path) + if protection & 0x1: + return struct.pack('>HH', 0, ERROR_DELETE_PROTECTED) + + try: + if is_dir: + os.rmdir(path) + else: + os.remove(path) + if os.path.isfile(path + METAFILE_EXTENSION): + os.remove(path + METAFILE_EXTENSION) + except: + if is_dir: + return struct.pack('>HH', 0, ERROR_DIRECTORY_NOT_EMPTY) + else: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + return struct.pack('>HH', 1, 0) + +def process_rename_object(key, name, target_dir, new_name): + logger.debug('ACTION_RENAME_OBJECT, key: %s, name: %s, target_dir: %s, new_name: %s', key, name, target_dir, new_name) + + cp1 = find_path(key, name) + if cp1 is None or len(cp1) == 0: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + from_path = '/'.join(cp1) + if not os.path.exists(from_path): + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + cp2 = find_path(target_dir, new_name) + if cp2 is None or len(cp2) == 0: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + to_path = '/'.join(cp2) + + if from_path == to_path: + return struct.pack('>HH', 1, 0) + + if os.path.exists(to_path): + return struct.pack('>HH', 0, ERROR_OBJECT_EXISTS) + + try: + os.rename(from_path, to_path) + if os.path.isfile(from_path + METAFILE_EXTENSION): + os.rename(from_path + METAFILE_EXTENSION, to_path + METAFILE_EXTENSION) + except: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + return struct.pack('>HH', 1, 0) + +def process_create_dir(key, name): + logger.debug('ACTION_CREATE_DIR, key: %s, name: %s', key, name) + + cp = find_path(key, name) + if cp is None or len(cp) == 0: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + try: + path = '/'.join(cp) + os.makedirs(path) + except: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + key = get_key() + locks[key] = ObjectLock(key, SHARED_LOCK, cp) + return struct.pack('>HHI', 1, 0, key) + +def process_set_protect(key, name, mask): + logger.debug('ACTION_SET_PROTECT, key: %s, name: %s, mask: %s', key, name, mask) + + cp = find_path(key, name) + if cp is None or len(cp) == 0: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + path = '/'.join(cp) + if write_metadata(path, protection=mask): + return struct.pack('>HH', 1, 0) + else: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + +def process_set_comment(key, name, comment): + logger.debug('ACTION_SET_COMMENT, key: %s, name: %s, comment: %s', key, name, comment) + + if len(comment) > 79: + return struct.pack('>HH', 0, ERROR_COMMENT_TOO_BIG) + + cp = find_path(key, name) + if cp is None or len(cp) == 0: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + + path = '/'.join(cp) + if write_metadata(path, comment=comment): + return struct.pack('>HH', 1, 0) + else: + return struct.pack('>HH', 0, ERROR_OBJECT_NOT_FOUND) + +def process_same_lock(key1, key2): + logger.debug('ACTION_SAME_LOCK, key1: %s key2: %s', key1, key2) + + if not (key1 in locks and key2 in locks): + return struct.pack('>HH', 0, LOCK_DIFFERENT) + elif locks[key1].path == locks[key2].path: + return struct.pack('>HH', 1, LOCK_SAME) + else: + return struct.pack('>HH', 0, LOCK_SAME_VOLUME) + +def process_request(req): + #logger.debug('len(req): %s, req: %s', len(req), list(req)) + + (rtype,) = struct.unpack('>H', req[:2]) + + if rtype == ACTION_LOCATE_OBJECT: + key, mode, nlen = struct.unpack('>IHB', req[2:9]) + name = req[9:9+nlen].decode('latin-1') + return process_locate_object(key, mode, name) + elif rtype == ACTION_FREE_LOCK: + (key,) = struct.unpack('>I', req[2:6]) + return process_free_lock(key) + elif rtype == ACTION_COPY_DIR: + (key,) = struct.unpack('>I', req[2:6]) + return process_copy_dir(key) + elif rtype == ACTION_PARENT: + (key,) = struct.unpack('>I', req[2:6]) + return process_parent(key) + elif rtype == ACTION_EXAMINE_OBJECT: + (key,) = struct.unpack('>I', req[2:6]) + return process_examine_object(key) + elif rtype == ACTION_EXAMINE_NEXT: + key, disk_key = struct.unpack('>IH', req[2:8]) + return process_examine_next(key, disk_key) + elif rtype == ACTION_EXAMINE_FH: + (arg1,) = struct.unpack('>I', req[2:6]) + return process_examine_fh(arg1) + elif rtype == ACTION_FINDINPUT or rtype == ACTION_FINDOUTPUT or rtype == ACTION_FINDUPDATE: + key, nlen = struct.unpack('>IB', req[2:7]) + name = req[7:7+nlen].decode('latin-1') + return process_findxxx(rtype, key, name) + elif rtype == ACTION_READ: + arg1, address, length = struct.unpack('>III', req[2:14]) + return process_read(arg1, address, length) + elif rtype == ACTION_WRITE: + arg1, address, length = struct.unpack('>III', req[2:14]) + return process_write(arg1, address, length) + elif rtype == ACTION_SEEK: + arg1, new_pos, mode = struct.unpack('>Iii', req[2:14]) + return process_seek(arg1, new_pos, mode) + elif rtype == ACTION_END: + (arg1,) = struct.unpack('>I', req[2:6]) + return process_end(arg1) + elif rtype == ACTION_DELETE_OBJECT: + key, nlen = struct.unpack('>IB', req[2:7]) + name = req[7:7+nlen].decode('latin-1') + return process_delete_object(key, name) + elif rtype == ACTION_RENAME_OBJECT: + key, target_dir, nlen, nnlen = struct.unpack('>IIBB', req[2:12]) + name = req[12:12+nlen].decode('latin-1') + new_name = req[12+nlen:12+nlen+nnlen].decode('latin-1') + return process_rename_object(key, name, target_dir, new_name) + elif rtype == ACTION_CREATE_DIR: + key, nlen = struct.unpack('>IB', req[2:7]) + name = req[7:7+nlen].decode('latin-1') + return process_create_dir(key, name) + elif rtype == ACTION_SET_PROTECT: + key, mask, nlen = struct.unpack('>IIB', req[2:11]) + name = req[11:11+nlen].decode('latin-1') + return process_set_protect(key, name, mask) + elif rtype == ACTION_SET_COMMENT: + key, nlen, clen = struct.unpack('>IBB', req[2:8]) + name = req[8:8+nlen].decode('latin-1') + comment = req[8+nlen:8+nlen+clen].decode('latin-1') + return process_set_comment(key, name, comment) + elif rtype == ACTION_SAME_LOCK: + key1, key2 = struct.unpack('>II', req[2:10]) + return process_same_lock(key1, key2) + elif rtype == ACTION_UNSUPPORTED: + (dp_Type,) = struct.unpack('>H', req[2:4]) + logger.warning('Unsupported action %d (Amiga/a314fs)', dp_Type) + return struct.pack('>HH', 0, ERROR_ACTION_NOT_KNOWN) + else: + logger.warning('Unsupported action %d (a314d/a314fs)', rtype) + return struct.pack('>HH', 0, ERROR_ACTION_NOT_KNOWN) + +done = False + +try: + idx = sys.argv.index('-ondemand') +except ValueError: + idx = -1 + +if idx != -1: + fd = int(sys.argv[idx + 1]) + drv = socket.socket(fileno=fd) +else: + drv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + drv.connect(('localhost', 7110)) + + send_register_req(b'a314fs') + stream_id, ptype, payload = wait_for_msg() + if payload[0] != 1: + logger.error('Unable to register service a314fs, shutting down') + drv.close() + done = True + +if not done: + os.chdir(SHARED_DIRECTORY) + logger.info('a314fs is running, shared directory: %s', SHARED_DIRECTORY) + +while not done: + stream_id, ptype, payload = wait_for_msg() + + if ptype == MSG_CONNECT: + if payload == b'a314fs': + if current_stream_id is not None: + send_reset(current_stream_id) + current_stream_id = stream_id + send_connect_response(stream_id, 0) + else: + send_connect_response(stream_id, 3) + elif ptype == MSG_DATA: + address, length = struct.unpack('>II', payload) + #logger.debug('address: %s, length: %s', address, length) + req = read_mem(address + 2, length - 2) + res = process_request(req) + write_mem(address + 2, res) + #write_mem(address, b'\xff\xff') + send_data(stream_id, b'\xff\xff') + elif ptype == MSG_EOS: + if stream_id == current_stream_id: + logger.debug('Got EOS, stream closed') + send_eos(stream_id) + current_stream_id = None + elif ptype == MSG_RESET: + if stream_id == current_stream_id: + logger.debug('Got RESET, stream closed') + current_stream_id = None diff --git a/a314/software-amiga/a314fs b/a314/software-amiga/a314fs index 1d51c6a..151aa9d 100644 Binary files a/a314/software-amiga/a314fs and b/a314/software-amiga/a314fs differ diff --git a/a314/software-amiga/a314fs-mountlist b/a314/software-amiga/a314fs-mountlist new file mode 100644 index 0000000..4f8e20c --- /dev/null +++ b/a314/software-amiga/a314fs-mountlist @@ -0,0 +1,12 @@ +PI0: FileSystem = l:a314fs + Device = a314.device + Unit = 0 + Surfaces = 1 + BlocksPerTrack = 10 + Reserved = 2 + LowCyl = 0 + HighCyl = 100 + StackSize = 1000 + Mount = 1 + DosType = 0x33313400 +# diff --git a/a314/software-amiga/a314fs_pistorm/a314fs.c b/a314/software-amiga/a314fs_pistorm/a314fs.c new file mode 100644 index 0000000..b07e801 --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/a314fs.c @@ -0,0 +1,1305 @@ +/* + * Copyright (c) 2018-2021 Niklas Ekström + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "../../a314device/a314.h" +#include "../../a314device/proto_a314.h" + +#include "messages.h" + +#define MKBADDRU(x) (((ULONG)x) >> 2) + +#define ID_314_DISK (('3' << 24) | ('1' << 16) | ('4' << 8)) + +#define REQ_RES_BUF_SIZE 256 +#define BUFFER_SIZE 4096 + +// Not defined if using NDK13 +#ifndef ACTION_EXAMINE_FH +#define ACTION_EXAMINE_FH 1034 +#endif + +#ifndef ACTION_SAME_LOCK +#define ACTION_SAME_LOCK 40 +#endif + +// Grab a reserved action type which seems to be unused +#define ACTION_UNSUPPORTED 65535 + +struct ExecBase *SysBase; +struct DosLibrary *DOSBase; +struct MsgPort *mp; + +struct DeviceList *my_volume; +char default_volume_name[] = "\006PiDisk"; + +char device_name[32]; // "\004PI0:" + +struct MsgPort *timer_mp; +struct timerequest *tr; + +struct MsgPort *a314_mp; +struct A314_IORequest *a314_ior; + +struct Library *A314Base; + +long socket; + +// These are allocated in A314 memory. +char *request_buffer = NULL; +char *data_buffer = NULL; + +void MyNewList(struct List *l) +{ + l->lh_Head = (struct Node *)&(l->lh_Tail); + l->lh_Tail = NULL; + l->lh_TailPred = (struct Node *)&(l->lh_Head); +} + +struct MsgPort *MyCreatePort(char *name, long pri) +{ + int sigbit = AllocSignal(-1); + if (sigbit == -1) + return NULL; + + struct MsgPort *port = (struct MsgPort *)AllocMem(sizeof(struct MsgPort), MEMF_PUBLIC | MEMF_CLEAR); + if (!port) + { + FreeSignal(sigbit); + return NULL; + } + + port->mp_Node.ln_Name = name; + port->mp_Node.ln_Pri = pri; + port->mp_Node.ln_Type = NT_MSGPORT; + port->mp_Flags = PA_SIGNAL; + port->mp_SigBit = sigbit; + port->mp_SigTask = FindTask(NULL); + + if (name) + AddPort(port); + else + MyNewList(&(port->mp_MsgList)); + + return port; +} + +struct IORequest *MyCreateExtIO(struct MsgPort *ioReplyPort, ULONG size) +{ + if (!ioReplyPort) + return NULL; + + struct IORequest *ioReq = (struct IORequest *)AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR); + if (!ioReq) + return NULL; + + ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE; + ioReq->io_Message.mn_Length = size; + ioReq->io_Message.mn_ReplyPort = ioReplyPort; + + return ioReq; +} + +#define DEBUG 0 + +#if !DEBUG +void dbg_init() +{ +} + +void dbg(const char* fmt, ...) +{ +} +#else +struct FileHandle *dbg_log; +struct MsgPort *dbg_replyport; +struct StandardPacket *dbg_sp; +char dbg_buf[256]; + +void dbg_init() +{ + dbg_log = (struct FileHandle *)BADDR(Open("CON:200/0/440/256/a314fs", MODE_NEWFILE)); + dbg_replyport = MyCreatePort(NULL, 0); + dbg_sp = (struct StandardPacket *)AllocMem(sizeof(struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR); +} + +void dbg_out(int l) +{ + dbg_sp->sp_Msg.mn_Node.ln_Name = (char *)&(dbg_sp->sp_Pkt); + dbg_sp->sp_Pkt.dp_Link = &(dbg_sp->sp_Msg); + dbg_sp->sp_Pkt.dp_Port = dbg_replyport; + dbg_sp->sp_Pkt.dp_Type = ACTION_WRITE; + dbg_sp->sp_Pkt.dp_Arg1 = (long)dbg_log->fh_Arg1; + dbg_sp->sp_Pkt.dp_Arg2 = (long)dbg_buf; + dbg_sp->sp_Pkt.dp_Arg3 = (long)l - 1; + + PutMsg((struct MsgPort *)dbg_log->fh_Type, (struct Message *)dbg_sp); + WaitPort(dbg_replyport); + GetMsg(dbg_replyport); +} + +void dbg(const char* fmt, ...) +{ + char numbuf[16]; + + const char *p = fmt; + char *q = dbg_buf; + + va_list args; + va_start(args, fmt); + while (*p != 0) + { + char c = *p++; + if (c == '$') + { + c = *p++; + if (c == 'b') + { + UBYTE x = va_arg(args, UBYTE); + *q++ = '$'; + for (int i = 0; i < 2; i++) + { + int ni = (x >> ((1 - i) * 4)) & 0xf; + *q++ = (ni >= 10) ? ('a' + (ni - 10)) : ('0' + ni); + } + } + else if (c == 'w') + { + UWORD x = va_arg(args, UWORD); + *q++ = '$'; + for (int i = 0; i < 4; i++) + { + int ni = (x >> ((3 - i) * 4)) & 0xf; + *q++ = (ni >= 10) ? ('a' + (ni - 10)) : ('0' + ni); + } + } + else if (c == 'l') + { + ULONG x = va_arg(args, ULONG); + *q++ = '$'; + for (int i = 0; i < 8; i++) + { + int ni = (x >> ((7 - i) * 4)) & 0xf; + *q++ = (ni >= 10) ? ('a' + (ni - 10)) : ('0' + ni); + } + } + else if (c == 'S') + { + unsigned char *s = (unsigned char *)va_arg(args, ULONG); + int l = *s++; + for (int i = 0; i < l; i++) + *q++ = *s++; + } + else if (c == 's') + { + unsigned char *s = (unsigned char *)va_arg(args, ULONG); + while (*s) + *q++ = *s++; + *q++ = 0; + } + } + else + { + *q++ = c; + } + } + *q++ = 0; + + va_end(args); + + dbg_out(q - dbg_buf); +} +#endif + +char *DosAllocMem(int len) +{ + long *p = (long *)AllocMem(len + 4, MEMF_PUBLIC | MEMF_CLEAR); + *p++ = len; + return((char *)p); +} + +void DosFreeMem(char *p) +{ + long *lp = (long *)p; + long len = *--lp; + FreeMem((char *)lp, len); +} + +void reply_packet(struct DosPacket *dp) +{ + struct MsgPort *reply_port = dp->dp_Port; + dp->dp_Port = mp; + PutMsg(reply_port, dp->dp_Link); +} + +// Routines for talking to the A314 driver. +LONG a314_cmd_wait(UWORD command, char *buffer, int length) +{ + a314_ior->a314_Request.io_Command = command; + a314_ior->a314_Request.io_Error = 0; + a314_ior->a314_Socket = socket; + a314_ior->a314_Buffer = buffer; + a314_ior->a314_Length = length; + return DoIO((struct IORequest *)a314_ior); +} + +LONG a314_connect(char *name) +{ + struct DateStamp ds; + DateStamp(&ds); + socket = ds.ds_Tick; + return a314_cmd_wait(A314_CONNECT, name, strlen(name)); +} + +LONG a314_read(char *buf, int length) +{ + return a314_cmd_wait(A314_READ, buf, length); +} + +LONG a314_write(char *buf, int length) +{ + return a314_cmd_wait(A314_WRITE, buf, length); +} + +LONG a314_eos() +{ + return a314_cmd_wait(A314_EOS, NULL, 0); +} + +LONG a314_reset() +{ + return a314_cmd_wait(A314_RESET, NULL, 0); +} + +void create_and_add_volume() +{ + my_volume = (struct DeviceList *)DosAllocMem(sizeof(struct DeviceList)); + my_volume->dl_Name = MKBADDRU(default_volume_name); + my_volume->dl_Type = DLT_VOLUME; + my_volume->dl_Task = mp; + my_volume->dl_DiskType = ID_314_DISK; + DateStamp(&my_volume->dl_VolumeDate); + + struct RootNode *root = (struct RootNode *)DOSBase->dl_Root; + struct DosInfo *info = (struct DosInfo *)BADDR(root->rn_Info); + + Forbid(); + my_volume->dl_Next = info->di_DevInfo; + info->di_DevInfo = MKBADDRU(my_volume); + Permit(); +} + +void startup_fs_handler(struct DosPacket *dp) +{ + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg1); + struct FileSysStartupMsg *fssm = (struct FileSysStartupMsg *)BADDR(dp->dp_Arg2); + struct DeviceNode *node = (struct DeviceNode *)BADDR(dp->dp_Arg3); + + memcpy(device_name, name, *name + 1); + device_name[*name + 1] = 0; + + node->dn_Task = mp; + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + reply_packet(dp); + + dbg_init(); + + dbg("ACTION_STARTUP\n"); + dbg(" device_name = $S\n", (ULONG)device_name); + dbg(" fssm = $l\n", (ULONG)fssm); + dbg(" node = $l\n", (ULONG)node); + + timer_mp = MyCreatePort(NULL, 0); + tr = (struct timerequest *)MyCreateExtIO(timer_mp, sizeof(struct timerequest)); + if (OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)tr, 0) != 0) + { + // If this happens, there's nothing we can do. + // For now, assume this does not happen. + dbg("Fatal error: unable to open timer.device\n"); + return; + } + + a314_mp = MyCreatePort(NULL, 0); + a314_ior = (struct A314_IORequest *)MyCreateExtIO(a314_mp, sizeof(struct A314_IORequest)); + if (OpenDevice(A314_NAME, 0, (struct IORequest *)a314_ior, 0) != 0) + { + // If this fails, there's nothing we can do. + // For now, assume this does not happen. + dbg("Fatal error: unable to open a314.device\n"); + return; + } + + A314Base = &(a314_ior->a314_Request.io_Device->dd_Library); + + if (a314_connect("a314fs") != A314_CONNECT_OK) + { + dbg("Fatal error: unable to connect to a314fs on rasp\n"); + // This COULD happen. + // If it DOES happen, we just wait for a bit and try again. + + // TODO: Have to use timer.device to set a timer for ~2 seconds and then try connecting again. + return; + } + + request_buffer = AllocMem(REQ_RES_BUF_SIZE, MEMF_FAST); + data_buffer = AllocMem(BUFFER_SIZE, MEMF_FAST); + + // We can assume that we arrive here, and have a stream to the Pi side, to where we can transfer data. + create_and_add_volume(); + + dbg("Startup successful\n"); + + // If we end up having problems with the connections, treat the disc as ejected, and try inserting it again in two seconds. + // TODO: This is not currently handled. +} + +void wait_for_response() +{ + for (unsigned long i = 0; 1; i++) + { + if (*request_buffer) + { + dbg("--Got response after $l sleeps\n", i); + return; + } + + tr->tr_node.io_Command = TR_ADDREQUEST; + tr->tr_node.io_Message.mn_ReplyPort = timer_mp; + tr->tr_time.tv_secs = 0; + tr->tr_time.tv_micro = 1000; + DoIO((struct IORequest *)tr); + } +} + +void write_req_and_wait_for_res(int len) +{ + ULONG buf[2] = {TranslateAddressA314(request_buffer), len}; + a314_write((char *)&buf[0], 8); + //wait_for_response(); + a314_read((char *)&buf[0], 8); +} + +struct FileLock *create_and_add_file_lock(long key, long mode) +{ + struct FileLock *lock = (struct FileLock *)DosAllocMem(sizeof(struct FileLock)); + + lock->fl_Key = key; + lock->fl_Access = mode; + lock->fl_Task = mp; + lock->fl_Volume = MKBADDRU(my_volume); + + Forbid(); + lock->fl_Link = my_volume->dl_Lock; + my_volume->dl_Lock = MKBADDRU(lock); + Permit(); + + return lock; +} + +void action_locate_object(struct DosPacket *dp) +{ + struct FileLock *parent = (struct FileLock *)BADDR(dp->dp_Arg1); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2); + long mode = dp->dp_Arg3; + + dbg("ACTION_LOCATE_OBJECT\n"); + dbg(" parent lock = $l\n", parent); + dbg(" name = $S\n", name); + dbg(" mode = $s\n", mode == SHARED_LOCK ? "SHARED_LOCK" : "EXCLUSIVE_LOCK"); + + struct LocateObjectRequest *req = (struct LocateObjectRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = parent == NULL ? 0 : parent->fl_Key; + req->mode = mode; + + int nlen = *name; + memcpy(req->name, name, nlen + 1); + + write_req_and_wait_for_res(sizeof(struct LocateObjectRequest) + nlen); + + struct LocateObjectResponse *res = (struct LocateObjectResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + struct FileLock *lock = create_and_add_file_lock(res->key, mode); + + dbg(" Returning lock $l\n", lock); + dp->dp_Res1 = MKBADDRU(lock); + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_free_lock(struct DosPacket *dp) +{ + ULONG arg1 = dp->dp_Arg1; + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + + dbg("ACTION_FREE_LOCK\n"); + dbg(" lock = $l\n", lock); + + if (lock != NULL) + { + struct FreeLockRequest *req = (struct FreeLockRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock->fl_Key; + + write_req_and_wait_for_res(sizeof(struct FreeLockRequest)); + + // Ignore the response. Must succeed. + //struct FreeLockResponse *res = (struct FreeLockResponse *)request_buffer; + + Forbid(); + if (my_volume->dl_Lock == arg1) + my_volume->dl_Lock = lock->fl_Link; + else + { + struct FileLock *prev = (struct FileLock *)BADDR(my_volume->dl_Lock); + while (prev->fl_Link != arg1) + prev = (struct FileLock *)BADDR(prev->fl_Link); + prev->fl_Link = lock->fl_Link; + } + Permit(); + DosFreeMem((char *)lock); + } + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + reply_packet(dp); +} + +void action_copy_dir(struct DosPacket *dp) +{ + struct FileLock *parent = (struct FileLock *)BADDR(dp->dp_Arg1); + + dbg("ACTION_COPY_DIR\n"); + dbg(" lock to duplicate = $l\n", parent); + + if (parent == NULL) + { + dp->dp_Res1 = 0; + dp->dp_Res2 = 0; + } + else + { + struct CopyDirRequest *req = (struct CopyDirRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = parent->fl_Key; + + write_req_and_wait_for_res(sizeof(struct CopyDirRequest)); + + struct CopyDirResponse *res = (struct CopyDirResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + struct FileLock *lock = create_and_add_file_lock(res->key, parent->fl_Access); + + dbg(" Returning lock $l\n", lock); + dp->dp_Res1 = MKBADDRU(lock); + dp->dp_Res2 = 0; + } + } + + reply_packet(dp); +} + +void action_parent(struct DosPacket *dp) +{ + struct FileLock *prev_lock = (struct FileLock *)BADDR(dp->dp_Arg1); + + dbg("ACTION_PARENT\n"); + dbg(" lock = $l\n", prev_lock); + + if (prev_lock == NULL) + { + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = ERROR_INVALID_LOCK; + } + else + { + struct ParentRequest *req = (struct ParentRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = prev_lock->fl_Key; + + write_req_and_wait_for_res(sizeof(struct ParentRequest)); + + struct ParentResponse *res = (struct ParentResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else if (res->key == 0) + { + dp->dp_Res1 = 0; + dp->dp_Res2 = 0; + } + else + { + struct FileLock *lock = create_and_add_file_lock(res->key, SHARED_LOCK); + + dbg(" Returning lock $l\n", lock); + dp->dp_Res1 = MKBADDRU(lock); + dp->dp_Res2 = 0; + } + } + + reply_packet(dp); +} + +void action_examine_object(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR(dp->dp_Arg2); + + dbg("ACTION_EXAMINE_OBJECT\n"); + dbg(" lock = $l\n", lock); + dbg(" fib = $l\n", fib); + + struct ExamineObjectRequest *req = (struct ExamineObjectRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + + write_req_and_wait_for_res(sizeof(struct ExamineObjectRequest)); + + struct ExamineObjectResponse *res = (struct ExamineObjectResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + int nlen = (unsigned char)(res->data[0]); + memcpy(fib->fib_FileName, res->data, nlen + 1); + fib->fib_FileName[nlen + 1] = 0; + + int clen = (unsigned char)(res->data[nlen + 1]); + memcpy(fib->fib_Comment, &(res->data[nlen + 1]), clen + 1); + fib->fib_Comment[clen + 1] = 0; + + fib->fib_DiskKey = res->disk_key; + fib->fib_DirEntryType = res->entry_type; + fib->fib_EntryType = res->entry_type; + fib->fib_Protection = res->protection; + fib->fib_Size = res->size; + fib->fib_NumBlocks = (res->size + 511) >> 9; + fib->fib_Date.ds_Days = res->date[0]; + fib->fib_Date.ds_Minute = res->date[1]; + fib->fib_Date.ds_Tick = res->date[2]; + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_examine_next(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR(dp->dp_Arg2); + + dbg("ACTION_EXAMINE_NEXT\n"); + dbg(" lock = $l\n", lock); + dbg(" fib = $l\n", fib); + + struct ExamineNextRequest *req = (struct ExamineNextRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + req->disk_key = fib->fib_DiskKey; + + write_req_and_wait_for_res(sizeof(struct ExamineNextRequest)); + + struct ExamineNextResponse *res = (struct ExamineNextResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + int nlen = (unsigned char)(res->data[0]); + memcpy(fib->fib_FileName, res->data, nlen + 1); + fib->fib_FileName[nlen + 1] = 0; + + int clen = (unsigned char)(res->data[nlen + 1]); + memcpy(fib->fib_Comment, &(res->data[nlen + 1]), clen + 1); + fib->fib_Comment[clen + 1] = 0; + + fib->fib_DiskKey = res->disk_key; + fib->fib_DirEntryType = res->entry_type; + fib->fib_EntryType = res->entry_type; + fib->fib_Protection = res->protection; + fib->fib_Size = res->size; + fib->fib_NumBlocks = (res->size + 511) >> 9; + fib->fib_Date.ds_Days = res->date[0]; + fib->fib_Date.ds_Minute = res->date[1]; + fib->fib_Date.ds_Tick = res->date[2]; + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_examine_fh(struct DosPacket *dp) +{ + ULONG arg1 = dp->dp_Arg1; + struct FileInfoBlock *fib = (struct FileInfoBlock *)BADDR(dp->dp_Arg2); + + dbg("ACTION_EXAMINE_FH\n"); + dbg(" arg1 = $l\n", arg1); + dbg(" fib = $l\n", fib); + + struct ExamineFhRequest *req = (struct ExamineFhRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->arg1 = arg1; + + write_req_and_wait_for_res(sizeof(struct ExamineFhRequest)); + + struct ExamineFhResponse *res = (struct ExamineFhResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + int nlen = (unsigned char)(res->data[0]); + memcpy(fib->fib_FileName, res->data, nlen + 1); + fib->fib_FileName[nlen + 1] = 0; + + int clen = (unsigned char)(res->data[nlen + 1]); + memcpy(fib->fib_Comment, &(res->data[nlen + 1]), clen + 1); + fib->fib_Comment[clen + 1] = 0; + + fib->fib_DiskKey = res->disk_key; + fib->fib_DirEntryType = res->entry_type; + fib->fib_EntryType = res->entry_type; + fib->fib_Protection = res->protection; + fib->fib_Size = res->size; + fib->fib_NumBlocks = (res->size + 511) >> 9; + fib->fib_Date.ds_Days = res->date[0]; + fib->fib_Date.ds_Minute = res->date[1]; + fib->fib_Date.ds_Tick = res->date[2]; + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_findxxx(struct DosPacket *dp) +{ + struct FileHandle *fh = (struct FileHandle *)BADDR(dp->dp_Arg1); + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg2); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg3); + + if (dp->dp_Type == ACTION_FINDUPDATE) + dbg("ACTION_FINDUPDATE\n"); + else if (dp->dp_Type == ACTION_FINDINPUT) + dbg("ACTION_FINDINPUT\n"); + else if (dp->dp_Type == ACTION_FINDOUTPUT) + dbg("ACTION_FINDOUTPUT\n"); + + dbg(" file handle = $l\n", fh); + dbg(" lock = $l\n", lock); + dbg(" name = $S\n", name); + + struct FindXxxRequest *req = (struct FindXxxRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + + int nlen = *name; + memcpy(req->name, name, nlen + 1); + + write_req_and_wait_for_res(sizeof(struct FindXxxRequest) + nlen); + + struct FindXxxResponse *res = (struct FindXxxResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + fh->fh_Arg1 = res->arg1; + fh->fh_Type = mp; + fh->fh_Port = DOSFALSE; // Not an interactive file. + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_read(struct DosPacket *dp) +{ + ULONG arg1 = dp->dp_Arg1; + UBYTE *dst = (UBYTE *)dp->dp_Arg2; + int length = dp->dp_Arg3; + + dbg("ACTION_READ\n"); + dbg(" arg1 = $l\n", arg1); + dbg(" length = $l\n", length); + + if (length == 0) + { + dp->dp_Res1 = -1; + dp->dp_Res2 = ERROR_INVALID_LOCK; // This is not the correct error. + reply_packet(dp); + return; + } + + int total_read = 0; + while (length) + { + int to_read = length; + if (to_read > BUFFER_SIZE) + to_read = BUFFER_SIZE; + + struct ReadRequest *req = (struct ReadRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->arg1 = arg1; + req->address = TranslateAddressA314(data_buffer); + req->length = to_read; + + write_req_and_wait_for_res(sizeof(struct ReadRequest)); + + struct ReadResponse *res = (struct ReadResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = -1; + dp->dp_Res2 = res->error_code; + reply_packet(dp); + return; + } + + if (res->actual) + { + memcpy(dst, data_buffer, res->actual); + dst += res->actual; + total_read += res->actual; + length -= res->actual; + } + + if (res->actual < to_read) + break; + } + + dp->dp_Res1 = total_read; + dp->dp_Res2 = 0; + reply_packet(dp); +} + +void action_write(struct DosPacket *dp) +{ + ULONG arg1 = dp->dp_Arg1; + UBYTE *src = (UBYTE *)dp->dp_Arg2; + int length = dp->dp_Arg3; + + dbg("ACTION_WRITE\n"); + dbg(" arg1 = $l\n", arg1); + dbg(" length = $l\n", length); + + int total_written = 0; + while (length) + { + int to_write = length; + if (to_write > BUFFER_SIZE) + to_write = BUFFER_SIZE; + + memcpy(data_buffer, src, to_write); + + struct WriteRequest *req = (struct WriteRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->arg1 = arg1; + req->address = TranslateAddressA314(data_buffer); + req->length = to_write; + + write_req_and_wait_for_res(sizeof(struct WriteRequest)); + + struct WriteResponse *res = (struct WriteResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = total_written; + dp->dp_Res2 = res->error_code; + reply_packet(dp); + return; + } + + if (res->actual) + { + src += res->actual; + total_written += res->actual; + length -= res->actual; + } + + if (res->actual < to_write) + break; + } + + dp->dp_Res1 = total_written; + dp->dp_Res2 = 0; + reply_packet(dp); +} + +void action_seek(struct DosPacket *dp) +{ + ULONG arg1 = dp->dp_Arg1; + LONG new_pos = dp->dp_Arg2; + LONG mode = dp->dp_Arg3; + + dbg("ACTION_SEEK\n"); + dbg(" arg1 = $l\n", arg1); + dbg(" new_pos = $l\n", new_pos); + dbg(" mode = $l\n", mode); + + struct SeekRequest *req = (struct SeekRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->arg1 = arg1; + req->new_pos = new_pos; + req->mode = mode; + + write_req_and_wait_for_res(sizeof(struct SeekRequest)); + + struct SeekResponse *res = (struct SeekResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = -1; + dp->dp_Res2 = res->error_code; + } + else + { + dp->dp_Res1 = res->old_pos; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_end(struct DosPacket *dp) +{ + ULONG arg1 = dp->dp_Arg1; + + dbg("ACTION_END\n"); + dbg(" arg1 = $l\n", arg1); + + struct EndRequest *req = (struct EndRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->arg1 = arg1; + + write_req_and_wait_for_res(sizeof(struct EndRequest)); + + //struct EndResponse *res = (struct EndResponse *)request_buffer; + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + reply_packet(dp); +} + +void action_delete_object(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2); + + dbg("ACTION_DELETE_OBJECT\n"); + dbg(" lock = $l\n", lock); + dbg(" name = $S\n", name); + + struct DeleteObjectRequest *req = (struct DeleteObjectRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + + int nlen = *name; + memcpy(req->name, name, nlen + 1); + + write_req_and_wait_for_res(sizeof(struct DeleteObjectRequest) + nlen); + + struct DeleteObjectResponse *res = (struct DeleteObjectResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_rename_object(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2); + struct FileLock *target_dir = (struct FileLock *)BADDR(dp->dp_Arg3); + unsigned char *new_name = (unsigned char *)BADDR(dp->dp_Arg4); + + dbg("ACTION_RENAME_OBJECT\n"); + dbg(" lock = $l\n", lock); + dbg(" name = $S\n", name); + dbg(" target directory = $l\n", lock); + dbg(" new name = $S\n", new_name); + + struct RenameObjectRequest *req = (struct RenameObjectRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + req->target_dir = target_dir == NULL ? 0 : target_dir->fl_Key; + + int nlen = *name; + int nnlen = *new_name; + + req->name_len = nlen; + req->new_name_len = nnlen; + + unsigned char *p = &(req->new_name_len) + 1; + memcpy(p, name + 1, nlen); + p += nlen; + memcpy(p, new_name + 1, nnlen); + + write_req_and_wait_for_res(sizeof(struct RenameObjectRequest) + nlen + nnlen); + + struct RenameObjectResponse *res = (struct RenameObjectResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_create_dir(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg2); + + dbg("ACTION_CREATE_DIR\n"); + dbg(" lock = $l\n", lock); + dbg(" name = $S\n", name); + + struct CreateDirRequest *req = (struct CreateDirRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + + int nlen = *name; + memcpy(req->name, name, nlen + 1); + + write_req_and_wait_for_res(sizeof(struct CreateDirRequest) + nlen); + + struct CreateDirResponse *res = (struct CreateDirResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + struct FileLock *lock = create_and_add_file_lock(res->key, SHARED_LOCK); + + dbg(" Returning lock $l\n", lock); + dp->dp_Res1 = MKBADDRU(lock); + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_set_protect(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg2); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg3); + long mask = dp->dp_Arg4; + + dbg("ACTION_SET_PROTECT\n"); + dbg(" lock = $l\n", lock); + dbg(" name = $S\n", name); + dbg(" mask = $l\n", mask); + + struct SetProtectRequest *req = (struct SetProtectRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + req->mask = mask; + + int nlen = *name; + memcpy(req->name, name, nlen + 1); + + write_req_and_wait_for_res(sizeof(struct SetProtectRequest) + nlen); + + struct SetProtectResponse *res = (struct SetProtectResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_set_comment(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg2); + unsigned char *name = (unsigned char *)BADDR(dp->dp_Arg3); + unsigned char *comment = (unsigned char *)BADDR(dp->dp_Arg4); + + dbg("ACTION_SET_COMMENT\n"); + dbg(" lock = $l\n", lock); + dbg(" name = $S\n", name); + dbg(" comment = $S\n", comment); + + struct SetCommentRequest *req = (struct SetCommentRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key = lock == NULL ? 0 : lock->fl_Key; + + int nlen = *name; + int clen = *comment; + + req->name_len = nlen; + req->comment_len = clen; + + unsigned char *p = &(req->comment_len) + 1; + memcpy(p, name + 1, nlen); + p += nlen; + memcpy(p, comment + 1, clen); + + write_req_and_wait_for_res(sizeof(struct SetCommentRequest) + nlen + clen); + + struct SetCommentResponse *res = (struct SetCommentResponse *)request_buffer; + if (!res->success) + { + dbg(" Failed, error code $l\n", (LONG)res->error_code); + dp->dp_Res1 = DOSFALSE; + dp->dp_Res2 = res->error_code; + } + else + { + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + } + + reply_packet(dp); +} + +void action_same_lock(struct DosPacket *dp) +{ + struct FileLock *lock1 = (struct FileLock *)BADDR(dp->dp_Arg1); + struct FileLock *lock2 = (struct FileLock *)BADDR(dp->dp_Arg2); + + dbg("ACTION_SAME_LOCK\n"); + dbg(" locks to compare = $l $l\n", lock1, lock2); + + struct SameLockRequest *req = (struct SameLockRequest *)request_buffer; + req->has_response = 0; + req->type = dp->dp_Type; + req->key1 = lock1->fl_Key; + req->key2 = lock2->fl_Key; + + write_req_and_wait_for_res(sizeof(struct SameLockRequest)); + + struct SameLockResponse *res = (struct SameLockResponse *)request_buffer; + dp->dp_Res1 = res->success ? DOSTRUE : DOSFALSE; + dp->dp_Res2 = res->error_code; + + reply_packet(dp); +} + +void fill_info_data(struct InfoData *id) +{ + memset(id, 0, sizeof(struct InfoData)); + id->id_DiskState = ID_VALIDATED; + id->id_NumBlocks = 512 * 1024; + id->id_NumBlocksUsed = 10; + id->id_BytesPerBlock = 512; + id->id_DiskType = my_volume->dl_DiskType; + id->id_VolumeNode = MKBADDRU(my_volume); + id->id_InUse = DOSTRUE; +} + +void action_info(struct DosPacket *dp) +{ + struct FileLock *lock = (struct FileLock *)BADDR(dp->dp_Arg1); + struct InfoData *id = (struct InfoData *)BADDR(dp->dp_Arg2); + + dbg("ACTION_INFO\n"); + dbg(" lock = $l\n", lock); + + fill_info_data(id); + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + reply_packet(dp); +} + +void action_disk_info(struct DosPacket *dp) +{ + struct InfoData *id = (struct InfoData *)BADDR(dp->dp_Arg1); + + dbg("ACTION_DISK_INFO\n"); + + fill_info_data(id); + + dp->dp_Res1 = DOSTRUE; + dp->dp_Res2 = 0; + reply_packet(dp); +} + +void action_unsupported(struct DosPacket *dp) +{ + dbg("ACTION_UNSUPPORTED\n"); + dbg(" Unsupported action: $l\n", (ULONG)dp->dp_Type); + + struct UnsupportedRequest *req = (struct UnsupportedRequest *)request_buffer; + req->has_response = 0; + req->type = ACTION_UNSUPPORTED; + req->dp_Type = dp->dp_Type; + + write_req_and_wait_for_res(sizeof(struct UnsupportedRequest)); + + struct UnsupportedResponse *res = (struct UnsupportedResponse *)request_buffer; + dp->dp_Res1 = res->success ? DOSTRUE : DOSFALSE; + dp->dp_Res2 = res->error_code; + reply_packet(dp); +} + +void start(__reg("a0") struct DosPacket *startup_packet) +{ + SysBase = *(struct ExecBase **)4; + DOSBase = (struct DosLibrary *)OpenLibrary(DOSNAME, 0); + + mp = MyCreatePort(NULL, 0); + + startup_fs_handler(startup_packet); + + while (1) + { + WaitPort(mp); + struct StandardPacket *sp = (struct StandardPacket *)GetMsg(mp); + struct DosPacket *dp = (struct DosPacket *)(sp->sp_Msg.mn_Node.ln_Name); + + switch (dp->dp_Type) + { + case ACTION_LOCATE_OBJECT: action_locate_object(dp); break; + case ACTION_FREE_LOCK: action_free_lock(dp); break; + case ACTION_COPY_DIR: action_copy_dir(dp); break; + case ACTION_PARENT: action_parent(dp); break; + case ACTION_EXAMINE_OBJECT: action_examine_object(dp); break; + case ACTION_EXAMINE_NEXT: action_examine_next(dp); break; + case ACTION_EXAMINE_FH: action_examine_fh(dp); break; + + case ACTION_FINDUPDATE: action_findxxx(dp); break; + case ACTION_FINDINPUT: action_findxxx(dp); break; + case ACTION_FINDOUTPUT: action_findxxx(dp); break; + case ACTION_READ: action_read(dp); break; + case ACTION_WRITE: action_write(dp); break; + case ACTION_SEEK: action_seek(dp); break; + case ACTION_END: action_end(dp); break; + //case ACTION_TRUNCATE: action_truncate(dp); break; + + case ACTION_DELETE_OBJECT: action_delete_object(dp); break; + case ACTION_RENAME_OBJECT: action_rename_object(dp); break; + case ACTION_CREATE_DIR: action_create_dir(dp); break; + + case ACTION_SET_PROTECT: action_set_protect(dp); break; + case ACTION_SET_COMMENT: action_set_comment(dp); break; + //case ACTION_SET_DATE: action_set_date(dp); break; + + case ACTION_SAME_LOCK: action_same_lock(dp); break; + case ACTION_DISK_INFO: action_disk_info(dp); break; + case ACTION_INFO: action_info(dp); break; + + /* + case ACTION_CURRENT_VOLUME: action_current_volume(dp); break; + case ACTION_RENAME_DISK: action_rename_disk(dp); break; + + case ACTION_DIE: //action_die(dp); break; + case ACTION_MORE_CACHE: //action_more_cache(dp); break; + case ACTION_FLUSH: //action_flush(dp); break; + case ACTION_INHIBIT: //action_inhibit(dp); break; + case ACTION_WRITE_PROTECT: //action_write_protect(dp); break; + */ + + default: action_unsupported(dp); break; + } + } + + dbg("Shutting down\n"); + + // More cleaning up is necessary if the handler is to exit. + CloseLibrary((struct Library *)DOSBase); +} diff --git a/a314/software-amiga/a314fs_pistorm/bcpl_end.asm b/a314/software-amiga/a314fs_pistorm/bcpl_end.asm new file mode 100644 index 0000000..5459133 --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/bcpl_end.asm @@ -0,0 +1,5 @@ + cnop 0,4 + dc.l 0 + dc.l 1 + dc.l 4 + dc.l 1 diff --git a/a314/software-amiga/a314fs_pistorm/bcpl_start.asm b/a314/software-amiga/a314fs_pistorm/bcpl_start.asm new file mode 100644 index 0000000..748c350 --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/bcpl_start.asm @@ -0,0 +1,8 @@ + dc.l 0 + + movem.l a0-a6,-(a7) + lsl.l #2,d1 + move.l d1,a0 + bsr _start + movem.l (a7)+,a0-a6 + jmp (a6) diff --git a/a314/software-amiga/a314fs_pistorm/build.bat b/a314/software-amiga/a314fs_pistorm/build.bat new file mode 100644 index 0000000..505cf2b --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/build.bat @@ -0,0 +1,5 @@ +vc a314fs.c -S -o a314fs.asm +python comment_out_sections.py +vc bcpl_start.asm a314fs.asm bcpl_end.asm -nostdlib -o ../a314fs +python patch_a314fs.py +del a314fs.asm diff --git a/a314/software-amiga/a314fs_pistorm/comment_out_sections.py b/a314/software-amiga/a314fs_pistorm/comment_out_sections.py new file mode 100644 index 0000000..b769e11 --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/comment_out_sections.py @@ -0,0 +1,5 @@ +with open('a314fs.asm', 'r+b') as f: + text = f.read().decode('utf-8') + text = text.replace('section', ';section') + f.seek(0, 0) + f.write(text.encode('utf-8')) diff --git a/a314/software-amiga/a314fs_pistorm/messages.h b/a314/software-amiga/a314fs_pistorm/messages.h new file mode 100644 index 0000000..da0050c --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/messages.h @@ -0,0 +1,318 @@ +#pragma pack(push, 1) + +struct LocateObjectRequest +{ + short has_response; + short type; + long key; + short mode; + char name[1]; +}; + +struct LocateObjectResponse +{ + short has_response; + short success; + short error_code; + long key; +}; + +struct FreeLockRequest +{ + short has_response; + short type; + long key; +}; + +struct FreeLockResponse +{ + short has_response; + short success; + short error_code; +}; + +struct CopyDirRequest +{ + short has_response; + short type; + long key; +}; + +struct CopyDirResponse +{ + short has_response; + short success; + short error_code; + long key; +}; + +struct ParentRequest +{ + short has_response; + short type; + long key; +}; + +struct ParentResponse +{ + short has_response; + short success; + short error_code; + long key; +}; + +struct ExamineObjectRequest +{ + short has_response; + short type; + long key; +}; + +struct ExamineObjectResponse +{ + short has_response; + short success; + short error_code; + + short disk_key; + short entry_type; + int size; + int protection; + int date[3]; + char data[1]; +}; + +struct ExamineNextRequest +{ + short has_response; + short type; + long key; + short disk_key; +}; + +struct ExamineNextResponse +{ + short has_response; + short success; + short error_code; + + short disk_key; + short entry_type; + int size; + int protection; + int date[3]; + char data[1]; +}; + +struct FindXxxRequest +{ + short has_response; + short type; + long key; + char name[1]; +}; + +struct FindXxxResponse +{ + short has_response; + short success; + short error_code; + long arg1; +}; + +struct ReadRequest +{ + short has_response; + short type; + long arg1; + int address; + int length; +}; + +struct ReadResponse +{ + short has_response; + short success; + short error_code; + int actual; +}; + +struct WriteRequest +{ + short has_response; + short type; + long arg1; + int address; + int length; +}; + +struct WriteResponse +{ + short has_response; + short success; + short error_code; + int actual; +}; + +struct SeekRequest +{ + short has_response; + short type; + long arg1; + int new_pos; + int mode; +}; + +struct SeekResponse +{ + short has_response; + short success; + short error_code; + int old_pos; +}; + +struct EndRequest +{ + short has_response; + short type; + long arg1; +}; + +struct EndResponse +{ + short has_response; + short success; + short error_code; +}; + +struct DeleteObjectRequest +{ + short has_response; + short type; + long key; + char name[1]; +}; + +struct DeleteObjectResponse +{ + short has_response; + short success; + short error_code; +}; + +struct RenameObjectRequest +{ + short has_response; + short type; + long key; + long target_dir; + unsigned char name_len; + unsigned char new_name_len; +}; + +struct RenameObjectResponse +{ + short has_response; + short success; + short error_code; +}; + +struct CreateDirRequest +{ + short has_response; + short type; + long key; + char name[1]; +}; + +struct CreateDirResponse +{ + short has_response; + short success; + short error_code; + long key; +}; + +struct SetProtectRequest +{ + short has_response; + short type; + long key; + long mask; + char name[1]; +}; + +struct SetProtectResponse +{ + short has_response; + short success; + short error_code; +}; + +struct SetCommentRequest +{ + short has_response; + short type; + long key; + unsigned char name_len; + unsigned char comment_len; +}; + +struct SetCommentResponse +{ + short has_response; + short success; + short error_code; +}; + +struct SameLockRequest +{ + short has_response; + short type; + long key1; + long key2; +}; + +struct SameLockResponse +{ + short has_response; + short success; + short error_code; +}; + +struct ExamineFhRequest +{ + short has_response; + short type; + long arg1; +}; + +struct ExamineFhResponse +{ + short has_response; + short success; + short error_code; + + short disk_key; + short entry_type; + int size; + int protection; + int date[3]; + char data[1]; +}; + +struct UnsupportedRequest +{ + short has_response; + short type; + short dp_Type; +}; + +struct UnsupportedResponse +{ + short has_response; + short success; + short error_code; +}; + +#pragma pack(pop) diff --git a/a314/software-amiga/a314fs_pistorm/patch_a314fs.py b/a314/software-amiga/a314fs_pistorm/patch_a314fs.py new file mode 100644 index 0000000..08d9983 --- /dev/null +++ b/a314/software-amiga/a314fs_pistorm/patch_a314fs.py @@ -0,0 +1,5 @@ +with open('a314fs', 'r+b') as f: + f.seek(0x1c) + b = f.read(4) + f.seek(0x20) + f.write(b)