mirror of https://github.com/zydeco/libres.git
initial commit
This commit is contained in:
commit
2562781a7e
|
@ -0,0 +1,16 @@
|
|||
LIB = libres.a
|
||||
|
||||
CC = clang
|
||||
AR = ar
|
||||
RANLIB = ranlib
|
||||
CFLAGS = -fPIC -std=c99
|
||||
|
||||
all: $(LIB)
|
||||
|
||||
$(LIB): res.c
|
||||
$(CC) -c $(CFLAGS) res.c
|
||||
$(AR) -ru $(LIB) res.o
|
||||
$(RANLIB) $(LIB)
|
||||
|
||||
clean:
|
||||
rm -rf $(LIB) res.o
|
|
@ -0,0 +1,16 @@
|
|||
libres - library for reading Macintosh resource forks
|
||||
Copyright (C) 2008-2009 Jesus A. Alvarez
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
@ -0,0 +1,126 @@
|
|||
// libres internal header
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "res.h"
|
||||
|
||||
#define kCompressedResourceTag 0xA89F6572
|
||||
#define kCompressedResourceFlg0 0x00120901
|
||||
#define kCompressedResourceFlg1 0x00120801
|
||||
#define kDCMPInvalidFlags 0xD5DC
|
||||
|
||||
#define efail(n) {errno = n; return NULL;}
|
||||
#define effail(n, m) {errno = n; free(m); return NULL;}
|
||||
#define eret(n, r) {errno = n; return r;}
|
||||
#define egoto(n, l) {errno = n; goto l;}
|
||||
|
||||
// in-memory structures
|
||||
struct RFILE {
|
||||
FILE *fp; // file
|
||||
void *buf; // memory
|
||||
void *fpriv; // functions
|
||||
res_seek_func seek; // functions
|
||||
res_read_func read; // functions
|
||||
size_t size;
|
||||
size_t dataOffset;
|
||||
uint16_t attributes;
|
||||
size_t numTypes;
|
||||
struct RmType *types;
|
||||
};
|
||||
|
||||
struct RmType {
|
||||
uint32_t type;
|
||||
size_t count;
|
||||
struct RmResRef *list;
|
||||
};
|
||||
|
||||
struct RmResRef {
|
||||
int16_t ID;
|
||||
RFlags flags;
|
||||
uint32_t size; // logical size (uncompressed)
|
||||
uint32_t psize; // physical size
|
||||
uint32_t offset; // offset from data section
|
||||
int16_t dcmp; // decompressor ID
|
||||
char* name;
|
||||
};
|
||||
|
||||
|
||||
// in-file structures
|
||||
|
||||
struct __attribute__ ((__packed__)) RfHdr {
|
||||
// resource fork header
|
||||
uint32_t dataOffset;
|
||||
uint32_t mapOffset;
|
||||
uint32_t dataLength;
|
||||
uint32_t mapLength;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) RfMap {
|
||||
// resource map
|
||||
struct RfHdr headerCopy;
|
||||
uint32_t rsvNextMapHandle;
|
||||
uint16_t rsvFileRef;
|
||||
uint16_t attributes;
|
||||
uint16_t typeListOffset; // from beginning of map to type list
|
||||
uint16_t nameListOffset; // from beginning of map to name list
|
||||
//uint16_t numTypes; // types minus one, overlaps RfTypeList
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) RfTypeEntry {
|
||||
// resource type entry
|
||||
uint32_t type; // resource type
|
||||
uint16_t count; // number of resources minus one
|
||||
uint16_t offset; // offset to ref list from type list
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) RfTypeList {
|
||||
// resource type list
|
||||
uint16_t count; // minus one
|
||||
struct RfTypeEntry entry[];
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) RfRefEntry {
|
||||
// resource reference entry
|
||||
int16_t ID;
|
||||
uint16_t nameOffset;
|
||||
uint8_t attributes;
|
||||
uint8_t offHi;
|
||||
uint16_t offLo;
|
||||
uint32_t rsvHandle;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) RfResEntry {
|
||||
// resource data entry
|
||||
uint32_t length;
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) RfCmpHdr {
|
||||
// compressed resource entry
|
||||
uint32_t tag;
|
||||
uint32_t flags;
|
||||
uint32_t size; // uncompressed
|
||||
union __attribute__ ((__packed__)) {
|
||||
struct __attribute__ ((__packed__)) {
|
||||
int16_t dcmp; // decompressor ID
|
||||
uint8_t wrkBufFrSz; // working buffer fractional size
|
||||
uint8_t expBufSz; // expansion buffer size
|
||||
} v0;
|
||||
struct __attribute__ ((__packed__)) {
|
||||
uint8_t wrkBufFrSz; // working buffer fractional size
|
||||
uint8_t expBufSz; // expansion buffer size
|
||||
int16_t dcmp; // decompressor ID
|
||||
} v1;
|
||||
} u;
|
||||
};
|
||||
|
||||
// private functions
|
||||
void* res_bread (RFILE *rp, void *buf, size_t offset, size_t count);
|
||||
uint32_t res_szread (RFILE *rp, size_t offset);
|
||||
RFILE* res_load (RFILE *rp);
|
||||
int res_ref_compar (const struct RmResRef *, const struct RmResRef *);
|
||||
int res_type_compar (const struct RmType *, const struct RmType *);
|
||||
struct RmType * res_type_find (RFILE *rp, uint32_t type);
|
||||
struct RmResRef * res_ref_find (RFILE *rp, struct RmType *type, int16_t ID);
|
||||
struct RmResRef * res_ref_find_named (RFILE *rp, struct RmType *type, const char *name);
|
||||
int res_ref_name_compar (const struct RmResRef * a, const struct RmResRef * b);
|
||||
void* res_read_raw (RFILE *rp, struct RmResRef *ref, void *buf, size_t start, size_t size, size_t *read, size_t *remain);
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* libres - library for reading Macintosh resource forks
|
||||
* Copyright (C) 2008-2009 Jesus A. Alvarez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
// http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <search.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "res.h"
|
||||
#include "libres_internal.h"
|
||||
|
||||
const char * libres_id = "libres 1.0.0 (C)2008-2009 namedfork.net";
|
||||
|
||||
RFILE* res_open (const char *path, int mode) {
|
||||
if (mode != 0) efail(EINVAL);
|
||||
RFILE* rp = malloc(sizeof(RFILE));
|
||||
if (rp == NULL) efail(ENOMEM);
|
||||
bzero(rp, sizeof(RFILE));
|
||||
|
||||
// open
|
||||
rp->fp = fopen(path, "r");
|
||||
if (rp->fp == NULL) {
|
||||
free(rp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get size
|
||||
errno = 0;
|
||||
fseek(rp->fp, 0, SEEK_END);
|
||||
rp->size = ftell(rp->fp);
|
||||
if (errno) effail(errno, rp);
|
||||
|
||||
rp->buf = NULL;
|
||||
return res_load(rp);
|
||||
}
|
||||
|
||||
RFILE* res_open_mem (void *buf, size_t size, int copy) {
|
||||
RFILE* rp = malloc(sizeof(RFILE));
|
||||
if (rp == NULL) efail(ENOMEM);
|
||||
bzero(rp, sizeof(RFILE));
|
||||
rp->size = size;
|
||||
if (copy) {
|
||||
rp->buf = malloc(size);
|
||||
if (rp == NULL) effail(ENOMEM, rp);
|
||||
memcpy(rp->buf, buf, size);
|
||||
} else rp->buf = buf;
|
||||
|
||||
return res_load(rp);
|
||||
}
|
||||
|
||||
RFILE* res_open_funcs (void *priv, res_seek_func seekf, res_read_func readf) {
|
||||
RFILE* rp = malloc(sizeof(RFILE));
|
||||
if (rp == NULL) efail(ENOMEM);
|
||||
bzero(rp, sizeof(RFILE));
|
||||
rp->seek = seekf;
|
||||
rp->read = readf;
|
||||
rp->fpriv = priv;
|
||||
rp->size = rp->seek(priv, 0, SEEK_END);
|
||||
return res_load(rp);
|
||||
}
|
||||
|
||||
int res_close (RFILE* rp) {
|
||||
if (rp == NULL) eret(EBADF, EOF);
|
||||
if (rp->buf) free(rp->buf);
|
||||
if (rp->fp) fclose(rp->fp);
|
||||
|
||||
// free list
|
||||
if (rp->types) {
|
||||
for(int i=0; i < rp->numTypes; i++) {
|
||||
struct RmType *t = &rp->types[i];
|
||||
if (t->list == NULL) continue;
|
||||
for(int j=0; j < t->count; j++) {
|
||||
struct RmResRef *r = &t->list[j];
|
||||
if (r->name) free(r->name);
|
||||
}
|
||||
free(t->list);
|
||||
}
|
||||
free(rp->types);
|
||||
}
|
||||
|
||||
free(rp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t res_typecount (RFILE *rp) {
|
||||
return rp->numTypes;
|
||||
}
|
||||
|
||||
uint32_t* res_types (RFILE *rp, uint32_t *buf, size_t start, size_t size, size_t *read, size_t *remain) {
|
||||
if (buf != NULL && size == 0) efail(EINVAL);
|
||||
if (size == 0 || start + size > rp->numTypes) size = rp->numTypes - start;
|
||||
if (buf == NULL) buf = calloc(size, sizeof(uint32_t));
|
||||
if (buf == NULL) efail(ENOMEM);
|
||||
if (read) *read = size;
|
||||
if (remain) *remain = rp->numTypes - (size + start);
|
||||
|
||||
for(size_t i=0; i < size; i++)
|
||||
buf[i] = rp->types[start+i].type;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
size_t res_count (RFILE *rp, uint32_t type) {
|
||||
struct RmType *t = res_type_find(rp, type);
|
||||
if (t == NULL) return 0;
|
||||
return t->count;
|
||||
}
|
||||
|
||||
ResAttr* res_list (RFILE *rp, uint32_t type, ResAttr *buf, size_t start, size_t size, size_t *read, size_t *remain) {
|
||||
struct RmType *t = res_type_find(rp, type);
|
||||
if (t == NULL) efail(ENOENT);
|
||||
if (buf != NULL && size == 0) efail(EINVAL);
|
||||
if (size == 0 || start + size > t->count) size = t->count - start;
|
||||
if (buf == NULL) buf = calloc(size, sizeof(ResAttr));
|
||||
if (buf == NULL) efail(ENOMEM);
|
||||
if (read) *read = size;
|
||||
if (remain) *remain = t->count - (size + start);
|
||||
|
||||
for(size_t i=0; i < size; i++) {
|
||||
buf[i].ID = t->list[start+i].ID;
|
||||
buf[i].flags = t->list[start+i].flags;
|
||||
buf[i].size = t->list[start+i].size;
|
||||
buf[i].name = t->list[start+i].name;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
ResAttr* res_attr (RFILE *rp, uint32_t type, int16_t ID, ResAttr *buf) {
|
||||
struct RmType *t = res_type_find(rp, type);
|
||||
if (t == NULL) efail(ENOENT);
|
||||
struct RmResRef *ref = res_ref_find(rp, t, ID);
|
||||
if (ref == NULL) efail(ENOENT);
|
||||
if (buf == NULL) buf = malloc(sizeof(ResAttr));
|
||||
if (buf == NULL) efail(ENOMEM);
|
||||
|
||||
buf->ID = ref->ID;
|
||||
buf->flags = ref->flags;
|
||||
buf->size = ref->size;
|
||||
buf->name = ref->name;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
ResAttr* res_attr_named (RFILE *rp, uint32_t type, const char *name, ResAttr *buf) {
|
||||
struct RmResRef *ref = res_ref_find_named(rp, res_type_find(rp, type), name);
|
||||
if (ref == NULL) efail(ENOENT);
|
||||
return res_attr(rp, type, ref->ID, buf);
|
||||
}
|
||||
|
||||
void* res_read (RFILE *rp, uint32_t type, int16_t ID, void *buf, size_t start, size_t size, size_t *read, size_t *remain) {
|
||||
struct RmType *t = res_type_find(rp, type);
|
||||
if (t == NULL) efail(ENOENT);
|
||||
struct RmResRef *ref = res_ref_find(rp, t, ID);
|
||||
if (ref == NULL) efail(ENOENT);
|
||||
|
||||
if (ref->flags.fl.compressed) efail(ENOSYS);
|
||||
return res_read_raw(rp, ref, buf, start, size, read, remain);
|
||||
}
|
||||
|
||||
void* res_read_named (RFILE *rp, uint32_t type, const char *name, void *buf, size_t start, size_t size, size_t *read, size_t *remain) {
|
||||
struct RmResRef *ref = res_ref_find_named(rp, res_type_find(rp, type), name);
|
||||
if (ref == NULL) efail(ENOENT);
|
||||
return res_read(rp, type, ref->ID, buf, start, size, read, remain);
|
||||
}
|
||||
|
||||
void* res_read_ind (RFILE *rp, uint32_t type, int16_t ind, void *buf, size_t start, size_t size, size_t *read, size_t *remain) {
|
||||
struct RmType *t = res_type_find(rp, type);
|
||||
if (t == NULL) efail(ENOENT);
|
||||
if (ind >= t->count || ind < 0) efail(ENOENT);
|
||||
struct RmResRef *ref = &t->list[ind];
|
||||
if (ref == NULL) efail(ENOENT);
|
||||
|
||||
if (ref->flags.fl.compressed) efail(ENOSYS);
|
||||
return res_read_raw(rp, ref, buf, start, size, read, remain);
|
||||
}
|
||||
|
||||
void res_printdir (RFILE *rp) {
|
||||
for(int i=0; i < rp->numTypes; i++) {
|
||||
struct RmType *t = &rp->types[i];
|
||||
for(int j=0; j < t->count; j++)
|
||||
printf("%c%c%c%c %hd (%ub) %s\n", TYPECHARS(t->type), t->list[j].ID, t->list[j].size, t->list[j].name?t->list[j].name:"");
|
||||
}
|
||||
}
|
||||
|
||||
void res_printattr (const ResAttr *attr, uint32_t type) {
|
||||
if (attr == NULL) return;
|
||||
if (type)
|
||||
printf("Type: %c%c%c%c\n", TYPECHARS(type));
|
||||
printf("ID: %hd\n", attr->ID);
|
||||
printf("Size: %u\n", attr->size);
|
||||
if (attr->name)
|
||||
printf("Name: %s\n", attr->name);
|
||||
printf("Attr: ");
|
||||
if (attr->flags.fl.sysRef) printf("sysRef%s", ((attr->flags.b&0x7F)?", ":""));
|
||||
if (attr->flags.fl.sysHeap) printf("sysHeap%s", ((attr->flags.b&0x3F)?", ":""));
|
||||
if (attr->flags.fl.purgeable) printf("purgeable%s", ((attr->flags.b&0x1F)?", ":""));
|
||||
if (attr->flags.fl.locked) printf("locked%s", ((attr->flags.b&0x0F)?", ":""));
|
||||
if (attr->flags.fl.protected) printf("protected%s", ((attr->flags.b&0x07)?", ":""));
|
||||
if (attr->flags.fl.preload) printf("preload%s", ((attr->flags.b&0x03)?", ":""));
|
||||
if (attr->flags.fl.changed) printf("changed%s", ((attr->flags.b&0x01)?", ":""));
|
||||
if (attr->flags.fl.compressed) printf("compressed");
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#pragma mark Private Functions
|
||||
#endif
|
||||
|
||||
void* res_bread (RFILE *rp, void *buf, size_t offset, size_t count) {
|
||||
if (offset+count > rp->size) efail(EFAULT);
|
||||
if (buf == NULL) buf = malloc(count);
|
||||
if (buf == NULL) efail(ENOMEM);
|
||||
|
||||
if (rp->buf)
|
||||
// memory
|
||||
memcpy(buf, rp->buf+offset, count);
|
||||
else if (rp->fp) {
|
||||
// file pointer
|
||||
fseek(rp->fp, offset, SEEK_SET);
|
||||
fread(buf, 1, count, rp->fp);
|
||||
} else {
|
||||
// functions
|
||||
rp->seek(rp->fpriv, (long)offset, (int)SEEK_SET);
|
||||
rp->read(rp->fpriv, buf, (unsigned long)count);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
uint32_t res_szread (RFILE *rp, size_t offset) {
|
||||
uint32_t r = 0;
|
||||
res_bread(rp, &r, offset, sizeof r);
|
||||
return ntohl(r);
|
||||
}
|
||||
|
||||
void* res_read_raw (RFILE *rp, struct RmResRef *ref, void *buf, size_t start, size_t size, size_t *read, size_t *remain) {
|
||||
if (ref == NULL) efail(ENOENT);
|
||||
if (buf != NULL && size == 0) efail(EINVAL);
|
||||
if (size == 0 || start + size > ref->psize) size = ref->psize - start;
|
||||
size_t rstart = start + ref->offset + rp->dataOffset + 4;
|
||||
if (rstart+size > rp->size) efail(EFAULT);
|
||||
if (buf == NULL) buf = malloc(ref->psize);
|
||||
if (buf == NULL) efail(ENOMEM);
|
||||
if (read) *read = size;
|
||||
if (remain) *remain = ref->psize - (size + start);
|
||||
|
||||
return res_bread(rp, buf, rstart, size);
|
||||
}
|
||||
|
||||
RFILE* res_load (RFILE *rp) {
|
||||
// read header
|
||||
struct RfHdr hdr;
|
||||
res_bread(rp, &hdr, 0, sizeof(struct RfHdr));
|
||||
rp->dataOffset = ntohl(hdr.dataOffset);
|
||||
|
||||
// read map
|
||||
struct RfMap *map = res_bread(rp, NULL, (size_t)ntohl(hdr.mapOffset), (size_t)ntohl(hdr.mapLength));
|
||||
if (map == NULL) egoto(EINVAL, error);
|
||||
rp->attributes = ntohs(map->attributes);
|
||||
struct RfTypeList *types = ((void*)map)+ntohs(map->typeListOffset);
|
||||
uint8_t *names = ((void*)map)+ntohs(map->nameListOffset);
|
||||
|
||||
// read types
|
||||
rp->numTypes = 1+ntohs(types->count);
|
||||
rp->types = calloc(rp->numTypes, sizeof(struct RmType));
|
||||
if (rp->types == NULL) egoto(ENOMEM, error);
|
||||
bzero(rp->types, sizeof(struct RmType) * rp->numTypes);
|
||||
|
||||
for(int i=0; i < rp->numTypes; i++) {
|
||||
struct RmType *t = &rp->types[i];
|
||||
t->type = ntohl(types->entry[i].type);
|
||||
t->count = 1+(size_t)ntohs(types->entry[i].count);
|
||||
t->list = calloc(t->count, sizeof(struct RmResRef));
|
||||
if (t->list == NULL) egoto(ENOMEM, error);
|
||||
bzero(t->list, t->count * sizeof(struct RmResRef));
|
||||
|
||||
// read resource refs & names
|
||||
int refsNeedSort = 0;
|
||||
struct RfRefEntry *ent = ((void*)types)+ntohs(types->entry[i].offset);
|
||||
for(int j=0; j < t->count; j++) {
|
||||
t->list[j].ID = ntohs(ent[j].ID);
|
||||
if (j && (t->list[j].ID < t->list[j-1].ID)) refsNeedSort = 1;
|
||||
t->list[j].flags.b = ent[j].attributes;
|
||||
t->list[j].offset = ((ent[j].offHi << 16) | ntohs(ent[j].offLo));
|
||||
t->list[j].psize = res_szread(rp, rp->dataOffset+t->list[j].offset);
|
||||
|
||||
uint16_t nameOffset = ntohs(ent[j].nameOffset);
|
||||
if (nameOffset == 0xFFFF) t->list[j].name = NULL;
|
||||
else {
|
||||
t->list[j].name = malloc(names[nameOffset]+1);
|
||||
if (t->list[j].name == NULL) egoto(ENOMEM, error);
|
||||
t->list[j].name[names[nameOffset]] = '\0';
|
||||
memcpy(t->list[j].name, &names[nameOffset+1], names[nameOffset]);
|
||||
}
|
||||
|
||||
// find logical size
|
||||
if (t->list[j].flags.fl.compressed) {
|
||||
struct RfCmpHdr cmpHdr;
|
||||
if (res_read_raw(rp, &t->list[j], &cmpHdr, 0, sizeof cmpHdr, NULL, NULL) == NULL)
|
||||
goto notCompressed;
|
||||
if (ntohl(cmpHdr.tag) != kCompressedResourceTag)
|
||||
goto notCompressed;
|
||||
t->list[j].size = ntohl(cmpHdr.size);
|
||||
|
||||
if (ntohl(cmpHdr.flags) == kCompressedResourceFlg0)
|
||||
t->list[j].dcmp = ntohs(cmpHdr.u.v0.dcmp);
|
||||
else if (ntohl(cmpHdr.flags) == kCompressedResourceFlg1)
|
||||
t->list[j].dcmp = ntohs(cmpHdr.u.v1.dcmp);
|
||||
else {
|
||||
t->list[j].dcmp = kDCMPInvalidFlags;
|
||||
fprintf(stderr, "libres: %c%c%c%c %hd: unknown compression flags\n", TYPECHARS(t->type), t->list[j].ID);
|
||||
}
|
||||
}
|
||||
|
||||
// resource not compressed, logical size = physical size
|
||||
if (t->list[j].flags.fl.compressed == 0) {
|
||||
notCompressed:
|
||||
t->list[j].flags.fl.compressed = 0;
|
||||
t->list[j].size = t->list[j].psize;
|
||||
}
|
||||
}
|
||||
|
||||
// keep ref list sorted
|
||||
// they are normally sorted by ID already, so it's rarely needed
|
||||
if (refsNeedSort) qsort(t->list, t->count, sizeof(struct RmResRef), (int(*)(const void*, const void*))res_ref_compar);
|
||||
}
|
||||
|
||||
// keep type list sorted
|
||||
// types are sorted alphabetically in files, we need them sorted numerically
|
||||
qsort(rp->types, rp->numTypes, sizeof(struct RmType), (int(*)(const void*, const void*))res_type_compar);
|
||||
|
||||
errno = 0;
|
||||
free(map);
|
||||
return rp;
|
||||
error:
|
||||
free(map);
|
||||
res_close(rp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int res_ref_compar (const struct RmResRef * a, const struct RmResRef * b) {
|
||||
return (int)(a->ID - b->ID);
|
||||
}
|
||||
|
||||
int res_type_compar (const struct RmType * a, const struct RmType * b) {
|
||||
// types are unsigned, compare manually
|
||||
if (a->type == b->type) return 0;
|
||||
if (a->type < b->type) return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct RmType * res_type_find (RFILE *rp, uint32_t type) {
|
||||
struct RmType key;
|
||||
key.type = type;
|
||||
return bsearch(&key, rp->types, rp->numTypes, sizeof(struct RmType), (int(*)(const void*, const void*))res_type_compar);
|
||||
}
|
||||
|
||||
struct RmResRef * res_ref_find (RFILE *rp, struct RmType *type, int16_t ID) {
|
||||
struct RmResRef key;
|
||||
if (type == NULL) return NULL;
|
||||
key.ID = ID;
|
||||
return bsearch(&key, type->list, type->count, sizeof(struct RmResRef), (int(*)(const void*, const void*))res_ref_compar);
|
||||
}
|
||||
|
||||
struct RmResRef * res_ref_find_named (RFILE *rp, struct RmType *type, const char *name) {
|
||||
struct RmResRef key;
|
||||
if (type == NULL) return NULL;
|
||||
key.name = (char*)name;
|
||||
size_t count = type->count;
|
||||
return lfind(&key, type->list, &count, sizeof(struct RmResRef), (int(*)(const void*, const void*))res_ref_name_compar);
|
||||
}
|
||||
|
||||
int res_ref_name_compar (const struct RmResRef * a, const struct RmResRef * b) {
|
||||
if (a->name == NULL || b->name == NULL) return 1;
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* libres - library for reading Macintosh resource forks
|
||||
* Copyright (C) 2008-2009 Jesus A. Alvarez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _RES_H_
|
||||
#define _RES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TYPECHARS(t) ((t) >> 24) & 0xFF, ((t) >> 16) & 0xFF, ((t) >> 8) & 0xFF, (t) & 0xFF
|
||||
|
||||
extern const char * libres_id;
|
||||
|
||||
// in-memory structures
|
||||
typedef struct RFILE RFILE;
|
||||
|
||||
typedef union __attribute__ ((__packed__)) {
|
||||
struct {
|
||||
unsigned int compressed:1;
|
||||
unsigned int changed:1;
|
||||
unsigned int preload:1;
|
||||
unsigned int protected:1;
|
||||
unsigned int locked:1;
|
||||
unsigned int purgeable:1;
|
||||
unsigned int sysHeap:1;
|
||||
unsigned int sysRef:1;
|
||||
} fl;
|
||||
uint8_t b;
|
||||
} RFlags;
|
||||
|
||||
/* this structure is only valid while the file is open */
|
||||
struct ResAttr {
|
||||
int16_t ID;
|
||||
RFlags flags;
|
||||
uint32_t size;
|
||||
const char* name; // owned by file, MacRoman encoding
|
||||
};
|
||||
typedef struct ResAttr ResAttr;
|
||||
|
||||
typedef unsigned long (*res_seek_func)(void *, long, int);
|
||||
typedef unsigned long (*res_read_func)(void *, void *, unsigned long);
|
||||
|
||||
/**
|
||||
@param path path to file
|
||||
@param mode reserved, set to 0
|
||||
@returns reference to open file or NULL
|
||||
*/
|
||||
RFILE* res_open (const char *path, int mode);
|
||||
|
||||
/**
|
||||
@param buf resource buffer
|
||||
@param size size of buf
|
||||
@param copy 1 to make a copy of buf, otherwise libres takes ownership of buf and frees it when it's closed
|
||||
@returns reference to open file or NULL
|
||||
*/
|
||||
RFILE* res_open_mem (void *buf, size_t size, int copy);
|
||||
RFILE* res_open_funcs (void *priv, res_seek_func seek, res_read_func read);
|
||||
int res_close (RFILE *rp);
|
||||
|
||||
/// number of resource types
|
||||
size_t res_typecount (RFILE *rp);
|
||||
|
||||
/**
|
||||
List resource types in the file
|
||||
@param buf list output or NULL
|
||||
@param start start element or 0
|
||||
@param size number of elements to list or 0
|
||||
@param read returns number of resources read, if not NULL
|
||||
@param remain number of resources left unread, if not NULL
|
||||
@returns buf (filled), or newly allocated complete list
|
||||
*/
|
||||
uint32_t* res_types (RFILE *rp, uint32_t *buf, size_t start, size_t size, size_t *read, size_t *remain);
|
||||
|
||||
/// count resources of a type
|
||||
size_t res_count (RFILE *rp, uint32_t type);
|
||||
|
||||
/**
|
||||
List resource attributes
|
||||
@param buf list output or NULL
|
||||
@param start start element or 0
|
||||
@param size number of elements to list or 0
|
||||
@param read returns number of resources read, if not NULL
|
||||
@param remain number of resources left unread, if not NULL
|
||||
@returns buf (filled), or newly allocated complete list
|
||||
*/
|
||||
ResAttr* res_list (RFILE *rp, uint32_t type, ResAttr *buf, size_t start, size_t size, size_t *read, size_t *remain);
|
||||
|
||||
/// get attributes for a resource (by ID)
|
||||
ResAttr* res_attr (RFILE *rp, uint32_t type, int16_t ID, ResAttr *buf);
|
||||
|
||||
/// get attributes for a resource (by name)
|
||||
ResAttr* res_attr_named (RFILE *rp, uint32_t type, const char *name, ResAttr *buf);
|
||||
|
||||
/**
|
||||
Read a resource
|
||||
@param buf resource output or NULL
|
||||
@param size size of buffer, ignored if buf is NULL
|
||||
@returns buf (filled), or newly allocated resource
|
||||
*/
|
||||
void* res_read (RFILE *rp, uint32_t type, int16_t ID, void *buf, size_t start, size_t size, size_t *read, size_t *remain);
|
||||
void* res_read_named (RFILE *rp, uint32_t type, const char *name, void *buf, size_t start, size_t size, size_t *read, size_t *remain);
|
||||
|
||||
void res_printdir (RFILE *rp);
|
||||
void res_printattr (const ResAttr *attr, uint32_t type);
|
||||
#endif /* _RES_H_ */
|
Loading…
Reference in New Issue