int main(int argc, char** argv) { int zip_mode = 0; if (argc >= 2 && strcmp(argv[1], "-z") == 0) { zip_mode = 1; --argc; ++argv; } size_t bonus_size = 0; unsigned char* bonus_data = NULL; if (argc >= 3 && strcmp(argv[1], "-b") == 0) { struct stat st; if (stat(argv[2], &st) != 0) { printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); return 1; } bonus_size = st.st_size; bonus_data = malloc(bonus_size); FILE* f = fopen(argv[2], "rb"); if (f == NULL) { printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); return 1; } if (fread(bonus_data, 1, bonus_size, f) != bonus_size) { printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno)); return 1; } fclose(f); argc -= 2; argv += 2; } if (argc != 4) { usage: printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n", argv[0]); return 2; } int num_src_chunks; ImageChunk* src_chunks; int num_tgt_chunks; ImageChunk* tgt_chunks; int i; if (zip_mode) { if (ReadZip(argv[1], &num_src_chunks, &src_chunks, 1) == NULL) { printf("failed to break apart source zip file\n"); return 1; } if (ReadZip(argv[2], &num_tgt_chunks, &tgt_chunks, 0) == NULL) { printf("failed to break apart target zip file\n"); return 1; } } else { if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) { printf("failed to break apart source image\n"); return 1; } if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) { printf("failed to break apart target image\n"); return 1; } // Verify that the source and target images have the same chunk // structure (ie, the same sequence of deflate and normal chunks). if (!zip_mode) { // Merge the gzip header and footer in with any adjacent // normal chunks. MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); MergeAdjacentNormalChunks(src_chunks, &num_src_chunks); } if (num_src_chunks != num_tgt_chunks) { printf("source and target don't have same number of chunks!\n"); printf("source chunks:\n"); DumpChunks(src_chunks, num_src_chunks); printf("target chunks:\n"); DumpChunks(tgt_chunks, num_tgt_chunks); return 1; } for (i = 0; i < num_src_chunks; ++i) { if (src_chunks[i].type != tgt_chunks[i].type) { printf("source and target don't have same chunk " "structure! (chunk %d)\n", i); printf("source chunks:\n"); DumpChunks(src_chunks, num_src_chunks); printf("target chunks:\n"); DumpChunks(tgt_chunks, num_tgt_chunks); return 1; } } } for (i = 0; i < num_tgt_chunks; ++i) { if (tgt_chunks[i].type == CHUNK_DEFLATE) { // Confirm that given the uncompressed chunk data in the target, we // can recompress it and get exactly the same bits as are in the // input target image. If this fails, treat the chunk as a normal // non-deflated chunk. if (ReconstructDeflateChunk(tgt_chunks+i) < 0) { printf("failed to reconstruct target deflate chunk %d [%s]; " "treating as normal\n", i, tgt_chunks[i].filename); ChangeDeflateChunkToNormal(tgt_chunks+i); if (zip_mode) { ImageChunk* src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks); if (src) { ChangeDeflateChunkToNormal(src); } } else { ChangeDeflateChunkToNormal(src_chunks+i); } continue; } // If two deflate chunks are identical (eg, the kernel has not // changed between two builds), treat them as normal chunks. // This makes applypatch much faster -- it can apply a trivial // patch to the compressed data, rather than uncompressing and // recompressing to apply the trivial patch to the uncompressed // data. ImageChunk* src; if (zip_mode) { src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks); } else { src = src_chunks+i; } if (src == NULL || AreChunksEqual(tgt_chunks+i, src)) { ChangeDeflateChunkToNormal(tgt_chunks+i); if (src) { ChangeDeflateChunkToNormal(src); } } } } // Merging neighboring normal chunks. if (zip_mode) { // For zips, we only need to do this to the target: deflated // chunks are matched via filename, and normal chunks are patched // using the entire source file as the source. MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); } else { // For images, we need to maintain the parallel structure of the // chunk lists, so do the merging in both the source and target // lists. MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); MergeAdjacentNormalChunks(src_chunks, &num_src_chunks); if (num_src_chunks != num_tgt_chunks) { // This shouldn't happen. printf("merging normal chunks went awry\n"); return 1; } } // Compute bsdiff patches for each chunk's data (the uncompressed // data, in the case of deflate chunks). DumpChunks(src_chunks, num_src_chunks); printf("Construct patches for %d chunks...\n", num_tgt_chunks); unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*)); size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t)); for (i = 0; i < num_tgt_chunks; ++i) { if (zip_mode) { ImageChunk* src; if (tgt_chunks[i].type == CHUNK_DEFLATE && (src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks))) { patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i); } else { patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i); } } else { if (i == 1 && bonus_data) { printf(" using %zu bytes of bonus data for chunk %d\n", bonus_size, i); src_chunks[i].data = realloc(src_chunks[i].data, src_chunks[i].len + bonus_size); memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size); src_chunks[i].len += bonus_size; } patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i); } printf("patch %3d is %zd bytes (of %zd)\n", i, patch_size[i], tgt_chunks[i].source_len); } // Figure out how big the imgdiff file header is going to be, so // that we can correctly compute the offset of each bsdiff patch // within the file. size_t total_header_size = 12; for (i = 0; i < num_tgt_chunks; ++i) { total_header_size += 4; switch (tgt_chunks[i].type) { case CHUNK_NORMAL: total_header_size += 8*4; break; case CHUNK_DEFLATE: total_header_size += 8*6 + 4*5; break; case CHUNK_RAW: total_header_size += 4 + patch_size[i]; break; } } size_t offset = total_header_size; FILE* f = fopen(argv[3], "wb"); // Write out the headers. fwrite("IMGDIFFX", 1, 8, f); Write4(num_tgt_chunks, f); for (i = 0; i < num_tgt_chunks; ++i) { Write4(tgt_chunks[i].type, f); switch (tgt_chunks[i].type) { case CHUNK_NORMAL: printf("chunk %3d: normal (%10zd, %10zd) %10zd\n", i, tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]); Write8(tgt_chunks[i].source_start, f); Write8(tgt_chunks[i].source_len, f); Write8(offset, f); Write8(patch_size[i], f); offset += patch_size[i]; break; case CHUNK_DEFLATE: printf("chunk %3d: deflate (%10zd, %10zd) %10zd %s\n", i, tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i], tgt_chunks[i].filename); Write8(tgt_chunks[i].source_start, f); Write8(tgt_chunks[i].source_len, f); Write8(offset, f); Write8(patch_size[i], f); Write8(tgt_chunks[i].source_uncompressed_len, f); Write8(tgt_chunks[i].len, f); Write4(tgt_chunks[i].level, f); Write4(tgt_chunks[i].method, f); Write4(tgt_chunks[i].windowBits, f); Write4(tgt_chunks[i].memLevel, f); Write4(tgt_chunks[i].strategy, f); offset += patch_size[i]; break; case CHUNK_RAW: printf("chunk %3d: raw (%10zd, %10zd)\n", i, tgt_chunks[i].start, tgt_chunks[i].len); Write4(patch_size[i], f); fwrite(patch_data[i], 1, patch_size[i], f); break; } } // Append each chunk's bsdiff patch, in order. for (i = 0; i < num_tgt_chunks; ++i) { if (tgt_chunks[i].type != CHUNK_RAW) { fwrite(patch_data[i], 1, patch_size[i], f); } } fclose(f); return 0; }
/* ============ GetWavinfo ============ */ wavinfo_t GetWavinfo (const char *name, byte *wav, size_t wavlength) { wavinfo_t info; int i; int format; int samples; memset (&info, 0, sizeof(info)); if (!wav) return info; iff_data = wav; iff_end = wav + wavlength; // find "RIFF" chunk FindChunk("RIFF"); if (!(data_p && !strncmp((char *)data_p + 8, "WAVE", 4))) { Con_Printf("Missing RIFF/WAVE chunks\n"); return info; } // get "fmt " chunk iff_data = data_p + 12; #if 0 DumpChunks (); #endif FindChunk("fmt "); if (!data_p) { Con_Printf("Missing fmt chunk\n"); return info; } data_p += 8; format = GetLittleShort(); if (format != 1) { Con_Printf("Microsoft PCM format only\n"); return info; } info.channels = GetLittleShort(); info.rate = GetLittleLong(); data_p += 4 + 2; info.width = GetLittleShort() / 8; // get cue chunk FindChunk("cue "); if (data_p) { data_p += 32; info.loopstart = GetLittleLong(); // Con_Printf("loopstart=%d\n", sfx->loopstart); // if the next chunk is a LIST chunk, look for a cue length marker FindNextChunk ("LIST"); if (data_p) { if (!strncmp((char *)data_p + 28, "mark", 4)) { // this is not a proper parse, but it works with cooledit... data_p += 24; i = GetLittleLong(); // samples in loop info.samples = info.loopstart + i; // Con_Printf("looped length: %i\n", i); } } } else info.loopstart = -1; // find data chunk FindChunk("data"); if (!data_p) { Con_Printf("Missing data chunk\n"); return info; } data_p += 4; samples = GetLittleLong() / info.width; if (info.samples) { if (samples < info.samples) Sys_Error ("Sound %s has a bad loop length", name); } else info.samples = samples; info.dataofs = data_p - wav; return info; }