246 строки
6.1 KiB
C
246 строки
6.1 KiB
C
/*
|
|
* dmgfile.c
|
|
* libdmg-hfsplus
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <zlib.h>
|
|
|
|
#include <dmg/dmg.h>
|
|
#include <dmg/dmgfile.h>
|
|
|
|
static void cacheRun(DMG* dmg, BLKXTable* blkx, int run) {
|
|
size_t bufferSize;
|
|
z_stream strm;
|
|
void* inBuffer;
|
|
int ret;
|
|
size_t have;
|
|
|
|
if(dmg->runData) {
|
|
free(dmg->runData);
|
|
}
|
|
|
|
bufferSize = SECTOR_SIZE * blkx->runs[run].sectorCount;
|
|
|
|
dmg->runData = (void*) malloc(bufferSize);
|
|
inBuffer = (void*) malloc(bufferSize);
|
|
memset(dmg->runData, 0, bufferSize);
|
|
|
|
ASSERT(dmg->dmg->seek(dmg->dmg, blkx->dataStart + blkx->runs[run].compOffset) == 0, "fseeko");
|
|
|
|
switch(blkx->runs[run].type) {
|
|
case BLOCK_ZLIB:
|
|
strm.zalloc = Z_NULL;
|
|
strm.zfree = Z_NULL;
|
|
strm.opaque = Z_NULL;
|
|
strm.avail_in = 0;
|
|
strm.next_in = Z_NULL;
|
|
|
|
ASSERT(inflateInit(&strm) == Z_OK, "inflateInit");
|
|
|
|
ASSERT((strm.avail_in = dmg->dmg->read(dmg->dmg, inBuffer, blkx->runs[run].compLength)) == blkx->runs[run].compLength, "fread");
|
|
strm.next_in = (unsigned char*) inBuffer;
|
|
|
|
do {
|
|
strm.avail_out = bufferSize;
|
|
strm.next_out = (unsigned char*) dmg->runData;
|
|
ASSERT((ret = inflate(&strm, Z_NO_FLUSH)) != Z_STREAM_ERROR, "inflate/Z_STREAM_ERROR");
|
|
if(ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_STREAM_END) {
|
|
ASSERT(FALSE, "inflate");
|
|
}
|
|
have = bufferSize - strm.avail_out;
|
|
} while (strm.avail_out == 0);
|
|
|
|
ASSERT(inflateEnd(&strm) == Z_OK, "inflateEnd");
|
|
break;
|
|
case BLOCK_RAW:
|
|
ASSERT((have = dmg->dmg->read(dmg->dmg, dmg->runData, blkx->runs[run].compLength)) == blkx->runs[run].compLength, "fread");
|
|
break;
|
|
case BLOCK_IGNORE:
|
|
break;
|
|
case BLOCK_COMMENT:
|
|
break;
|
|
case BLOCK_TERMINATOR:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
dmg->runStart = (blkx->runs[run].sectorStart + blkx->firstSectorNumber) * SECTOR_SIZE;
|
|
dmg->runEnd = dmg->runStart + (blkx->runs[run].sectorCount * SECTOR_SIZE);
|
|
}
|
|
|
|
static void cacheOffset(DMG* dmg, off_t location) {
|
|
int i;
|
|
int j;
|
|
uint64_t sector;
|
|
|
|
sector = (uint64_t)(location / SECTOR_SIZE);
|
|
|
|
for(i = 0; i < dmg->numBLKX; i++) {
|
|
if(sector >= dmg->blkx[i]->firstSectorNumber && sector < (dmg->blkx[i]->firstSectorNumber + dmg->blkx[i]->sectorCount)) {
|
|
for(j = 0; j < dmg->blkx[i]->blocksRunCount; j++) {
|
|
if(sector >= (dmg->blkx[i]->firstSectorNumber + dmg->blkx[i]->runs[j].sectorStart) &&
|
|
sector < (dmg->blkx[i]->firstSectorNumber + dmg->blkx[i]->runs[j].sectorStart + dmg->blkx[i]->runs[j].sectorCount)) {
|
|
cacheRun(dmg, dmg->blkx[i], j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int dmgFileRead(io_func* io, off_t location, size_t size, void *buffer) {
|
|
DMG* dmg;
|
|
size_t toRead;
|
|
|
|
dmg = (DMG*) io->data;
|
|
|
|
location += dmg->offset;
|
|
|
|
if(size == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
if(location < dmg->runStart || location >= dmg->runEnd) {
|
|
cacheOffset(dmg, location);
|
|
}
|
|
|
|
if((location + size) > dmg->runEnd) {
|
|
toRead = dmg->runEnd - location;
|
|
} else {
|
|
toRead = size;
|
|
}
|
|
|
|
memcpy(buffer, (void*)((uint8_t*)dmg->runData + (uint32_t)(location - dmg->runStart)), toRead);
|
|
size -= toRead;
|
|
location += toRead;
|
|
buffer = (void*)((uint8_t*)buffer + toRead);
|
|
|
|
if(size > 0) {
|
|
return dmgFileRead(io, location - dmg->offset, size, buffer);
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static int dmgFileWrite(io_func* io, off_t location, size_t size, void *buffer) {
|
|
fprintf(stderr, "Error: writing to DMGs is not supported (impossible to achieve with compressed images and retain asr multicast ordering).\n");
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void closeDmgFile(io_func* io) {
|
|
DMG* dmg;
|
|
|
|
dmg = (DMG*) io->data;
|
|
|
|
if(dmg->runData) {
|
|
free(dmg->runData);
|
|
}
|
|
|
|
free(dmg->blkx);
|
|
releaseResources(dmg->resources);
|
|
dmg->dmg->close(dmg->dmg);
|
|
free(dmg);
|
|
free(io);
|
|
}
|
|
|
|
io_func* openDmgFile(AbstractFile* abstractIn) {
|
|
off_t fileLength;
|
|
UDIFResourceFile resourceFile;
|
|
DMG* dmg;
|
|
ResourceData* blkx;
|
|
ResourceData* curData;
|
|
int i;
|
|
|
|
io_func* toReturn;
|
|
|
|
if(abstractIn == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
fileLength = abstractIn->getLength(abstractIn);
|
|
abstractIn->seek(abstractIn, fileLength - sizeof(UDIFResourceFile));
|
|
readUDIFResourceFile(abstractIn, &resourceFile);
|
|
|
|
dmg = (DMG*) malloc(sizeof(DMG));
|
|
dmg->dmg = abstractIn;
|
|
dmg->resources = readResources(abstractIn, &resourceFile);
|
|
dmg->numBLKX = 0;
|
|
|
|
blkx = (getResourceByKey(dmg->resources, "blkx"))->data;
|
|
|
|
curData = blkx;
|
|
while(curData != NULL) {
|
|
dmg->numBLKX++;
|
|
curData = curData->next;
|
|
}
|
|
|
|
dmg->blkx = (BLKXTable**) malloc(sizeof(BLKXTable*) * dmg->numBLKX);
|
|
|
|
i = 0;
|
|
while(blkx != NULL) {
|
|
dmg->blkx[i] = (BLKXTable*)(blkx->data);
|
|
i++;
|
|
blkx = blkx->next;
|
|
}
|
|
|
|
dmg->offset = 0;
|
|
|
|
dmg->runData = NULL;
|
|
cacheOffset(dmg, 0);
|
|
|
|
toReturn = (io_func*) malloc(sizeof(io_func));
|
|
|
|
toReturn->data = dmg;
|
|
toReturn->read = &dmgFileRead;
|
|
toReturn->write = &dmgFileWrite;
|
|
toReturn->close = &closeDmgFile;
|
|
|
|
return toReturn;
|
|
}
|
|
|
|
io_func* openDmgFilePartition(AbstractFile* abstractIn, int partition) {
|
|
io_func* toReturn;
|
|
Partition* partitions;
|
|
uint8_t ddmBuffer[SECTOR_SIZE];
|
|
DriverDescriptorRecord* ddm;
|
|
int numPartitions;
|
|
int i;
|
|
unsigned int BlockSize;
|
|
|
|
toReturn = openDmgFile(abstractIn);
|
|
|
|
if(toReturn == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
toReturn->read(toReturn, 0, SECTOR_SIZE, ddmBuffer);
|
|
ddm = (DriverDescriptorRecord*) ddmBuffer;
|
|
flipDriverDescriptorRecord(ddm, FALSE);
|
|
BlockSize = ddm->sbBlkSize;
|
|
|
|
partitions = (Partition*) malloc(BlockSize);
|
|
toReturn->read(toReturn, BlockSize, BlockSize, partitions);
|
|
flipPartitionMultiple(partitions, FALSE, FALSE, BlockSize);
|
|
numPartitions = partitions->pmMapBlkCnt;
|
|
partitions = (Partition*) realloc(partitions, numPartitions * BlockSize);
|
|
toReturn->read(toReturn, BlockSize, numPartitions * BlockSize, partitions);
|
|
flipPartition(partitions, FALSE, BlockSize);
|
|
|
|
if(partition >= 0) {
|
|
((DMG*)toReturn->data)->offset = partitions[partition].pmPyPartStart * BlockSize;
|
|
} else {
|
|
for(i = 0; i < numPartitions; i++) {
|
|
if(strcmp((char*)partitions->pmParType, "Apple_HFSX") == 0 || strcmp((char*)partitions->pmParType, "Apple_HFS") == 0) {
|
|
((DMG*)toReturn->data)->offset = partitions->pmPyPartStart * BlockSize;
|
|
break;
|
|
}
|
|
partitions = (Partition*)((uint8_t*)partitions + BlockSize);
|
|
}
|
|
}
|
|
|
|
return toReturn;
|
|
}
|