137 lines
3.1 KiB
C
137 lines
3.1 KiB
C
#include "io.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef __unix__
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
static const char* outfile_name;
|
|
static FILE* infile;
|
|
static FILE* outfile;
|
|
|
|
void open_files(const char* infile_name, const char* outfile_name_) {
|
|
outfile_name = outfile_name_;
|
|
infile = fopen(infile_name, "rb");
|
|
if (infile == NULL) {
|
|
fprintf(stderr, "failed to open source file: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
outfile = fopen(outfile_name, "wb");
|
|
if (outfile == NULL) {
|
|
fprintf(stderr, "failed to open output file: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void close_files(void) {
|
|
fclose(infile);
|
|
if (fclose(outfile) != 0) {
|
|
fprintf(stderr, "failed to close output file: %s\n", strerror(errno));
|
|
// NOTE: ideally we'd do this on any dirty exit
|
|
if (remove(outfile_name) != 0) {
|
|
fprintf(stderr, "failed to remove output file; if it exists, it is corrupt: %s\n", strerror(errno));
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef __unix__
|
|
chmod(outfile_name, 0777);
|
|
#endif
|
|
}
|
|
|
|
void reserve(size_t len) {
|
|
if (fseek(outfile, len, SEEK_CUR) != 0) {
|
|
fprintf(stderr, "failed to reserve space in in output file: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void emit(const void* restrict ptr, size_t count) {
|
|
fwrite(ptr, 1, count, outfile);
|
|
if (ferror(outfile)) {
|
|
fprintf(stderr, "failed to write to output file\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void emit_u8(uint8_t x) {
|
|
emit(&x, sizeof(uint8_t));
|
|
}
|
|
|
|
void emit_u32(uint32_t x) {
|
|
emit(&x, sizeof(uint32_t));
|
|
}
|
|
|
|
void emit_u64(uint64_t x) {
|
|
emit(&x, sizeof(uint64_t));
|
|
}
|
|
|
|
void patch(size_t off, const void* ptr, size_t count) {
|
|
fpos_t save;
|
|
if (fgetpos(outfile, &save) != 0) {
|
|
fprintf(stderr, "failed to save file position before patch: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
if (fseek(outfile, (long) off, SEEK_SET) != 0) {
|
|
fprintf(stderr, "failed to set file position for patch: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
fwrite(ptr, 1, count, outfile);
|
|
if (ferror(outfile) != 0) {
|
|
fprintf(stderr, "failed to patch output file: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
if (fsetpos(outfile, &save) != 0) {
|
|
fprintf(stderr, "failed to restore file position after patch: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void patch_u32(size_t off, uint32_t x) {
|
|
patch(off, &x, sizeof(uint32_t));
|
|
}
|
|
|
|
void patch_i32(size_t off, int32_t x) {
|
|
patch_u32(off, (uint32_t) x);
|
|
}
|
|
|
|
static _Bool init = false;
|
|
static char peek_buf;
|
|
|
|
static char next_(void) {
|
|
char c = getc(infile);
|
|
if (c == EOF) {
|
|
if (ferror(infile)) {
|
|
fprintf(stderr, "failed to read source file: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
c = 0;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
char nextc(void) {
|
|
if (!init) {
|
|
init = true;
|
|
peek_buf = next_();
|
|
}
|
|
int tmp = peek_buf;
|
|
peek_buf = next_();
|
|
return peek_buf;
|
|
}
|
|
|
|
char peekc(void) {
|
|
if (!init) {
|
|
init = true;
|
|
return nextc();
|
|
}
|
|
return peek_buf;
|
|
}
|