Files
86Box/src/include/86box/fdd_pcjs.h
2024-05-20 11:07:55 -04:00

256 lines
8.3 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the pcjs v2 floppy image format (read-only)
*
* Authors: cold-brewed
*
* Copyright 2024 cold-brewed
*
* More info: https://www.pcjs.org/tools/diskimage/
* pcjs disk module v2: https://github.com/jeffpar/pcjs/blob/master/machines/pcx86/modules/v2/disk.js
*/
#ifndef EMU_FLOPPY_PCJS_H
#define EMU_FLOPPY_PCJS_H
/* Currently targeting v2 of the spec */
#define PCJS_DISK_SPEC_VERSION 2
#define PCJS_MAX_TRACKS 256
#define PCJS_MAX_SIDES 2
#define PCJS_MAX_SECTORS 256
/* The json keys as defined in each sector array item */
#define PCJS_OBJECT_KEY_CYLINDER "c"
#define PCJS_OBJECT_KEY_TRACK PCJS_OBJECT_KEY_CYLINDER
#define PCJS_OBJECT_KEY_HEAD "h"
#define PCJS_OBJECT_KEY_SECTOR "s"
#define PCJS_OBJECT_KEY_LENGTH "l"
#define PCJS_OBJECT_KEY_DATA "d"
#define PCJS_OBJECT_KEY_FILE "f"
#define PCJS_OBJECT_KEY_OFFSET "d"
/* The json keys as defined in the fileTable object */
#define PCJS_OBJECT_KEY_FT_HASH "hash"
#define PCJS_OBJECT_KEY_FT_PATH "path"
#define PCJS_OBJECT_KEY_FT_ATTR "attr"
#define PCJS_OBJECT_KEY_FT_DATE "date"
#define PCJS_OBJECT_KEY_FT_SIZE "size"
/* String length defaults */
#define PCJS_IMAGE_INFO_STRING_LEN 128
#define PCJS_IMAGE_INFO_ARRAY_LEN 128
#define PCJS_FILE_TABLE_STRING_LEN 128
/* Defaults for optional json values */
#define JSON_OPTIONAL_NUMBER_DEFAULT 0
#define JSON_OPTIONAL_STRING_DEFAULT ""
/* Structure for each sector */
typedef struct pcjs_sector_t {
/* Track number */
uint8_t track;
/* Side number */
uint8_t side;
/* Sector number */
uint8_t sector;
/* Size of the sector */
uint16_t size;
/* Encoded size of the sector */
uint16_t encoded_size;
/* Pointer the the allocated data for the sector */
uint8_t *data;
/* Number of times to repeat the pattern until end of sector */
uint16_t pattern_repeat;
/* Last pattern entry to repeat */
int32_t last_entry;
/* Maps back to a file entry. -1 if not set */
int32_t file;
/* The offset in the mapped file entry. -1 if not set */
int32_t offset;
} pcjs_sector_t;
/* Cases are mixed here (some camelCase) to match the pcjs values */
typedef struct pcjs_image_info_t {
char type[PCJS_IMAGE_INFO_STRING_LEN];
char name[PCJS_IMAGE_INFO_STRING_LEN];
char format[PCJS_IMAGE_INFO_STRING_LEN];
char hash[PCJS_IMAGE_INFO_STRING_LEN];
uint32_t checksum;
uint8_t cylinders;
uint8_t heads;
uint8_t trackDefault;
uint16_t sectorDefault;
uint32_t diskSize;
uint8_t boot_sector[PCJS_IMAGE_INFO_ARRAY_LEN];
uint8_t boot_sector_array_size;
char version[PCJS_IMAGE_INFO_STRING_LEN];
char repository[PCJS_IMAGE_INFO_STRING_LEN];
} pcjs_image_info_t;
typedef struct pcjs_file_table_entry_t {
char hash[PCJS_FILE_TABLE_STRING_LEN];
char path[PCJS_FILE_TABLE_STRING_LEN];
char attr[PCJS_FILE_TABLE_STRING_LEN];
char date[PCJS_FILE_TABLE_STRING_LEN];
uint32_t size;
} pcjs_file_table_entry_t ;
typedef struct pcjs_file_table_t {
pcjs_file_table_entry_t *entries;
uint16_t num_entries;
} pcjs_file_table_t;
typedef struct pcjs_t {
/* FILE pointer for the json file */
FILE *fp;
/* These values are read in from the metadata */
/* Total number of tracks */
uint8_t total_tracks;
/* Total number of sides */
uint8_t total_sides;
/* Total number of sectors per track */
uint16_t total_sectors;
/* These values are calculated for validation */
/* Calculated number of tracks */
uint8_t calc_total_tracks;
/* Calculated number of sides */
uint8_t calc_total_sides;
/* Calculated number of sectors per track */
uint16_t calc_total_sectors;
/* Number of sectors per track */
uint8_t spt[PCJS_MAX_TRACKS][PCJS_MAX_SIDES];
/* Current track */
uint8_t current_track;
/* Current side */
uint8_t current_side;
/* Current sector */
uint8_t current_sector[PCJS_MAX_SIDES];
/* Disk is in dmf format? */
uint8_t dmf;
uint8_t interleave;
uint8_t gap2_len;
uint8_t gap3_len;
int track_width;
/* Flags for the entire disk */
uint16_t disk_flags;
/* Flags for the current track */
uint16_t track_flags;
uint8_t interleave_ordered[PCJS_MAX_TRACKS][PCJS_MAX_SIDES];
/* The main mapping of all the sectors back to each individual pcjs_sector_t item. */
pcjs_sector_t sectors[PCJS_MAX_TRACKS][PCJS_MAX_SIDES][PCJS_MAX_SECTORS];
/* Disk metadata information contained in each image */
pcjs_image_info_t image_info;
/* Optional file table mapping for each sector */
pcjs_file_table_t file_table;
} pcjs_t;
/* Errors */
enum pcjs_img_error {
E_SUCCESS = 0,
E_MISSING_KEY = 1,
E_UNEXPECTED_VALUE = 2,
E_INTEGRITY,
E_INVALID_OBJECT,
E_ALLOC,
E_PARSE,
};
typedef enum pcjs_img_error pcjs_error_t;
/* Macros */
/* Macro for getting image info metadata: strings */
#define IMAGE_INFO_GET_STRING(type) \
const cJSON * type##_json = cJSON_GetObjectItemCaseSensitive(imageInfo, #type); \
if (cJSON_IsString( type##_json) && type##_json->valuestring != NULL) { \
strncpy(dev->image_info.type, type##_json->valuestring, sizeof(dev->image_info. type) - 1); \
} else { \
pcjs_log("Required string value for \"%s\" missing from imageInfo\n", #type); \
pcjs_error = E_INVALID_OBJECT; \
return 1; \
}
/* Macro for getting image info metadata: ints */
#define IMAGE_INFO_GET_NUMBER(type) \
const cJSON * type##_json = cJSON_GetObjectItemCaseSensitive(imageInfo, #type); \
if (cJSON_IsNumber( type##_json)) { \
dev->image_info.type = type##_json->valueint; \
} else { \
pcjs_log("Required number value for \"%s\" missing from imageInfo\n", #type); \
pcjs_error = E_INVALID_OBJECT; \
return 1; \
}
/* Macro for getting required object value: number */
#define JSON_GET_OBJECT_NUMBER_REQUIRED(var, json, key) \
const cJSON *var##_json = cJSON_GetObjectItemCaseSensitive(json, key); \
if (!cJSON_IsNumber(var##_json)) { \
pcjs_log("Required number value for \"%s\" missing or invalid\n", key); \
pcjs_error = E_INVALID_OBJECT; \
goto fail; \
} else { \
var = var##_json->valueint; \
}
/* Macro for getting optional object value: number
* Default value will be used if the number does not exist */
#define JSON_GET_OBJECT_NUMBER_OPTIONAL(var, json, key) \
const cJSON *var##_json = cJSON_GetObjectItemCaseSensitive(json, key); \
if (!cJSON_IsNumber(var##_json)) { \
var = JSON_OPTIONAL_NUMBER_DEFAULT; \
} else { \
var = var##_json->valueint; \
}
/* Macro for getting optional object value: number
* Provided default value will be used if the number does not exist */
#define JSON_GET_OBJECT_NUMBER_OPTIONAL_DEFAULT(var, json, key, default) \
const cJSON *var##_json = cJSON_GetObjectItemCaseSensitive(json, key); \
if (!cJSON_IsNumber(var##_json)) { \
var = default; \
} else { \
var = var##_json->valueint; \
}
/* Macro for getting optional object value: string
* Default value will be used if the string does not exist */
#define JSON_GET_OBJECT_STRING_OPTIONAL(var, json, key) \
const cJSON * var##_json = cJSON_GetObjectItemCaseSensitive(json, key); \
if (cJSON_IsString( var##_json) && var##_json->valuestring != NULL) { \
strncpy(var, var##_json->valuestring, sizeof(var) - 1); \
} else { \
strncpy(var, JSON_OPTIONAL_STRING_DEFAULT, sizeof(var) - 1); \
}
/* Macro for getting required object value: string */
#define JSON_GET_OBJECT_STRING_REQUIRED(var, json, key) \
const cJSON * var##_json = cJSON_GetObjectItemCaseSensitive(json, key); \
if (cJSON_IsString( var##_json) && var##_json->valuestring != NULL) { \
strncpy(var, var##_json->valuestring, sizeof(var) - 1); \
} else { \
pcjs_error = E_INVALID_OBJECT; \
goto fail; \
}
extern void pcjs_init(void);
extern void pcjs_load(int drive, char *fn);
extern void pcjs_close(int drive);
extern const char* pcjs_errmsg(void);
#endif