mirror of
https://github.com/zydeco/libres.git
synced 2024-06-04 20:29:26 +00:00
initial commit
This commit is contained in:
commit
2562781a7e
16
Makefile
Normal file
16
Makefile
Normal file
|
@ -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
|
16
README
Normal file
16
README
Normal file
|
@ -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.
|
126
libres_internal.h
Normal file
126
libres_internal.h
Normal file
|
@ -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);
|
395
res.c
Normal file
395
res.c
Normal file
|
@ -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);
|
||||||
|
}
|
121
res.h
Normal file
121
res.h
Normal file
|
@ -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
Block a user