Skip to content

feat(dataminer): migrate generators to extract::Installation#5

Merged
th3w1zard1 merged 5 commits into
masterfrom
feat/dataminer-installation
May 30, 2026
Merged

feat(dataminer): migrate generators to extract::Installation#5
th3w1zard1 merged 5 commits into
masterfrom
feat/dataminer-installation

Conversation

@th3w1zard1
Copy link
Copy Markdown
Collaborator

Summary

  • Migrate all dataminer code generators from legacy resource containers to extract::Installation
  • Add Installation index accessors for chitin/modules/override enumeration
  • Add installation_helpers.h with chitin-only and override-then-chitin lookup scopes
  • Fix pre-existing bug: TSL nwscript.nss was read from KotOR chitin.key

Test plan

  • No remaining KeyBifResourceContainer / FolderResourceContainer usage in dataminer
  • Retail menu smoke PASS (regression gate on WASM build)
  • Linux + Windows CI build + ctest green
  • dataminer target compiles on CI runners

Replace legacy KeyBif/Folder/RIM/ERF containers in dataminer with the
Installation index API, add shared lookup helpers, and fix TSL nwscript
loading to use the TSL chitin path instead of KotOR.
Copilot AI review requested due to automatic review settings May 29, 2026 21:38
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates src/apps/dataminer generators off legacy resource containers onto extract::Installation / FileResource, adds public Installation accessors for enumerating indexed resources, and introduces shared helper utilities to keep lookup order consistent (chitin-only vs override-then-chitin). It also addresses a pre-existing issue where TSL nwscript.nss could be read from the wrong game’s chitin.

Changes:

  • Replaced KeyBifResourceContainer / FolderResourceContainer usage in dataminer generators with extract::Installation lookups and enumeration.
  • Added Installation index accessors (chitinResources(), moduleArchives(), overrideResources()) and a new installation_helpers.h.
  • Fixed the TSL nwscript.nss lookup bug by using the correct game installation context.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/apps/dataminer/routines.cpp Switches nwscript parsing to Installation chitin lookup (and fixes the prior TSL/K1 mixup).
src/apps/dataminer/models.cpp Migrates model enumeration/loading to Installation chitin index and FileResource reads.
src/apps/dataminer/installation_helpers.h Adds shared search-scope helpers and resource enumeration helpers for dataminer generators.
src/apps/dataminer/guis.cpp Migrates GUI resource enumeration and lookup to Installation (override-then-chitin for TSL).
src/apps/dataminer/gffparsers.cpp Migrates GFF ingestion to Installation chitin/modules indexing (but currently misses module ingestion due to moduleRoot semantics).
src/apps/dataminer/2daparsers.cpp Migrates 2DA enumeration/loading to Installation chitin index and FileResource reads.
src/apps/dataminer/CMakeLists.txt Adds the new helper header to the dataminer target.
include/reone/extract/installation.h Exposes public accessors for already-indexed chitin/modules/override collections.
docs/plans/2026-05-29-dataminer-installation-plan.md Adds an implementation plan/status doc for the dataminer migration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 289 to +292
try {
auto mdlData = keyBif.findResourceData(ResourceId(resId.resRef, ResType::Mdl));
auto mdxData = keyBif.findResourceData(ResourceId(resId.resRef, ResType::Mdx));
if (!mdlData || !mdxData) {
auto mdlData = resource.readData();
auto mdxLocation = installation.resource(ResourceId(resource.id().resRef(), ResType::Mdx), chitinOnlyOrder());
if (!mdxLocation) {
Comment on lines 241 to +243
}
twoDAs.push_back({resId.resRef, reader.twoDA()});
}
twoDAs.push_back({resource.id().resRef(), reader.twoDA()});
});
Comment on lines 259 to +261
}
twoDAs.push_back({resId.resRef, reader.twoDA()});
}
twoDAs.push_back({resource.id().resRef(), reader.twoDA()});
});
Comment on lines +301 to 318
Installation installation(GameID::TSL, k2dir);
installation.loadChitin();
installation.loadModules();

auto ingestGff = [&](const FileResource &resource) {
if (resource.id().type != resType) {
return;
}
auto bytes = keyBif.findResourceData(resId);
auto stream = MemoryInputStream(*bytes);
auto bytes = resource.readData();
auto stream = MemoryInputStream(bytes);
auto reader = GffReader(stream);
reader.load();
trees[resId.resRef.value()] = reader.root();
}
trees[resource.id().resRef.value()] = reader.root();
};

