forked from grumpycoders/pcsx-redux
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathexe2iso.cc
211 lines (200 loc) · 8.01 KB
/
exe2iso.cc
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
/***************************************************************************
* Copyright (C) 2022 PCSX-Redux authors *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
#include <stdint.h>
#include "cdrom/iec-60908b.h"
#include "flags.h"
#include "fmt/format.h"
#include "iec-60908b/edcecc.h"
#include "support/file.h"
static void storeU32(uint32_t value, uint8_t* buffer) {
buffer[0] = value & 0xff;
buffer[1] = (value >> 8) & 0xff;
buffer[2] = (value >> 16) & 0xff;
buffer[3] = (value >> 24) & 0xff;
}
// make sure to call this with a sector that's memset to 0
static void getSector(uint8_t data[2048], uint32_t lba, uint32_t exeSize, uint32_t exeOffset = 19) {
switch (lba) {
case 16:
data[0] = 1;
data[1] = 'C';
data[2] = 'D';
data[3] = '0';
data[4] = '0';
data[5] = '1';
storeU32(1, data + 132);
storeU32(17, data + 140);
storeU32(18, data + 158);
break;
case 17:
data[0] = 1;
data[2] = 18;
data[6] = 1;
break;
case 18:
data[0] = 42;
storeU32(exeOffset, data + 2);
storeU32(exeSize, data + 10);
data[32] = 9;
data[33] = 'P';
data[34] = 'S';
data[35] = 'X';
data[36] = '.';
data[37] = 'E';
data[38] = 'X';
data[39] = 'E';
data[40] = ';';
data[41] = '1';
break;
}
}
// make sure to call this with a sector that's memset to 0, as it relies on zeros being
// in the right place.
static void makeHeader(uint8_t sector[2352], uint32_t lba) {
PCSX::IEC60908b::MSF time(lba + 150);
memset(sector + 1, 0xff, 10);
time.toBCD(sector + 12);
sector[15] = 2;
sector[18] = sector[22] = 8;
}
int main(int argc, char** argv) {
CommandLine::args args(argc, argv);
fmt::print(R"(
exe2iso by Nicolas "Pixel" Noble
https://github.com/grumpycoders/pcsx-redux/tree/main/tools/exe2iso/
)");
auto output = args.get<std::string>("o");
auto inputs = args.positional();
const bool asksForHelp = args.get<bool>("h").value_or(false);
const uint32_t offset = std::stoul(args.get<std::string>("offset").value_or("0"), nullptr, 0);
const bool hasOutput = output.has_value();
const bool oneInput = inputs.size() == 1;
const bool pad = args.get<bool>("pad").value_or(false);
const bool regen = args.get<bool>("regen").value_or(false);
const auto license = args.get<std::string>("license");
if (asksForHelp || !oneInput || !hasOutput) {
fmt::print(R"(
Usage: {} input.ps-exe [-offset value] [-pad] [-regen] [-license file] -o output.bin
input.ps-exe mandatory: specify the input ps-exe file.
-o output.bin mandatory: name of the output file.
-offset value optional: move the exe data by value sectors.
-pad optional: pads the iso with 150 blank sectors.
-regen optional: generates proper ECC/EDC.
-license file optional: use this license file.
-h displays this help information and exit.
)",
argv[0]);
return -1;
}
auto& input = inputs[0];
PCSX::IO<PCSX::File> file(new PCSX::PosixFile(input));
if (file->failed()) {
fmt::print("Unable to open file: {}\n", input);
return -1;
}
PCSX::IO<PCSX::File> licenseFile(new PCSX::FailedFile);
PCSX::IO<PCSX::File> out(new PCSX::PosixFile(output.value(), PCSX::FileOps::TRUNCATE));
if (out->failed()) {
fmt::print("Error opening output file {}\n", output.value());
return -1;
}
if (license.has_value()) {
licenseFile.setFile(new PCSX::PosixFile(license.value()));
}
uint32_t exeSize = file->size();
exeSize += 2047;
exeSize /= 2048;
exeSize *= 2048;
uint32_t exeOffset = 19 + offset;
uint8_t sector[2352];
bool wroteLicense = false;
// Sectors 0-15 are the license. We can keep it to zeroes and it'll work most everywhere.
if (licenseFile && !licenseFile->failed()) {
uint8_t licenseData[2352 * 16];
memset(licenseData, 0, sizeof(licenseData));
licenseFile->read(licenseData, sizeof(licenseData));
if (licenseData[0x2492] == 'L') {
// official license file from the sdk, in 2336 bytes per sector.
for (unsigned i = 0; i < 16; i++) {
memset(sector, 0, sizeof(sector));
memcpy(sector + 16, licenseData + 2336 * i, 2336);
makeHeader(sector, i);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
wroteLicense = true;
} else if (licenseData[0x24e2] == 'L') {
// looks like an iso file itself
for (unsigned i = 0; i < 16; i++) {
memcpy(sector, licenseData + 2352 * i, 2352);
makeHeader(sector, i);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
wroteLicense = true;
} else {
fmt::print("Unrecognized LICENSE file format {}\n", output.value());
}
}
if (!wroteLicense) {
for (unsigned i = 0; i < 16; i++) {
memset(sector, 0, sizeof(sector));
makeHeader(sector, i);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
}
// The actual structure of the iso. We're only generating 3 sectors,
// from 16 to 18, as it's the only things necessary for the PS1 bios.
for (unsigned i = 16; i < 19; i++) {
memset(sector, 0, sizeof(sector));
makeHeader(sector, i);
// This function will fill the sector with the right data, as
// necessary for the PS1 bios.
getSector(sector + 24, i, exeSize, exeOffset);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
// Potential padding before the start of the exe.
for (unsigned i = 19; i < exeOffset; i++) {
memset(sector, 0, sizeof(sector));
makeHeader(sector, i);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
unsigned LBA = exeOffset;
// The actual exe.
for (unsigned i = 0; i < exeSize; i += 2048) {
memset(sector, 0, sizeof(sector));
makeHeader(sector, LBA++);
file->read(sector + 24, 2048);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
if (pad) {
// 150 sectors padding.
for (unsigned i = 0; i < 150; i++) {
memset(sector, 0, sizeof(sector));
makeHeader(sector, LBA++);
if (regen) compute_edcecc(sector);
out->write(sector, sizeof(sector));
}
}
fmt::print("Done.");
}