bool unshield_read_common_header(uint8_t** buffer, CommonHeader* common) { uint8_t* p = *buffer; common->signature = READ_UINT32(p); p += 4; if (CAB_SIGNATURE != common->signature) { unshield_error("Invalid file signature"); if (MSCF_SIGNATURE == common->signature) unshield_warning("Found Microsoft Cabinet header. Use cabextract (http://www.kyz.uklinux.net/cabextract.php) to unpack this file."); return false; } common->version = READ_UINT32(p); p += 4; common->volume_info = READ_UINT32(p); p += 4; common->cab_descriptor_offset = READ_UINT32(p); p += 4; common->cab_descriptor_size = READ_UINT32(p); p += 4; #if VERBOSE unshield_trace("Common header: %08x %08x %08x %08x", common->version, common->volume_info, common->cab_descriptor_offset, common->cab_descriptor_size); #endif *buffer = p; return true; }
const char* unshield_directory_name(Unshield* unshield, int index) { if (unshield && index >= 0) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; if (index < (int)header->cab.directory_count) return unshield_get_utf8_string(header, header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + header->file_table[index]); } unshield_warning("Failed to get directory name %i", index); return NULL; }
const char* unshield_file_name (Unshield* unshield, int index)/*{{{*/ { FileDescriptor* fd = unshield_get_file_descriptor(unshield, index); if (fd) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; return unshield_get_utf8_string(header, header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + fd->name_offset); } unshield_warning("Failed to get file descriptor %i", index); return NULL; }/*}}}*/
bool unshield_file_save_old(Unshield* unshield, int index, const char* filename)/*{{{*/ { /* XXX: Thou Shalt Not Cut & Paste... */ bool success = false; FILE* output = NULL; size_t input_buffer_size = BUFFER_SIZE; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); int bytes_left; uLong total_written = 0; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; if (!unshield) goto exit; if (!(file_descriptor = unshield_get_file_descriptor(unshield, index))) { unshield_error("Failed to get file descriptor for file %i", index); goto exit; } if ((file_descriptor->flags & FILE_INVALID) || 0 == file_descriptor->data_offset) { /* invalid file */ goto exit; } if (file_descriptor->link_flags & LINK_PREV) { success = unshield_file_save(unshield, file_descriptor->link_previous, filename); goto exit; } reader = unshield_reader_create(unshield, index, file_descriptor); if (!reader) { unshield_error("Failed to create data reader for file %i", index); goto exit; } if (unshield_fsize(reader->volume_file) == (long)file_descriptor->data_offset) { unshield_error("File %i is not inside the cabinet.", index); goto exit; } if (filename) { output = fopen(filename, "w"); if (!output) { unshield_error("Failed to open output file '%s'", filename); goto exit; } } if (file_descriptor->flags & FILE_COMPRESSED) bytes_left = file_descriptor->compressed_size; else bytes_left = file_descriptor->expanded_size; /*unshield_trace("Bytes to read: %i", bytes_left);*/ while (bytes_left > 0) { uLong bytes_to_write = 0; int result; if (file_descriptor->flags & FILE_COMPRESSED) { static const uint8_t END_OF_CHUNK[4] = { 0x00, 0x00, 0xff, 0xff }; uLong read_bytes; size_t input_size = reader->volume_bytes_left; uint8_t* chunk_buffer; while (input_size > input_buffer_size) { input_buffer_size *= 2; #if VERBOSE >= 3 unshield_trace("increased input_buffer_size to 0x%x", input_buffer_size); #endif input_buffer = realloc(input_buffer, input_buffer_size); assert(input_buffer); } if (!unshield_reader_read(reader, input_buffer, input_size)) { #if VERBOSE unshield_error("Failed to read 0x%x bytes of file %i (%s) from input cabinet file %i", input_size, index, unshield_file_name(unshield, index), file_descriptor->volume); #endif goto exit; } bytes_left -= input_size; for (chunk_buffer = input_buffer; input_size; ) { size_t chunk_size; uint8_t* match = find_bytes(chunk_buffer, input_size, END_OF_CHUNK, sizeof(END_OF_CHUNK)); if (!match) { unshield_error("Could not find end of chunk for file %i (%s) from input cabinet file %i", index, unshield_file_name(unshield, index), file_descriptor->volume); goto exit; } chunk_size = match - chunk_buffer; /* Detect when the chunk actually contains the end of chunk marker. Needed by Qtime.smk from "The Feeble Files - spanish version". The first bit of a compressed block is always zero, so we apply this workaround if it's a one. A possibly more proper fix for this would be to have unshield_uncompress_old eat compressed data and discard chunk markers inbetween. */ while ((chunk_size + sizeof(END_OF_CHUNK)) < input_size && chunk_buffer[chunk_size + sizeof(END_OF_CHUNK)] & 1) { unshield_warning("It seems like we have an end of chunk marker inside of a chunk."); chunk_size += sizeof(END_OF_CHUNK); match = find_bytes(chunk_buffer + chunk_size, input_size - chunk_size, END_OF_CHUNK, sizeof(END_OF_CHUNK)); if (!match) { unshield_error("Could not find end of chunk for file %i (%s) from input cabinet file %i", index, unshield_file_name(unshield, index), file_descriptor->volume); goto exit; } chunk_size = match - chunk_buffer; } #if VERBOSE >= 3 unshield_trace("chunk_size = 0x%x", chunk_size); #endif /* add a null byte to make inflate happy */ chunk_buffer[chunk_size] = 0; bytes_to_write = BUFFER_SIZE; read_bytes = chunk_size; result = unshield_uncompress_old(output_buffer, &bytes_to_write, chunk_buffer, &read_bytes); if (Z_OK != result) { unshield_error("Decompression failed with code %i. input_size=%i, volume_bytes_left=%i, volume=%i, read_bytes=%i", result, input_size, reader->volume_bytes_left, file_descriptor->volume, read_bytes); goto exit; } #if VERBOSE >= 3 unshield_trace("read_bytes = 0x%x", read_bytes); #endif chunk_buffer += chunk_size; chunk_buffer += sizeof(END_OF_CHUNK); input_size -= chunk_size; input_size -= sizeof(END_OF_CHUNK); if (output) if (bytes_to_write != fwrite(output_buffer, 1, bytes_to_write, output)) { unshield_error("Failed to write %i bytes to file '%s'", bytes_to_write, filename); goto exit; } total_written += bytes_to_write; } } else { bytes_to_write = MIN(bytes_left, BUFFER_SIZE); if (!unshield_reader_read(reader, output_buffer, bytes_to_write)) { #if VERBOSE unshield_error("Failed to read %i bytes from input cabinet file %i", bytes_to_write, file_descriptor->volume); #endif goto exit; } bytes_left -= bytes_to_write; if (output) if (bytes_to_write != fwrite(output_buffer, 1, bytes_to_write, output)) { unshield_error("Failed to write %i bytes to file '%s'", bytes_to_write, filename); goto exit; } total_written += bytes_to_write; } } if (file_descriptor->expanded_size != total_written) { unshield_error("Expanded size expected to be %i, but was %i", file_descriptor->expanded_size, total_written); goto exit; } success = true; exit: unshield_reader_destroy(reader); FCLOSE(output); FREE(input_buffer); FREE(output_buffer); return success; }/*}}}*/
static FileDescriptor* unshield_read_file_descriptor(Unshield* unshield, int index) { /* XXX: multi-volume support... */ Header* header = unshield->header_list; uint8_t* p = NULL; uint8_t* saved_p = NULL; FileDescriptor* fd = NEW1(FileDescriptor); switch (header->major_version) { case 0: case 5: saved_p = p = header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + header->file_table[header->cab.directory_count + index]; #if VERBOSE unshield_trace("File descriptor offset %i: %08x", index, p - header->data); #endif fd->volume = header->index; fd->name_offset = READ_UINT32(p); p += 4; fd->directory_index = READ_UINT32(p); p += 4; fd->flags = READ_UINT16(p); p += 2; fd->expanded_size = READ_UINT32(p); p += 4; fd->compressed_size = READ_UINT32(p); p += 4; p += 0x14; fd->data_offset = READ_UINT32(p); p += 4; #if VERBOSE >= 2 unshield_trace("Name offset: %08x", fd->name_offset); unshield_trace("Directory index: %08x", fd->directory_index); unshield_trace("Flags: %04x", fd->flags); unshield_trace("Expanded size: %08x", fd->expanded_size); unshield_trace("Compressed size: %08x", fd->compressed_size); unshield_trace("Data offset: %08x", fd->data_offset); #endif if (header->major_version == 5) { memcpy(fd->md5, p, 0x10); p += 0x10; assert((p - saved_p) == 0x3a); } break; case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: default: saved_p = p = header->data + header->common.cab_descriptor_offset + header->cab.file_table_offset + header->cab.file_table_offset2 + index * 0x57; #if VERBOSE unshield_trace("File descriptor offset: %08x", p - header->data); #endif fd->flags = READ_UINT16(p); p += 2; fd->expanded_size = READ_UINT32(p); p += 4; p += 4; fd->compressed_size = READ_UINT32(p); p += 4; p += 4; fd->data_offset = READ_UINT32(p); p += 4; p += 4; memcpy(fd->md5, p, 0x10); p += 0x10; p += 0x10; fd->name_offset = READ_UINT32(p); p += 4; fd->directory_index = READ_UINT16(p); p += 2; assert((p - saved_p) == 0x40); p += 0xc; fd->link_previous = READ_UINT32(p); p += 4; fd->link_next = READ_UINT32(p); p += 4; fd->link_flags = *p; p ++; #if VERBOSE if (fd->link_flags != LINK_NONE) { unshield_trace("Link: previous=%i, next=%i, flags=%i", fd->link_previous, fd->link_next, fd->link_flags); } #endif fd->volume = READ_UINT16(p); p += 2; assert((p - saved_p) == 0x57); break; } if (!(fd->flags & FILE_COMPRESSED) && fd->compressed_size != fd->expanded_size) { unshield_warning("File is not compressed but compressed size is %08x and expanded size is %08x", fd->compressed_size, fd->expanded_size); } return fd; }