auto modulesPath = getFileIgnoreCase(k2dir, "modules");
for (auto &entry : std::filesystem::directory_iterator(modulesPath)) {
if (!std::filesystem::is_regular_file(entry)) {
continue;
}
auto extension = boost::to_lower_copy(entry.path().extension().string());
if (extension == ".rim") {
auto rim = RimResourceContainer(entry.path());
rim.init();
for (auto &res : rim.resourceIds()) {
if (res.type != resType) {
continue;
}
auto bytes = rim.findResourceData(res);
auto stream = MemoryInputStream(*bytes);
auto reader = GffReader(stream);
reader.load();
trees[res.resRef.value()] = reader.root();
}
} else if (extension == ".erf") {
auto erf = ErfResourceContainer(entry.path());
erf.init();
for (auto &res : erf.resourceIds()) {
if (res.type != resType) {
continue;
}
auto bytes = erf.findResourceData(res);
auto stream = MemoryInputStream(*bytes);
auto reader = GffReader(stream);
reader.load();
trees[res.resRef.value()] = reader.root();
}
}
}
forEachResource(installation.chitinResources(), resType, ingestGff);
forEachResource(installation.moduleArchives(), resType, ingestGff);

Comment on lines 289 to +292
try {
auto mdlData = keyBif.findResourceData(ResourceId(resId.resRef, ResType::Mdl));
auto mdxData = keyBif.findResourceData(ResourceId(resId.resRef, ResType::Mdx));
if (!mdlData || !mdxData) {
auto mdlData = resource.readData();
auto mdxLocation = installation.resource(ResourceId(resource.id().resRef(), ResType::Mdx), chitinOnlyOrder());
if (!mdxLocation) {
Comment on lines 241 to +243
}
twoDAs.push_back({resId.resRef, reader.twoDA()});
}
twoDAs.push_back({resource.id().resRef(), reader.twoDA()});
});
Comment on lines 259 to +261
}
twoDAs.push_back({resId.resRef, reader.twoDA()});
}
twoDAs.push_back({resource.id().resRef(), reader.twoDA()});
});
Comment on lines +301 to 318
Installation installation(GameID::TSL, k2dir);
installation.loadChitin();
installation.loadModules();

auto ingestGff = [&](const FileResource &resource) {
if (resource.id().type != resType) {
return;
}
auto bytes = keyBif.findResourceData(resId);
auto stream = MemoryInputStream(*bytes);
auto bytes = resource.readData();
auto stream = MemoryInputStream(bytes);
auto reader = GffReader(stream);
reader.load();
trees[resId.resRef.value()] = reader.root();
}
trees[resource.id().resRef.value()] = reader.root();
};

auto modulesPath = getFileIgnoreCase(k2dir, "modules");
for (auto &entry : std::filesystem::directory_iterator(modulesPath)) {
if (!std::filesystem::is_regular_file(entry)) {
continue;
}
auto extension = boost::to_lower_copy(entry.path().extension().string());
if (extension == ".rim") {
auto rim = RimResourceContainer(entry.path());
rim.init();
for (auto &res : rim.resourceIds()) {
if (res.type != resType) {
continue;
}
auto bytes = rim.findResourceData(res);
auto stream = MemoryInputStream(*bytes);
auto reader = GffReader(stream);
reader.load();
trees[res.resRef.value()] = reader.root();
}
} else if (extension == ".erf") {
auto erf = ErfResourceContainer(entry.path());
erf.init();
for (auto &res : erf.resourceIds()) {
if (res.type != resType) {
continue;
}
auto bytes = erf.findResourceData(res);
auto stream = MemoryInputStream(*bytes);
auto reader = GffReader(stream);
reader.load();
trees[res.resRef.value()] = reader.root();
}
}
}
forEachResource(installation.chitinResources(), resType, ingestGff);
forEachResource(installation.moduleArchives(), resType, ingestGff);

ResRef is a struct member on ResourceId, not a callable; fixes dataminer
build on Linux and Windows CI.
Record stuck wasm-ci recovery and merge-ready state for PR #5.
Record self-hosted runner recovery and pending wasm-ci after 45m queue stall.
@th3w1zard1 th3w1zard1 merged commit e8e4b67 into master May 30, 2026
8 checks passed
@th3w1zard1 th3w1zard1 deleted the feat/dataminer-installation branch May 30, 2026 02:34
th3w1zard1 added a commit that referenced this pull request May 30, 2026
PR #5 merged; record runner recovery and full CI green on e8e4b67.
th3w1zard1 added a commit that referenced this pull request May 30, 2026
Replace pre-merge pass 19 draft with final landed state after PR #5 merge.
th3w1zard1 added a commit that referenced this pull request May 30, 2026
Update web progress PR status after #4/#5 merge; record pass 22 smoke gate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants