diff options
Diffstat (limited to 'tools/create_kyradat/create_kyradat.cpp')
-rw-r--r-- | tools/create_kyradat/create_kyradat.cpp | 258 |
1 files changed, 141 insertions, 117 deletions
diff --git a/tools/create_kyradat/create_kyradat.cpp b/tools/create_kyradat/create_kyradat.cpp index 3ed69e2218..21d23378e4 100644 --- a/tools/create_kyradat/create_kyradat.cpp +++ b/tools/create_kyradat/create_kyradat.cpp @@ -39,6 +39,7 @@ #include <string> #include <map> #include <algorithm> +#include <map> enum { kKyraDatVersion = 65 @@ -1142,39 +1143,27 @@ const char *getIdString(const int id) { } } -struct DataIdEntry { - DataIdEntry() : extractInfo(), id(-1) {} - DataIdEntry(ExtractEntrySearchData e, int i) : extractInfo(e), id(i) {} - - ExtractEntrySearchData extractInfo; - int id; -}; - struct ExtractData { - ExtractData() : extractInfo(), result() {} - ExtractData(ExtractEntrySearchData e, Search::ResultData r) : extractInfo(e), result(r) {} + ExtractData() : desc(), offset() {} + ExtractData(ExtractEntrySearchData d, uint32 o) : desc(d), offset(o) {} - ExtractEntrySearchData extractInfo; - Search::ResultData result; + ExtractEntrySearchData desc; + uint32 offset; }; -typedef std::list<DataIdEntry> DataIdList; +typedef std::pair<int, ExtractEntrySearchData> SearchMapEntry; +typedef std::multimap<int, ExtractEntrySearchData> SearchMap; -typedef std::map<int, Search::ResultData> IdMap; +typedef std::pair<int, ExtractData> ExtractMapEntry; +typedef std::multimap<int, ExtractData> ExtractMap; -bool getExtractionData(const Game *g, Search &search, IdMap &map); +bool getExtractionData(const Game *g, Search &search, ExtractMap &map); bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size) { char filename[128]; - ExtractInformation extractInfo; - extractInfo.game = g->game; - extractInfo.lang = g->lang; - extractInfo.platform = g->platform; - extractInfo.special = g->special; - Search search(data, size); - IdMap ids; + ExtractMap ids; if (!getExtractionData(g, search, ids)) return false; @@ -1185,47 +1174,32 @@ bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size) { return false; } - bool breakProcess = false; + ExtractInformation extractInfo; + extractInfo.game = g->game; + extractInfo.platform = g->platform; + extractInfo.special = g->special; - // Compare against need list - for (const int *entry = needList; *entry != -1; ++entry) { - if (ids.find(*entry) != ids.end()) - continue; + for (ExtractMap::const_iterator i = ids.begin(); i != ids.end(); ++i) { + const int id = i->first; - // Try whether the data is present in the kyra.dat file already - filename[0] = 0; - if (!getFilename(filename, &extractInfo, *entry)) - error("couldn't find filename for id %d", *entry); + const ExtractFilename *fDesc = getFilenameDesc(id); - PAKFile::cFileList *list = out.getFileList(); - // If the data wasn't found already, we need to break the extraction here - if (!list || !list->findEntry(filename)) { - fprintf(stderr, "Couldn't find id %d/%s in executable file\n", *entry, getIdString(*entry)); - breakProcess = true; - } else { - warning("Id %d/%s is present in kyra.dat but could not be found in the executable", *entry, getIdString(*entry)); + if (!fDesc) { + fprintf(stderr, "ERROR: couldn't find file description for id %d\n", id); + return false; } - } - if (breakProcess) - return false; + if (isLangSpecific(fDesc->type)) + extractInfo.lang = i->second.desc.lang; + else + extractInfo.lang = UNK_LANG; - for (IdMap::const_iterator i = ids.begin(); i != ids.end(); ++i) { - const int id = i->first; - filename[0] = 0; if (!getFilename(filename, &extractInfo, id)) { fprintf(stderr, "ERROR: couldn't get filename for id %d\n", id); return false; } - const ExtractFilename *fDesc = getFilenameDesc(id); - - if (!fDesc) { - fprintf(stderr, "ERROR: couldn't find file description for id %d\n", id); - return false; - } - const ExtractType *tDesc = findExtractType(fDesc->type); if (!tDesc) { @@ -1237,21 +1211,27 @@ bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size) { if (list && list->findEntry(filename) != 0) continue; - if (!tDesc->extract(out, &extractInfo, data + i->second.offset, i->second.data.size, filename, id)) { + if (!tDesc->extract(out, &extractInfo, data + i->second.offset, i->second.desc.hint.size, filename, id)) { fprintf(stderr, "ERROR: couldn't extract id %d\n", id); return false; } } - if (!updateIndex(out, &extractInfo)) { - error("couldn't update INDEX file, stop processing of all files"); - return false; + for (int i = 0; i < 3; ++i) { + if (g->lang[i] == -1) + continue; + + extractInfo.lang = g->lang[i]; + if (!updateIndex(out, &extractInfo)) { + error("couldn't update INDEX file, stop processing of all files"); + return false; + } } return true; } -bool setupSearch(const int *needList, Search &search, DataIdList &dataIdList) { +bool setupSearch(const Game *g, const int *needList, Search &search, SearchMap &searchData) { for (const int *entry = needList; *entry != -1; ++entry) { ExtractEntryList providers = getProvidersForId(*entry); @@ -1260,9 +1240,9 @@ bool setupSearch(const int *needList, Search &search, DataIdList &dataIdList) { return false; } else { for (ExtractEntryList::const_iterator i = providers.begin(); i != providers.end(); ++i) { - // We will add *all* providers here, regardless of the language and platform! + // We'll add all providers here... search.addData(i->hint); - dataIdList.push_back(DataIdEntry(*i, *entry)); + searchData.insert(SearchMapEntry(*entry, *i)); } } } @@ -1270,8 +1250,69 @@ bool setupSearch(const int *needList, Search &search, DataIdList &dataIdList) { return true; } -bool getExtractionData(const Game *g, Search &search, IdMap &ids) { - DataIdList dataIdList; +typedef std::list<ExtractMap::const_iterator> MatchList; +MatchList filterPlatformMatches(const Game *g, std::pair<ExtractMap::const_iterator, ExtractMap::const_iterator> range) { + bool hasPlatformMatch = false; + for (ExtractMap::const_iterator i = range.first; i != range.second; ++i) { + if (i->second.desc.platform == g->platform) { + hasPlatformMatch = true; + break; + } + } + + MatchList result; + if (hasPlatformMatch) { + for (ExtractMap::const_iterator i = range.first; i != range.second; ++i) { + if (i->second.desc.platform == g->platform) + result.push_back(i); + } + } else { + for (ExtractMap::const_iterator i = range.first; i != range.second; ++i) + result.push_back(i); + } + + return result; +} + +MatchList filterLanguageMatches(const int lang, const MatchList &input) { + std::list<ExtractMap::const_iterator> result; + + for (MatchList::const_iterator i = input.begin(); i != input.end(); ++i) { + if ((*i)->second.desc.lang == lang) + result.push_back(*i); + } + + return result; +} + +MatchList::const_iterator filterOutBestMatch(const MatchList &input) { + MatchList::const_iterator result = input.begin(); + + if (input.size() > 1) + warning("Multiple entries found for id %d/%s", (*result)->first, getIdString((*result)->first)); + + for (MatchList::const_iterator i = input.begin(); i != input.end(); ++i) { + // Reduce all entries to one single entry. + // + // We use the following rules for this (in this order): + // - Prefer the entry with the higest size + // - Prefer the entry, which starts at the smallest offest + // + // TODO: These rules might not be safe for all games, but hopefully + // they will work fine. If there are any problems it should be rather + // easy to identify them, since we print out a warning for multiple + // entries found. + if ((*result)->second.desc.hint.size <= (*i)->second.desc.hint.size) { + if ((*result)->second.offset >= (*i)->second.offset) + result = i; + } + } + + return result; +} + +bool getExtractionData(const Game *g, Search &search, ExtractMap &map) { + SearchMap searchMap; const int *needList = getNeedList(g); if (!needList) { @@ -1279,7 +1320,7 @@ bool getExtractionData(const Game *g, Search &search, IdMap &ids) { return false; } - if (!setupSearch(needList, search, dataIdList)) + if (!setupSearch(g, needList, search, searchMap)) return false; // Process the data search @@ -1291,78 +1332,61 @@ bool getExtractionData(const Game *g, Search &search, IdMap &ids) { return false; } + ExtractMap temporaryExtractMap; for (const int *entry = needList; *entry != -1; ++entry) { - typedef std::list<ExtractData> ExtractList; - ExtractList idResults; + typedef std::pair<SearchMap::const_iterator, SearchMap::const_iterator> KeyRange; + KeyRange idRange = searchMap.equal_range(*entry); - // Fill in all id entries found for (Search::ResultList::const_iterator i = results.begin(); i != results.end(); ++i) { - for (DataIdList::const_iterator j = dataIdList.begin(); j != dataIdList.end(); ++j) { - if (j->id == *entry && i->data == j->extractInfo.hint) - idResults.push_back(ExtractData(j->extractInfo, *i)); + for (SearchMap::const_iterator j = idRange.first; j != idRange.second; ++j) { + if (j->second.hint == i->data) + temporaryExtractMap.insert(ExtractMapEntry(*entry, ExtractData(j->second, i->offset))); } } + } - // Sort out entries with mistmatching language settings - for (ExtractList::iterator i = idResults.begin(); i != idResults.end();) { - if (i->extractInfo.lang != UNK_LANG && i->extractInfo.lang != g->lang) { - i = idResults.erase(i); - continue; - } - - ++i; - } - - // Determin whether we have a 100% platform match - bool hasPlatformMatch = false; - for (ExtractList::const_iterator i = idResults.begin(); i != idResults.end(); ++i) { - if (i->extractInfo.platform == g->platform) - hasPlatformMatch = true; - } + // Free up some memory + results.clear(); + searchMap.clear(); - for (ExtractList::iterator i = idResults.begin(); i != idResults.end();) { - // Sort out entries with mistmatching platform settings, when we have a platform match - if (hasPlatformMatch && i->extractInfo.platform != g->platform) { - i = idResults.erase(i); - continue; - } + for (const int *entry = needList; *entry != -1; ++entry) { + MatchList possibleMatches = filterPlatformMatches(g, temporaryExtractMap.equal_range(*entry)); - ++i; - } + // This means, no matching entries were found, this is in general not a good sign, we might + // think of nicer handling of this in the future. TODO: Error out? + if (possibleMatches.empty()) + continue; - if (idResults.empty()) + const ExtractFilename *fDesc = getFilenameDesc(*entry); + if (!fDesc) continue; - if (idResults.size() > 1) - warning("Multiple entries found for id %d/%s", *entry, getIdString(*entry)); + if (isLangSpecific(fDesc->type)) { + for (int i = 0; i < 3; ++i) { + if (g->lang[i] == -1) + continue; - Search::ResultData result; - result.data.size = 0; - result.offset = 0xFFFFFFFF; + MatchList langMatches = filterLanguageMatches(g->lang[i], possibleMatches); + if (langMatches.empty()) + printf("foo %s %d\n", getIdString(*entry), g->lang[i]); + MatchList::const_iterator bestMatch = filterOutBestMatch(langMatches); - // Reduce all entries to one single entry. - // - // We use the following rules for this (in this order): - // - Prefer the entry with the higest size - // - Prefer the entry, which starts at the smallest offest - // - // TODO: These rules might not be safe for all games, but hopefully - // they will work fine. If there are any problems it should be rather - // easy to identify them, since we print out a warning for multiple - // entries found. - for (ExtractList::iterator i = idResults.begin(); i != idResults.end(); ++i) { - if (result.data.size <= i->result.data.size) { - if (result.offset >= i->result.offset) - result = i->result; + // TODO: Error out. This case means an entry was not found for a required language. + if (bestMatch == langMatches.end()) + continue; + + map.insert(**bestMatch); } - } + } else { + MatchList::const_iterator bestMatch = filterOutBestMatch(possibleMatches); - ids[*entry] = result; - } + // TODO: Error out. This case means an entry was not found. + if (bestMatch == possibleMatches.end()) + continue; - // Free up some memory - results.clear(); - dataIdList.clear(); + map.insert(**bestMatch); + } + } return true; } |