static bool backup_original_manifest(void) { unsigned int len, pos = 0; char block[BLOCK_SIZE]; FILE *input, *output; bool ret = false; struct stat s; // No existing manifest; this is non-fatal if(stat(MANIFEST_TXT, &s)) { ret = true; goto err_out; } input = fopen_unsafe(MANIFEST_TXT, "rb"); if(!input) goto err_out; output = fopen_unsafe(MANIFEST_TXT "~", "wb"); if(!output) goto err_close_input; len = (unsigned int)ftell_and_rewind(input); while(pos < len) { unsigned int block_size = MIN(BLOCK_SIZE, len - pos); if(fread(block, block_size, 1, input) != 1) goto err_close_output; if(fwrite(block, block_size, 1, output) != 1) goto err_close_output; pos += block_size; } ret = true; err_close_output: fclose(output); err_close_input: fclose(input); err_out: return ret; }
static void delete_hook(const char *file) { struct manifest_entry *new_entry; struct SHA256_ctx ctx; bool ret; FILE *f; new_entry = ccalloc(1, sizeof(struct manifest_entry)); if(!new_entry) goto err_out; if(delete_p) { delete_p->next = new_entry; delete_p = delete_p->next; } else delete_list = delete_p = new_entry; delete_p->name = cmalloc(strlen(file) + 1); if(!delete_p->name) goto err_delete_p; strcpy(delete_p->name, file); f = fopen_unsafe(file, "rb"); if(!f) goto err_delete_p_name; delete_p->size = (unsigned long)ftell_and_rewind(f); ret = manifest_compute_sha256(&ctx, f, delete_p->size); fclose(f); if(!ret) goto err_delete_p_name; memcpy(delete_p->sha256, ctx.H, sizeof(Uint32) * 8); return; err_delete_p_name: free(delete_p->name); err_delete_p: free(delete_p); delete_p = NULL; err_out: return; }
struct audio_stream *construct_mikmod_stream(char *filename, Uint32 frequency, Uint32 volume, Uint32 repeat) { FILE *input_file; char *input_buffer; Uint32 file_size; struct audio_stream *ret_val = NULL; input_file = fopen_unsafe(filename, "rb"); if(input_file) { MODULE *open_file; file_size = ftell_and_rewind(input_file); input_buffer = cmalloc(file_size); fread(input_buffer, file_size, 1, input_file); open_file = MikMod_LoadSongRW(SDL_RWFromMem(input_buffer, file_size), 64); if(open_file) { struct mikmod_stream *mm_stream = cmalloc(sizeof(struct mikmod_stream)); mm_stream->module_data = open_file; Player_Start(mm_stream->module_data); initialize_sampled_stream((struct sampled_stream *)mm_stream, mm_set_frequency, mm_get_frequency, frequency, 2, 0); ret_val = (struct audio_stream *)mm_stream; construct_audio_stream((struct audio_stream *)mm_stream, mm_mix_data, mm_set_volume, mm_set_repeat, mm_set_order, mm_set_position, mm_get_order, mm_get_position, mm_destruct, volume, repeat); } fclose(input_file); free(input_buffer); } return ret_val; }
bool manifest_entry_check_validity(struct manifest_entry *e, FILE *f) { unsigned long len = e->size; struct SHA256_ctx ctx; // It must be the same length if((unsigned long)ftell_and_rewind(f) != len) return false; /* Compute the SHA256 digest for this file. Do it block-wise so as to * conserve RAM and scale to enormously large files. */ if(!manifest_compute_sha256(&ctx, f, len)) return false; // Verify the digest against the manifest if(memcmp(ctx.H, e->sha256, sizeof(Uint32) * 8) != 0) return false; return true; }
enum host_status host_send_file(struct host *h, FILE *file, const char *mime_type) { boolean mid_deflate = false; char line[LINE_BUF_LEN]; uint32_t crc, uSize; z_stream stream; long size; // Tell the client that we're going to use HTTP/1.1 features if(http_send_line(h, "HTTP/1.1 200 OK") < 0) return -HOST_SEND_FAILED; /* To bring ourselves into complete HTTP 1.1 compliance, send * some headers that we know our client doesn't actually need. */ if(http_send_line(h, "Accept-Ranges: bytes") < 0) return -HOST_SEND_FAILED; if(http_send_line(h, "Vary: Accept-Encoding") < 0) return -HOST_SEND_FAILED; // Always zlib deflate content; keeps code simple if(http_send_line(h, "Content-Encoding: gzip") < 0) return -HOST_SEND_FAILED; // We'll just send everything chunked, unconditionally if(http_send_line(h, "Transfer-Encoding: chunked") < 0) return -HOST_SEND_FAILED; // Pass along a type hint for the client (mandatory sanity check for MZX) snprintf(line, LINE_BUF_LEN, "Content-Type: %s", mime_type); line[LINE_BUF_LEN - 1] = 0; if(http_send_line(h, line) < 0) return -HOST_SEND_FAILED; // Terminate the headers with a blank line if(http_send_line(h, "") < 0) return -HOST_SEND_FAILED; // Initialize CRC for GZIP footer crc = crc32(0L, Z_NULL, 0); // Record uncompressed size for GZIP footer size = ftell_and_rewind(file); uSize = (uint32_t)size; if(size < 0) return -HOST_FREAD_FAILED; while(true) { int deflate_offset = 0, ret = Z_OK, deflate_flag = Z_SYNC_FLUSH; char block[BLOCK_SIZE], zblock[BLOCK_SIZE]; size_t block_size; /* Read a block from the disk source and compute a CRC32 on the * fly. This CRC will be dumped at the end of the deflated data * and is required for RFC 1952 compliancy. */ block_size = fread(block, 1, BLOCK_SIZE, file); crc = crc32(crc, (Bytef *)block, block_size); /* The fread() above pretty much guarantees that block_size will * be BLOCK_SIZE up to the final block. However, fread() also * returns a short count if there was an I/O error, and we must * detect this. If it's legitimately the final block, give the * compressor this information. */ if(block_size != BLOCK_SIZE) { if(!feof(file)) return -HOST_FREAD_FAILED; deflate_flag = Z_FINISH; } /* We exhausted input at a block boundary. This is unlikely, * but in the event that it happens we can simply ignore * the deflate stage and write out the terminal chunk signature. */ if(block_size == 0) break; /* Regardless of whether we are initializing the compressor in * the next section or not, we always have BLOCK_SIZE aligned * input data available. */ stream.avail_in = block_size; stream.next_in = (Bytef *)block; if(!mid_deflate) { deflate_offset = zlib_forge_gzip_header(zblock); stream.avail_out = BLOCK_SIZE - (unsigned long)deflate_offset; stream.next_out = (Bytef *)&zblock[deflate_offset]; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; ret = deflateInit2(&stream, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); if(ret != Z_OK) return -HOST_ZLIB_DEFLATE_FAILED; mid_deflate = true; } else { stream.avail_out = BLOCK_SIZE; stream.next_out = (Bytef *)zblock; } while(true) { unsigned long chunk_size; // Deflate a chunk of the input (partially or fully) ret = deflate(&stream, deflate_flag); if(ret != Z_OK && ret != Z_STREAM_END) return -HOST_ZLIB_INFLATE_FAILED; // Compute chunk length (final chunk includes GZIP footer) chunk_size = BLOCK_SIZE - stream.avail_out; if(ret == Z_STREAM_END) chunk_size += 2 * sizeof(uint32_t); // Dump compressed chunk length snprintf(line, LINE_BUF_LEN, "%lx", chunk_size); if(http_send_line(h, line) < 0) return -HOST_SEND_FAILED; // Send the compressed output block over the socket if(!__send(h, zblock, BLOCK_SIZE - stream.avail_out)) return -HOST_SEND_FAILED; /* We might not have finished the entire stream, but the * available input is likely to have been exhausted. With * Z_SYNC_FLUSH this will commonly result in zero bytes * remaining in the input source. Additionally, if this is * the final chunk (and Z_FINISH was flagged), Z_STREAM_END * will be set. In either case, we must break out. */ if((ret == Z_OK && stream.avail_in == 0) || ret == Z_STREAM_END) break; // Output has been flushed; start over for the remaining input (if any) stream.avail_out = BLOCK_SIZE; stream.next_out = (Bytef *)zblock; } /* Z_FINISH was flagged, stream ended * Terminate compression */ if(ret == Z_STREAM_END) { // Free any zlib allocated resources deflateEnd(&stream); // Write out GZIP `CRC32' footer if(!__send(h, &crc, sizeof(uint32_t))) return -HOST_SEND_FAILED; // Write out GZIP `ISIZE' footer if(!__send(h, &uSize, sizeof(uint32_t))) return -HOST_SEND_FAILED; mid_deflate = false; } // Newline after chunk's data if(http_send_line(h, "") < 0) return -HOST_SEND_FAILED; // Final block; can break out if(block_size != BLOCK_SIZE) break; } // Terminal chunk signature, so called "trailer" if(http_send_line(h, "0") < 0) return -HOST_SEND_FAILED; // Post-trailer newline if(http_send_line(h, "") < 0) return -HOST_SEND_FAILED; return HOST_SUCCESS; }
static void decrypt(const char *file_name) { FILE *source; FILE *dest; int file_length; int pro_method; int i; int len; char num_boards; char offset_low_byte; char xor_val; char password[15]; char *file_buffer; char *src_ptr; int meter_target, meter_curr = 0; source = fopen(file_name, "rb"); file_length = ftell_and_rewind(source); meter_target = file_length + (file_length - 15) + 4; meter_initial_draw(meter_curr, meter_target, "Decrypting..."); file_buffer = cmalloc(file_length); src_ptr = file_buffer; fread(file_buffer, file_length, 1, source); fclose(source); meter_curr = file_length - 1; meter_update_screen(&meter_curr, meter_target); src_ptr += 25; dest = fopen(file_name, "wb"); if(!dest) { error("Cannot decrypt write-protected world.", 1, 8, 0x0DD5); return; } pro_method = *src_ptr; src_ptr++; // Get password memcpy(password, src_ptr, 15); src_ptr += 18; // First, normalize password... for(i = 0; i < 15; i++) { password[i] ^= magic_code[i]; password[i] -= 0x12 + pro_method; password[i] ^= 0x8D; } // Xor code xor_val = get_pw_xor_code(password, pro_method); // Copy title fwrite(file_buffer, 25, 1, dest); fputc(0, dest); fputs("M\x02\x11", dest); meter_curr += 25 + 1 + 3 - 1; meter_update_screen(&meter_curr, meter_target); len = file_length - 44; for(; len > 0; len--) { fputc((*src_ptr) ^ xor_val, dest); src_ptr++; if((len % 1000) == 0) { meter_curr += 999; meter_update_screen(&meter_curr, meter_target); } } meter_curr = file_length + (file_length - 15) - 1; meter_update_screen(&meter_curr, meter_target); // Must fix all the absolute file positions so that they're 15 // less now src_ptr = file_buffer + 4245; fseek(dest, 4230, SEEK_SET); offset_low_byte = src_ptr[0] ^ xor_val; fputc(offset_low_byte - 15, dest); if(offset_low_byte < 15) { fputc((src_ptr[1] ^ xor_val) - 1, dest); } else { fputc(src_ptr[1] ^ xor_val, dest); } fputc(src_ptr[2] ^ xor_val, dest); fputc(src_ptr[3] ^ xor_val, dest); meter_curr += 4 - 1; meter_update_screen(&meter_curr, meter_target); src_ptr += 4; num_boards = ((*src_ptr) ^ xor_val); src_ptr++; // If custom SFX is there, run through and skip it if(!num_boards) { int sfx_length = (char)(src_ptr[0] ^ xor_val); sfx_length |= ((char)(src_ptr[1] ^ xor_val)) << 8; src_ptr += sfx_length + 2; num_boards = (*src_ptr) ^ xor_val; src_ptr++; } meter_target += num_boards * 4; meter_curr--; meter_update_screen(&meter_curr, meter_target); // Skip titles src_ptr += (25 * num_boards); // Synchronize source and dest positions fseek(dest, (long)(src_ptr - file_buffer - 15), SEEK_SET); // Offset boards for(i = 0; i < num_boards; i++) { // Skip length src_ptr += 4; fseek(dest, 4, SEEK_CUR); // Get offset offset_low_byte = src_ptr[0] ^ xor_val; fputc(offset_low_byte - 15, dest); if(offset_low_byte < 15) { fputc((src_ptr[1] ^ xor_val) - 1, dest); } else { fputc(src_ptr[1] ^ xor_val, dest); } fputc(src_ptr[2] ^ xor_val, dest); fputc(src_ptr[3] ^ xor_val, dest); src_ptr += 4; meter_target += 4 - 1; meter_update_screen(&meter_curr, meter_target); } free(file_buffer); fclose(dest); meter_restore_screen(); }