struct Pakfile {
explicit Pakfile(size_t size) : size(size), data(new char[size]) {
memset(data.get(), 0xff, size);
+ free_spans.emplace_back(Span { 0, size });
}
+ struct Span {
+ size_t start, end;
+ };
+ vector<Span> free_spans;
+
size_t size, used = 0;
unique_ptr<char[]> data;
};
return (val + granularity - 1) & ~(granularity - 1);
}
-FileEntry place_file(const fs::path &path, const fs::path &name_in_pak, bool pak_is_card, uint32_t force_addr, Pakfile *pak)
+FileEntry place_data(string str, const fs::path &name_in_pak, bool pak_is_card, uint32_t force_addr, Pakfile *pak)
{
- ifstream file(path);
- string str{istreambuf_iterator<char>(file), istreambuf_iterator<char>()};
-
size_t original_size = str.size();
bool compressed = false, encrypted = false;
}
#endif
+ size_t rounded_size = round_up(str.size(), CHUNK_SIZE);
+
uint32_t offset;
if (force_addr == 0xffffffff) {
- offset = pak->used;
+ // Find fullest span and try to put it there; this isn't optimal,
+ // but we don't generally need to be too sophisticated.
+ Pakfile::Span *fullest_span = nullptr;
+ for (Pakfile::Span &span : pak->free_spans) {
+ size_t span_left = span.end - span.start;
+ if (rounded_size > span_left) {
+ // No room.
+ continue;
+ }
+ if (fullest_span == nullptr || span_left < fullest_span->end - fullest_span->start) {
+ fullest_span = &span;
+ }
+ }
+ if (fullest_span == nullptr) {
+ if (pak->used + rounded_size <= pak->size) {
+ fprintf(stderr, "ERROR: Free space too fragmented while trying to place %s (%zu bytes, %zu bytes free)\n",
+ name_in_pak.c_str(), str.size(), pak->size - pak->used);
+ } else {
+ fprintf(stderr, "ERROR: Out of space while trying to place %s (%zu bytes, %zu bytes free)\n",
+ name_in_pak.c_str(), str.size(), pak->size - pak->used);
+ }
+ exit(1);
+ }
+ offset = fullest_span->start;
+ fullest_span->start += rounded_size; // Could lead to zero.
} else {
+ // Find the right span, potentially splitting it.
+ Pakfile::Span *forced_span = nullptr;
+ for (Pakfile::Span &span : pak->free_spans) {
+ if (force_addr >= span.start && force_addr < span.end) {
+ forced_span = &span;
+ break;
+ }
+ }
+ if (forced_span == nullptr || rounded_size > forced_span->end - forced_span->start) {
+ fprintf(stderr, "ERROR: Tried to place %s (%zu bytes) at 0x%08x, but there was no room\n",
+ name_in_pak.c_str(), str.size(), force_addr);
+ exit(1);
+ }
+
+ if (force_addr == forced_span->start) {
+ forced_span->start += rounded_size; // Could lead to zero.
+ } else {
+ Pakfile::Span new_span { force_addr + rounded_size, forced_span->end };
+ forced_span->end = force_addr;
+ if (new_span.start != new_span.end) {
+ pak->free_spans.emplace_back(new_span); // NOTE: Invalidates the forced_span pointer.
+ }
+ }
+
offset = force_addr;
}
+
if (compressed) {
fprintf(stderr, "- %s 0x%08x: %s (%zu => %zu bytes, %.2f%% saved)\n",
pak_is_card ? "card.dat" : "game.dat", offset, name_in_pak.c_str(),
pak_is_card ? "card.dat" : "game.dat", offset, name_in_pak.c_str(), str.size());
}
- if (offset + str.size() > pak->size || pak->used > offset) {
- fprintf(stderr, "ERROR: Out of space while trying to place %s (%zu bytes)\n",
- name_in_pak.c_str(), str.size());
- exit(1);
- }
-
memcpy(pak->data.get() + offset, str.data(), str.size());
FileEntry entry;
entry.padding = 0;
entry.size = str.size();
- pak->used = offset + round_up(str.size(), CHUNK_SIZE);
+ pak->used += rounded_size;
return entry;
}
-int force_order(const fs::path &path) {
+FileEntry place_file(const fs::path &path, const fs::path &name_in_pak, bool pak_is_card, uint32_t force_addr, Pakfile *pak)
+{
+ ifstream file(path);
+ string str{istreambuf_iterator<char>(file), istreambuf_iterator<char>()};
+ return place_data(move(str), name_in_pak, pak_is_card, force_addr, pak);
+}
+
+int place_order(const fs::path &path) {
const fs::path relpath = path.lexically_relative("output");
- if (relpath == "boot/psx.bin") {
- // Boot loader comes first (naturally, and in flash).
+
+ // These need to be placed at fixed addresses:
+ if (relpath == "boot/psx.bin" || // Boot loader comes first (naturally, and in flash).
+ relpath == "boot/checksum.dat" || // Seemingly needs to live at 0xfe0000 (in flash).
+ relpath == "boot/config.dat" || // Not loaded by filename; hardcoded at 0xfe2000 (in flash).
+ relpath == SOUND_FILENAME) { // Not a file per se; hardcoded at 0x18c0000 (on the PCMCIA card).
return 0;
}
- if (relpath == "boot/checksum.dat") {
- // Seemingly needs to live at 0xfe0000 (in flash).
- return 2;
- }
- if (relpath == "boot/config.dat") {
- // Not loaded by filename; hardcoded at 0xfe2000 (in flash).
- return 3;
- }
- if (relpath == SOUND_FILENAME) {
- // Not a file per se; hardcoded at 0x18c0000 (on the PCMCIA card).
- return 4;
- }
- // Everything else lives between end of psx.bin and 0xfe0000.
+ // Everything else can be placed afterwards.
return 1;
}
paths.push_back(de.path());
}
+ // Seemingly, 0x01BE0000 to the end of the card (4 MB) is reserved for edit data.
+ // It needs to start with this header, or the bootloader fails with error 1N.
+ string edit_data_header = "D.D.R. 4thMIX TM\n----------------\n";
+ edit_data_header.resize(33554432 - EDIT_DATA_OFFSET);
+ place_data(move(edit_data_header), "(edit data)", /*put_on_card=*/true, EDIT_DATA_OFFSET, &card_dat);
sort(paths.begin(), paths.end(), [](const fs::path &a, const fs::path &b) {
- if (force_order(a) != force_order(b)) {
- return force_order(a) < force_order(b);
+ if (place_order(a) != place_order(b)) {
+ return place_order(a) < place_order(b);
}
return a < b;
});
}
}
- fprintf(stderr, "game.dat: %zu kB used\n", game_dat.used / 1024);
- fprintf(stderr, "card.dat: %zu kB used\n", card_dat.used / 1024);
+ fprintf(stderr, "game.dat: %zu kB used, %zu kB free\n", game_dat.used / 1024, game_dat.size / 1024 - game_dat.used / 1024);
+ fprintf(stderr, "card.dat: %zu kB used, %zu kB free\n", card_dat.used / 1024, card_dat.size / 1024 - card_dat.used / 1024);
sort(entries.begin(), entries.end(), [](const FileEntry &a, const FileEntry &b) {
return a.filename_hash < b.filename_hash;
memcpy(game_dat.data.get() + FILE_TABLE_OFFSET, entries.data(), file_table_size);
//memset(game_dat.data.get() + file_table_end, 0xff, game_dat.size - file_table_end);
- if (card_dat.used >= EDIT_DATA_OFFSET) {
- fprintf(stderr, "ERROR: No room in card.dat for footer!\n");
- exit(1);
- }
- const char footer[] = "D.D.R. 4thMIX TM\n----------------\n";
- memcpy(card_dat.data.get() + EDIT_DATA_OFFSET, footer, strlen(footer));
-
FILE *fp = fopen("game.dat", "wb");
fwrite(game_dat.data.get(), game_dat.size, 1, fp);
fclose(fp);