-
Notifications
You must be signed in to change notification settings - Fork 12
AppleSingle format support & bugfixes #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
2324f0b
start laying out structs & some initial api
mach-kernel 3514855
wip entry types / found place to hook for file reads
mach-kernel 28b6fb8
make typedefs safer instead of using int and long, LOL ALSO GOT ENDIAND
mach-kernel f699a2c
clean up a little bit, add almost ready
mach-kernel f9b34ec
add files, needs testing
mach-kernel 0e2c79d
fix bug / dont overwrite if applesingle header detected
mach-kernel a44b7dc
fix path tokenization issue
mach-kernel 2187f8a
portable endian test + os specific bswaps + fix replacefile bug on wi…
mach-kernel fd687ce
fix buffer overflow + clean up header
mach-kernel 7a24451
first draft output / wip
mach-kernel 633394a
wip
mach-kernel 9321b77
wip / some bugfixes
mach-kernel d2e341c
almost working output
mach-kernel 6b32fe5
needs more testing, but supposedly working!!!!!
mach-kernel 436bffe
update readme
mach-kernel 5c04eee
allow EXTRACTFOLDER and EXTRACTVOLUME to work
mach-kernel 094ce66
fix an extractvolume bug!
mach-kernel 7a3855e
don't generate fileinformation.txt if exporting as applesingle
mach-kernel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oliverschmidt 👍
There was a problem hiding this comment.
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 :-)