bool Launcher::UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) { bool success; bfs::path dirname; bfs::path filename; int directory = unshield_file_directory(unshield, index); dirname = output_dir; if (prefix && prefix[0]) dirname /= prefix; if (directory >= 0) { const char* tmp = unshield_directory_name(unshield, directory); if (tmp && tmp[0]) fill_path(dirname, tmp); } make_sure_directory_exists(dirname); filename = dirname; filename /= unshield_file_name(unshield, index); emit signalGUI(QString("Extracting: ") + QString(filename.c_str())); success = unshield_file_save(unshield, index, filename.c_str()); if (!success) bfs::remove(filename); return success; }
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; }/*}}}*/
/* * If filename is NULL, just throw away the result */ bool unshield_file_save (Unshield* unshield, int index, const char* filename)/*{{{*/ { bool success = false; FILE* output = NULL; unsigned char* input_buffer = (unsigned char*)malloc(BUFFER_SIZE+1); unsigned char* output_buffer = (unsigned char*)malloc(BUFFER_SIZE); int bytes_left; uLong total_written = 0; UnshieldReader* reader = NULL; FileDescriptor* file_descriptor; MD5_CTX md5; MD5Init(&md5); 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 = BUFFER_SIZE; int result; if (file_descriptor->flags & FILE_COMPRESSED) { uLong read_bytes; uint16_t bytes_to_read = 0; if (!unshield_reader_read(reader, &bytes_to_read, sizeof(bytes_to_read))) { #if VERBOSE unshield_error("Failed to read %i bytes of file %i (%s) from input cabinet file %i", sizeof(bytes_to_read), index, unshield_file_name(unshield, index), file_descriptor->volume); #endif goto exit; } bytes_to_read = letoh16(bytes_to_read); if (!unshield_reader_read(reader, input_buffer, bytes_to_read)) { #if VERBOSE unshield_error("Failed to read %i bytes of file %i (%s) from input cabinet file %i", bytes_to_read, index, unshield_file_name(unshield, index), file_descriptor->volume); #endif goto exit; } /* add a null byte to make inflate happy */ input_buffer[bytes_to_read] = 0; read_bytes = bytes_to_read+1; result = unshield_uncompress(output_buffer, &bytes_to_write, input_buffer, &read_bytes); if (Z_OK != result) { unshield_error("Decompression failed with code %i. bytes_to_read=%i, volume_bytes_left=%i, volume=%i, read_bytes=%i", result, bytes_to_read, reader->volume_bytes_left, file_descriptor->volume, read_bytes); goto exit; } #if VERBOSE >= 3 unshield_trace("read_bytes = %i", read_bytes); #endif bytes_left -= 2; bytes_left -= bytes_to_read; } 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; } MD5Update(&md5, output_buffer, 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; } if (unshield->header_list->major_version >= 6) { unsigned char md5result[16]; MD5Final(md5result, &md5); if (0 != memcmp(md5result, file_descriptor->md5, 16)) { unshield_error("MD5 checksum failure for file %i (%s)", index, unshield_file_name(unshield, index)); goto exit; } } success = true; exit: unshield_reader_destroy(reader); FCLOSE(output); FREE(input_buffer); FREE(output_buffer); return success; }/*}}}*/