-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdumprom.cpp
executable file
·241 lines (188 loc) · 6.31 KB
/
dumprom.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
dumprom - Andy Anderson 2020
Quick and dirty tool to extract files from Epson PX-8 ROM capsules (and probably PX-4, EHT-10).
Currently hard-coded for;
* M format (loaded into TPA for execution).
* Requires all files in current directory (i.e. the file-name splitting code will break if directories are specified).
To compile on linux;
g++ dumprom.cpp -o dumprom
Reference documentation;
* PX-8 OS Reference Manual - chapter 15
* EHT-10 Development Tool User's Guide - Appendix 1
*/
#include <cstdint>
#include <cassert>
#include <string>
#include <cstring>
#include <vector>
#include <fstream>
#include <iostream>
#include <streambuf>
#ifdef _MSC_VER
#define PACK_PRE __pragma (pack( push, 1))
#define PACK_POST __pragma (pack( pop ))
#define PACK_ATTRIBUTE
#else
#define PACK_PRE
#define PACK_POST
#define PACK_ATTRIBUTE __attribute__((packed))
#endif
const uint32_t RECORD_SIZE = 128;
PACK_PRE
struct RomHeader
{
uint8_t id[2]; // 0xE5, (0x37=M format, 0x50=P format)
uint8_t capacity; // 0x08=64kbits, 0x10=128kbits, 0x20=256kbits, 0x40=512kbits, 0x80=1mbits
uint8_t checksum[2];
uint8_t system_name[3];
uint8_t rom_name[14];
uint8_t dir_entries; // number of entries + 1 (then rounded up to a multiple of 4)
uint8_t v;
uint8_t version[2];
uint8_t month[2];
uint8_t day[2];
uint8_t year[2];
} PACK_ATTRIBUTE;
struct DirEntry
{
uint8_t validity; // 0x00=valid 0xE5=invalid
uint8_t file_name[8];
uint8_t file_type[3];
uint8_t logical_extent;
uint16_t zero;
uint8_t record_count; // 0 to 128. number of 128 byte records controlled by the dir entry
uint8_t allocation_map[16]; // The IDs of each 1K block used by the file
} PACK_ATTRIBUTE;
PACK_POST
static uint8_t* file_area_offset(const RomHeader* const hdr)
{
assert(hdr->dir_entries%4 == 0);
assert(hdr->dir_entries >= 0 && hdr->dir_entries <=0x20);
return ((uint8_t*)hdr) + (hdr->dir_entries * sizeof(DirEntry));
}
static DirEntry* dir_entry_offset(const RomHeader* const hdr, const uint8_t dir_entry)
{
assert(dir_entry >= 1);
assert(dir_entry <= hdr->dir_entries);
return (DirEntry*)(((uint8_t*)hdr) + dir_entry * sizeof(DirEntry));
}
static uint8_t* block_address(uint8_t* fileBase, const uint8_t blockNo)
{
assert(blockNo >=1);
return fileBase + ((blockNo-1) * 1024);
}
static void fatal(const char* msg)
{
std::cerr << msg << std::endl;
exit(-1);
}
static std::string trim(const std::string& s)
{
std::string::size_type pos = s.find_first_of(' ');
if(pos == std::string::npos)
{
return s;
}
return s.substr(0, pos);
}
static void dump_files(const uint8_t* romBase, const uint32_t romSize)
{
const RomHeader* header = (RomHeader*)romBase;
if((header->id[0] != 0xE5) && (header->id[1] != 0x37))
{
fatal("Not a valid rom file.");
}
uint8_t* fileBase = file_area_offset(header);
std::ofstream outFile;
// Enumerate files
uint8_t dirNo = 1;
std::string fileName;
std::string extension;
uint8_t extentNo = 0;
while(dirNo <= header->dir_entries)
{
DirEntry* dir = dir_entry_offset(header, dirNo);
if(dir->validity == 0x00)
{
uint32_t bytesRemaining = dir->record_count * RECORD_SIZE;
if(dir->logical_extent == 0)
{
// close current file
outFile.close();
extentNo = 0;
fileName = std::string(dir->file_name, dir->file_name+sizeof(DirEntry::file_name));
extension = std::string(dir->file_type, dir->file_type+sizeof(DirEntry::file_type));
fileName = trim(fileName);
extension = trim(extension);
// Some ROMs (i.e. the Epson Utils) have bit 0x80 set in the the file type characters.
// I think this indicates attributes such as ReadOnly etc. Mask them out to make a valid file name.
extension[0] &= 0x7f;
extension[1] &= 0x7f;
extension[2] &= 0x7f;
// open new file
outFile.open(fileName + "." + extension, std::ios::out | std::ios::binary);
if(!outFile) fatal("Could not open output file.");
// Write each block in the allocation map
for(uint8_t i=0; i<16; ++i)
{
if(dir->allocation_map[i])
{
const uint32_t chunkSize = (bytesRemaining >= 1024) ? 1024 : bytesRemaining;
outFile.write((char*)block_address(fileBase, dir->allocation_map[i]), chunkSize);
bytesRemaining -= chunkSize;
}
}
}
else
{
// TODO - Warning if file name is different
// TODO - Warning if logical_extent != extentNo+1
extentNo = dir->logical_extent;
// Write each block in the allocation map
for(uint8_t i=0; i<16; ++i)
{
if(dir->allocation_map[i])
{
const uint32_t chunkSize = (bytesRemaining >= 1024) ? 1024 : bytesRemaining;
outFile.write((char*)block_address(fileBase, dir->allocation_map[i]), chunkSize);
bytesRemaining -= chunkSize;
}
}
}
}
++dirNo;
}
// Close file
outFile.close();
}
static void usage()
{
std::cout << "Usage: dumprom <romfile>\n" << std::endl;
}
int main(int argc, char* argv[])
{
assert(sizeof(RomHeader) == 32);
assert(sizeof(DirEntry) == 32);
if(argc != 2)
{
usage();
exit(-1);
}
std::string fileName = argv[1];
std::ifstream inFile(fileName, std::ios::in | std::ios::binary);
if(!inFile)
{
fatal("failed to open input file.");
}
std::vector<uint8_t> buffer((std::istreambuf_iterator<char>(inFile)),
std::istreambuf_iterator<char>());
if(buffer.size() == 0x8000)
{
// convert physical to logical addresses
std::vector<uint8_t> temp = buffer;
memcpy(&buffer[0], &temp[0x4000], 0x4000);
memcpy(&buffer[0x4000], &temp[0], 0x4000);
}
dump_files(buffer.data(), (uint32_t)buffer.size());
return 0;
}