Skip to content

Commit

Permalink
Merge pull request #13 from mach-kernel/applesingle
Browse files Browse the repository at this point in the history
AppleSingle format support & bugfixes
  • Loading branch information
mach-kernel authored Mar 23, 2018
2 parents e5e89a3 + 7a3855e commit edcf533
Show file tree
Hide file tree
Showing 13 changed files with 472 additions and 56 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ Any and all contributions are welcome. Included is also a `cadius.pro` file you

## Changelog

#### 1.4.0
- Adds AppleSingle file format support, with initial support for data and ProDOS file info IDs 1 & 11 ([#7](https://github.com/mach-kernel/cadius/issues/7)).
- Fix path bugs on Windows ([#9](https://github.com/mach-kernel/cadius/issues/9)).
- Fix buffer overflow from [#12](https://github.com/mach-kernel/cadius/issues/12).
- Fix segfault when using `EXTRACTVOLUME`.

A big thank you to [@oliverschmidt](https://github.com/oliverschmidt) for helping test this release!

#### 1.3.2
- Maintenance release / macro cleanup
- Maintenance release / macro cleanup.

#### 1.3.1
- Resolves timestamp bugs in [#10](https://github.com/mach-kernel/cadius/issues/10). Thanks, @a2-4am!
Expand All @@ -48,17 +56,17 @@ Any and all contributions are welcome. Included is also a `cadius.pro` file you
- `REPLACEFILE` command, `DELETEFILE` support for type/auxtype suffix.

#### 1.2-b3
- Fix Windows build issues, make some shared OS methods static / remove from `os.c` ([@mach-kernel](https://github.com/mach-kernel))
- Fix Windows build issues, make some shared OS methods static / remove from `os.c` ([@mach-kernel](https://github.com/mach-kernel)).

#### 1.2-b2
- Clean up OS macros, explicit `win32` and `posix` modules ([@mach-kernel](https://github.com/mach-kernel))
- Clean up OS macros, explicit `win32` and `posix` modules ([@mach-kernel](https://github.com/mach-kernel)).

#### 1.2-b1
- UTF-8 encode all source files
- Initial POSIX support ([@mach-kernel](https://github.com/mach-kernel))
- UTF-8 encode all source files.
- Initial POSIX support ([@mach-kernel](https://github.com/mach-kernel)).

#### 1.1
- Initial fork from BrutalDeluxe
- Initial fork from BrutalDeluxe.

## License

Expand Down
1 change: 0 additions & 1 deletion Src/Dc_Prodos.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ struct file_descriptive_entry
struct file_descriptive_entry *next;
};


struct prodos_file
{
int entry_type; /* Seedling, Sapling, Tree, Extended */
Expand Down
4 changes: 3 additions & 1 deletion Src/Dc_Shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
#include <ctype.h>

#include "Dc_Shared.h"
#include "os/os.h"
#include "Dc_Memory.h"
#include "File_AppleSingle.h"
#include "os/os.h"

#ifdef IS_WINDOWS
#include <malloc.h>
Expand Down Expand Up @@ -64,6 +65,7 @@ unsigned char *LoadBinaryFile(char *file_path, int *data_length_rtn)

/* Renvoi les données et la taille */
*data_length_rtn = nb_read;

return(data);
}

Expand Down
8 changes: 5 additions & 3 deletions Src/Dc_Shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

#pragma once

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
#include <stdint.h>

typedef unsigned char BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;

#define BUFFER_SIZE 2048

Expand Down
218 changes: 218 additions & 0 deletions Src/File_AppleSingle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#include "File_AppleSingle.h"

const unsigned int AS_MAGIC = (uint32_t) 0x00051600;

static uint32_t as_field32(uint32_t num)
{
return IS_LITTLE_ENDIAN ? swap32(num) : num;
}

static uint16_t as_field16(uint16_t num)
{
return IS_LITTLE_ENDIAN ? swap16(num) : num;
}

/**
* Is this an AppleSingle file?
* @brief ASIsAppleSingle
* @param buf
* @return
*/
bool ASIsAppleSingle(unsigned char *buf)
{
int buf_magic;
memcpy(&buf_magic, buf, sizeof(AS_MAGIC));
buf_magic = as_field32(buf_magic);

return buf_magic == AS_MAGIC;
}

/**
* Parse header out of raw data buffer.
* @brief ASParseHeader
* @param buf The buffer
* @return
*/
struct as_file_header *ASParseHeader(unsigned char *buf)
{
struct as_file_header *header = malloc(sizeof(as_file_header));
struct as_file_header *buf_header = (as_file_header *) buf;

header->magic = as_field32(buf_header->magic);
header->version = as_field32(buf_header->version);
header->num_entries = as_field16(buf_header->num_entries);

return header;
}

/**
* @brief ASParseProdosEntry
* @param entry_buf The entry buffer
* @return
*/
struct as_prodos_info *ASParseProdosEntry(unsigned char *entry_buf, DWORD length)
{
struct as_prodos_info *prodos_entry = malloc(sizeof(as_prodos_info));
struct as_prodos_info *buf_prodos_entry = (as_prodos_info *) entry_buf;

prodos_entry->access = as_field16(buf_prodos_entry->access);
prodos_entry->filetype = as_field16(buf_prodos_entry->filetype);
prodos_entry->auxtype = as_field32(buf_prodos_entry->auxtype);

return prodos_entry;
}

/**
* Read headers and return a list of as_file_entry pointers.
* @brief ASGetEntries
* @param buf
* @return
*/
struct as_file_entry *ASGetEntries(struct as_file_header *header, unsigned char *buf)
{
if (!header)
{
printf(" Invalid AppleSingle file!\n");
return NULL;
}

struct as_file_entry *entries = malloc(
header->num_entries * sizeof(as_file_entry)
);

struct as_file_entry *buf_entries = buf + sizeof(as_file_header);
memcpy(entries, buf_entries, header->num_entries * sizeof(as_file_entry));

if (IS_LITTLE_ENDIAN)
for (int i = 0; i < header->num_entries; ++i)
{
entries[i].entry_id = swap32(entries[i].entry_id);
entries[i].offset = swap32(entries[i].offset);
entries[i].length = swap32(entries[i].length);
}

return entries;
}

/**
* Grab data from data entry and place in prodos_file.
* @brief ASDecorateDataFork
* @param current_file The current file
* @param data The data
* @param data_fork_entry The data fork entry
*/
void ASDecorateDataFork(struct prodos_file *current_file, unsigned char *data, as_file_entry *data_fork_entry)
{
if (data_fork_entry->entry_id != data_fork) return;

unsigned char *data_entry = malloc(data_fork_entry->length);
memcpy(data_entry, data + data_fork_entry->offset, data_fork_entry->length);
current_file->data = data_entry;
current_file->data_length = data_fork_entry->length;

return;
}

/**
* Read ProDOS metadata struct and place in prodos_file.
*
* @brief ASDecorateProdosFileInfo
* @param current_file The current file
* @param data The data
* @param prodos_entry The prodos entry
*/
void ASDecorateProdosFileInfo(struct prodos_file *current_file, unsigned char *data, as_file_entry *prodos_entry)
{
if (prodos_entry->entry_id != prodos_file_info) return;

struct as_prodos_info *info_meta = ASParseProdosEntry(
data + prodos_entry->offset, prodos_entry->length
);

if (!info_meta) return;

current_file->access = info_meta->access;
current_file->type = info_meta->filetype;
current_file->aux_type = info_meta->auxtype;

return;
}

/**
* Parse AppleSingle header and write attributes into prodos_file
* struct
* @brief ASDecorateProdosFile
* @param current_file
* @param data
*/
void ASDecorateProdosFile(struct prodos_file *current_file, unsigned char *data)
{
struct as_file_header *header = ASParseHeader(data);
struct as_file_entry *entries = ASGetEntries(header, data);

for (int i = 0; i < header->num_entries; ++i)
switch(entries[i].entry_id)
{
case data_fork:
ASDecorateDataFork(current_file, data, &entries[i]);
break;
case prodos_file_info:
ASDecorateProdosFileInfo(current_file, data, &entries[i]);
break;
default:
printf(" Entry ID %d unsupported, ignoring!\n", entries[i].entry_id);
printf(" (See https://tools.ietf.org/html/rfc1740 for ID lookup)\n");
break;
}
return;
}

struct as_from_prodos ASFromProdosFile(struct prodos_file *file)
{
struct as_file_header *as_header = malloc(sizeof(as_file_header));
as_header->magic = as_field32(AS_MAGIC);
for (int i = 0; i < 4; ++i) as_header->filler[i] = 0;
as_header->version = as_field32(0x00020000);
as_header->num_entries = as_field16(2);

uint32_t header_offset = sizeof(as_file_header) + (2 * sizeof(as_file_entry));
struct as_file_entry *data_entry = malloc(sizeof(as_file_entry));
data_entry->entry_id = as_field32(data_fork);
data_entry->length = as_field32(file->data_length);
data_entry->offset = as_field32(header_offset);

uint32_t prodos_entry_offset = header_offset + file->data_length;
struct as_file_entry *prodos_entry = malloc(sizeof(as_file_entry));
prodos_entry->entry_id = as_field32(prodos_file_info);
prodos_entry->length = as_field32(sizeof(as_prodos_info));
prodos_entry->offset = as_field32(prodos_entry_offset);

struct as_prodos_info *prodos_info = malloc(sizeof(as_prodos_info));
prodos_info->access = as_field16(file->entry->access);
prodos_info->filetype = as_field16(file->entry->file_type);
prodos_info->auxtype = as_field32(file->entry->file_aux_type);

uint32_t payload_size = prodos_entry_offset + sizeof(as_prodos_info);
char *payload = malloc(payload_size);
char *seek = payload;

memcpy(seek, as_header, sizeof(as_file_header));
seek += sizeof(as_file_header);

memcpy(seek, data_entry, sizeof(as_file_entry));
seek += sizeof(as_file_entry);

memcpy(seek, prodos_entry, sizeof(as_file_entry));
seek += sizeof(as_file_entry);

memcpy(seek, file->data, file->data_length);
seek += file->data_length;

memcpy(seek, prodos_info, sizeof(as_prodos_info));

struct as_from_prodos as_file;
as_file.length = payload_size;
as_file.data = payload;

return as_file;
}
83 changes: 83 additions & 0 deletions Src/File_AppleSingle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* AppleSingle file format support
* RFC 1740: https://tools.ietf.org/html/rfc1740
*
* Author: David Stancu, @mach-kernel, Mar. 2018
*
*/

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>

#include "Dc_Shared.h"
#include "Dc_Prodos.h"

const unsigned int AS_MAGIC;
#define IS_LITTLE_ENDIAN 'APPL' == (uint32_t) 0x4150504C

#pragma pack(push, 1)

typedef struct as_file_header
{
DWORD magic;
DWORD version;
DWORD filler[4];
WORD num_entries;
} as_file_header;

typedef struct as_file_entry
{
DWORD entry_id;
DWORD offset;
DWORD length;
} as_file_entry;

typedef enum as_entry_types
{
data_fork = 1,
resource_fork = 2,
real_name = 3,
comment = 4,
icon_bw = 5,
icon_color = 6,
file_dates_info = 8,
finder_info = 9,
mac_file_info = 10,
prodos_file_info = 11,
msdos_file_info = 12,
short_name = 13,
afp_file_info = 14,
directory_id = 15
} as_entry_types;

typedef struct as_prodos_info
{
WORD access;
WORD filetype;
DWORD auxtype;
} as_prodos_info;

typedef struct as_from_prodos
{
uint16_t length;
char *data;
} as_from_prodos;

#pragma pack(pop)

bool ASIsAppleSingle(unsigned char *buf);

struct as_file_header *ASParseHeader(unsigned char *buf);
struct as_prodos_info *ASParseProdosEntry(unsigned char *entry_buf, DWORD length);
struct as_file_entry *ASGetEntries(struct as_file_header *header, unsigned char *buf);

void ASDecorateDataFork(struct prodos_file *current_file, unsigned char *data, as_file_entry *data_fork_entry);
void ASDeocrateProdosFileInfo(struct prodos_file *current_file, unsigned char *data, as_file_entry *prodos_entry);
void ASDecorateProdosFile(struct prodos_file *current_file, unsigned char *data);

struct as_from_prodos ASFromProdosFile(struct prodos_file *file);
Loading

0 comments on commit edcf533

Please sign in to comment.