Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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!
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the positive feedback :-)


#### 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