]> git.sesse.net Git - s573compress/commitdiff
Rework file placement in the .dat files; should be fewer special cases now.
authorSteinar H. Gunderson <steinar+ddr@gunderson.no>
Sun, 1 Dec 2019 20:22:54 +0000 (21:22 +0100)
committerSteinar H. Gunderson <steinar+ddr@gunderson.no>
Sun, 1 Dec 2019 20:22:54 +0000 (21:22 +0100)
build_dat.cpp

index 8bcacc38d85ee8dd1e054ed98059b471f15f5dff..8a61b1be7b925b4ad4e0542c4631b2894ef9370e 100644 (file)
@@ -27,8 +27,14 @@ extern string compress_konamilz77(const string &input);
 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;
 };
@@ -72,11 +78,8 @@ static size_t round_up(size_t val, size_t granularity)
        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;
 
@@ -109,12 +112,63 @@ FileEntry place_file(const fs::path &path, const fs::path &name_in_pak, bool pak
        }
 #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(),
@@ -124,12 +178,6 @@ FileEntry place_file(const fs::path &path, const fs::path &name_in_pak, bool pak
                        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;
@@ -142,31 +190,30 @@ FileEntry place_file(const fs::path &path, const fs::path &name_in_pak, bool pak
        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;
 }
 
@@ -190,10 +237,15 @@ int main(void)
                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;
        });
@@ -222,8 +274,8 @@ int main(void)
                }
        }
 
-       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;
@@ -255,13 +307,6 @@ int main(void)
        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);