Created
December 3, 2016 15:37
-
-
Save murachue/396283e7bb921c6e753f2f23fde6a49b to your computer and use it in GitHub Desktop.
Binary to PlayStation1 executable converter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// mkpsxexe.c | |
// Copyright 2016 Murachue | |
// License: CC-BY | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <inttypes.h> | |
#include <stdlib.h> | |
size_t fwriteu32(uint32_t val, FILE *fp) { | |
char buf[4]; | |
// little endian | |
buf[0] = (val >> 0) & 0xFF; | |
buf[1] = (val >> 8) & 0xFF; | |
buf[2] = (val >> 16) & 0xFF; | |
buf[3] = (val >> 24) & 0xFF; | |
return fwrite(buf, 4, 1, fp); | |
} | |
int main(int argc, char **argv) { | |
uint32_t load_addr = 0, entry_addr = 0, initial_sp = 0; | |
const char *outfile = NULL, *infile; | |
int opt; | |
while((opt = getopt(argc, argv, "l:e:s:o:")) != -1) { | |
switch(opt) { | |
case 'l': // load | |
load_addr = strtoul(optarg, NULL, 16); | |
break; | |
case 'e': // entry | |
entry_addr = strtoul(optarg, NULL, 16); | |
break; | |
case 's': // sp | |
initial_sp = strtoul(optarg, NULL, 16); | |
break; | |
case 'o': // output-file | |
outfile = optarg; // ah... is this ok? | |
break; | |
default: | |
fprintf(stderr, "Unknown option %c\n", opt); | |
return 1; | |
} | |
} | |
argc -= optind; | |
argv += optind; | |
infile = argv[0]; | |
if(load_addr == 0) { fprintf(stderr, "Load address (-l) not specified.\n"); return 1; } | |
if(entry_addr == 0) { fprintf(stderr, "Entry address (-e) not specified.\n"); return 1; } | |
if(initial_sp == 0) { fprintf(stderr, "Initial stack pointer (-s) not specified.\n"); return 1; } | |
if(outfile == NULL) { fprintf(stderr, "Output file (-o) not specified.\n"); return 1; } | |
if(infile == NULL) { fprintf(stderr, "Input file not specified.\n"); return 1; } | |
if(1 < argc) { fprintf(stderr, "Extra arguments.\n"); return 1; } | |
{ | |
FILE *wp, *fp; | |
uint32_t datasize, aligneddatasize; | |
if((wp = fopen(outfile, "wb")) == NULL) { | |
perror("Could not open the output file"); | |
return 1; | |
} | |
if((fp = fopen(infile, "rb")) == NULL) { | |
perror("Could not open the input file"); | |
return 1; | |
} | |
if(fseek(fp, 0, SEEK_END) != 0) { | |
perror("Could not seek to the end of the input file"); | |
return 1; | |
} | |
datasize = ftell(fp); // must be <4GB | |
aligneddatasize = (datasize + (0x800 - 1)) & ~0x7FF; // round up to 0x800 | |
if(fseek(fp, 0, SEEK_SET) != 0) { | |
perror("Could not seek to the begin of the input file"); | |
return 1; | |
} | |
if(fwrite("PS-X EXE", 8, 1, wp) < 1) { | |
perror("Could not write magic"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write zero8"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write zeroC"); | |
return 1; | |
} | |
if(fwriteu32(entry_addr, wp) < 1) { | |
perror("Could not write entry point"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write initial gp = 0"); | |
return 1; | |
} | |
if(fwriteu32(load_addr, wp) < 1) { | |
perror("Could not write load address"); | |
return 1; | |
} | |
if(fwriteu32(aligneddatasize, wp) < 1) { | |
perror("Could not write size"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write zero20"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write zero24"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write zero28"); | |
return 1; | |
} | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write zero2C"); | |
return 1; | |
} | |
if(fwriteu32(initial_sp, wp) < 1) { | |
perror("Could not write initial sp"); | |
return 1; | |
} | |
// should write the "SCEI. for * area" to 0x4C~? | |
{ | |
int i; | |
for(i = 0x34; i < 0x4C; i += 4) { | |
if(fwriteu32(0, wp) < 1) { | |
perror("Could not write header padding before licensing"); | |
return 1; | |
} | |
} | |
} | |
{ | |
const char licensing[] = "Sony Computer Entertainment Inc. for Japan area"; | |
const char nul[1] = {0}; | |
int i; | |
if(fwrite(licensing, sizeof(licensing), 1, wp) < 1) { | |
perror("Could not write licensing"); | |
return 1; | |
} | |
for(i = 0x4C + sizeof(licensing); i < 0x800; i++) { | |
if(fwrite(nul, 1, 1, wp) < 1) { | |
perror("Could not write header last padding"); | |
return 1; | |
} | |
} | |
} | |
// copy the binary | |
{ | |
char buf[2048]; | |
uint32_t copied = 0; | |
while(copied < datasize) { | |
size_t shouldread, r; | |
shouldread = ((datasize - copied) < 2048) ? (datasize - copied) : 2048; | |
if(fread(buf, shouldread, 1, fp) < 1) { | |
perror("Could not read from the input file"); | |
return 1; | |
} | |
if(fwrite(buf, shouldread, 1, wp) < 1) { | |
perror("Could not write to the output file"); | |
return 1; | |
} | |
copied += shouldread; | |
} | |
} | |
if(fclose(fp) != 0) { | |
perror("Could not close the input file (ignored)"); | |
} | |
// padding required? | |
if(datasize < aligneddatasize) | |
{ | |
char buf[2048] = {0}; | |
if(fwrite(buf, aligneddatasize - datasize, 1, wp) < 1) { | |
perror("Could not write the padding to the output file"); | |
return 1; | |
} | |
} | |
if(fclose(wp) != 0) { | |
perror("Could not close the output file (ignored)"); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment