diff --git a/internal/erf/erf.go b/internal/erf/erf.go index 711af01..8830ac1 100644 --- a/internal/erf/erf.go +++ b/internal/erf/erf.go @@ -82,23 +82,24 @@ var extensionTypes = map[string]uint16{ "txi": 0x07E6, "git": 0x07E7, "uti": 0x07E9, - "utt": 0x07EA, "utc": 0x07EB, "dlg": 0x07ED, "itp": 0x07EE, - "uts": 0x07F0, + "utt": 0x07F0, "dds": 0x07F1, + "uts": 0x07F3, "fac": 0x07F6, "gff": 0x07F7, - "ute": 0x07F9, - "utd": 0x07FB, + "ute": 0x07F8, + "utd": 0x07FA, "utp": 0x07FC, "dfa": 0x07FD, "gic": 0x07FE, "gui": 0x07FF, - "utm": 0x0800, + "utm": 0x0803, "dwk": 0x0804, "pwk": 0x0805, + "utg": 0x0807, "jrl": 0x0808, "utw": 0x080A, "ssf": 0x080C, @@ -146,23 +147,24 @@ var typeExtensions = map[uint16]string{ 0x07E6: "txi", 0x07E7: "git", 0x07E9: "uti", - 0x07EA: "utt", 0x07EB: "utc", 0x07ED: "dlg", 0x07EE: "itp", - 0x07F0: "uts", + 0x07F0: "utt", 0x07F1: "dds", + 0x07F3: "uts", 0x07F6: "fac", 0x07F7: "gff", - 0x07F9: "ute", - 0x07FB: "utd", + 0x07F8: "ute", + 0x07FA: "utd", 0x07FC: "utp", 0x07FD: "dfa", 0x07FE: "gic", 0x07FF: "gui", - 0x0800: "utm", + 0x0803: "utm", 0x0804: "dwk", 0x0805: "pwk", + 0x0807: "utg", 0x0808: "jrl", 0x080A: "utw", 0x080C: "ssf", diff --git a/internal/erf/erf_test.go b/internal/erf/erf_test.go index 6e82b43..628c04e 100644 --- a/internal/erf/erf_test.go +++ b/internal/erf/erf_test.go @@ -97,3 +97,34 @@ func TestExtensionMappingsSupportModernAssetTypes(t *testing.T) { } } } + +func TestExtensionMappingsSupportAllUTBlueprintTypes(t *testing.T) { + cases := map[string]uint16{ + "uti": 0x07E9, + "utc": 0x07EB, + "utt": 0x07F0, + "uts": 0x07F3, + "ute": 0x07F8, + "utd": 0x07FA, + "utp": 0x07FC, + "utm": 0x0803, + "utg": 0x0807, + "utw": 0x080A, + } + for ext, wantType := range cases { + gotType, ok := ResourceTypeForExtension(ext) + if !ok { + t.Fatalf("expected %s to resolve to a resource type", ext) + } + if gotType != wantType { + t.Fatalf("expected %s type 0x%04X, got 0x%04X", ext, wantType, gotType) + } + gotExt, ok := ExtensionForResourceType(wantType) + if !ok { + t.Fatalf("expected type 0x%04X to resolve back to an extension", wantType) + } + if gotExt != ext { + t.Fatalf("expected type 0x%04X to resolve to %s, got %s", wantType, ext, gotExt) + } + } +} diff --git a/internal/pipeline/pipeline_test.go b/internal/pipeline/pipeline_test.go index 60e4675..11a67e0 100644 --- a/internal/pipeline/pipeline_test.go +++ b/internal/pipeline/pipeline_test.go @@ -178,6 +178,61 @@ func TestExtractReadsHAKAssets(t *testing.T) { } } +func TestExtractReadsSoundBlueprintResources(t *testing.T) { + root := t.TempDir() + mustMkdir(t, filepath.Join(root, "src")) + mustMkdir(t, filepath.Join(root, "assets")) + mustMkdir(t, filepath.Join(root, "build")) + + mustWriteFile(t, filepath.Join(root, "nwn-tool.json"), `{ + "module": { + "name": "Test Module", + "resref": "testmod" + }, + "paths": { + "source": "src", + "assets": "assets", + "build": "build" + } +} +`) + + p, err := project.Load(root) + if err != nil { + t.Fatalf("load project: %v", err) + } + if err := p.ValidateLayout(); err != nil { + t.Fatalf("validate layout: %v", err) + } + if err := p.Scan(); err != nil { + t.Fatalf("scan: %v", err) + } + + archiveFile, err := os.Create(filepath.Join(root, "build", "testmod.mod")) + if err != nil { + t.Fatalf("create mod: %v", err) + } + if err := erf.Write(archiveFile, erf.New("MOD ", []erf.Resource{ + {Name: "S_AL_CANDLE01", Type: 0x07F3, Data: minimalGFF(t, "UTS")}, + })); err != nil { + t.Fatalf("write mod: %v", err) + } + if err := archiveFile.Close(); err != nil { + t.Fatalf("close mod: %v", err) + } + + result, err := Extract(p) + if err != nil { + t.Fatalf("extract: %v", err) + } + if result.Written != 1 { + t.Fatalf("expected 1 extracted sound blueprint, got %d", result.Written) + } + if _, err := os.Stat(filepath.Join(root, "src", "blueprints", "sounds", "s_al_candle01.uts.json")); err != nil { + t.Fatalf("expected extracted sound blueprint: %v", err) + } +} + func TestExtractDefaultOnlyReadsConfiguredModuleArchive(t *testing.T) { root := t.TempDir() mustMkdir(t, filepath.Join(root, "src", "module")) @@ -3368,6 +3423,20 @@ func mustWriteFile(t *testing.T, path, data string) { } } +func minimalGFF(t *testing.T, fileType string) []byte { + t.Helper() + document := gff.Document{ + FileType: fileType + " ", + FileVersion: "V3.2", + Root: gff.Struct{Type: 0}, + } + var buf bytes.Buffer + if err := gff.Write(&buf, document); err != nil { + t.Fatalf("write %s gff: %v", fileType, err) + } + return buf.Bytes() +} + func TestBuildHAKsFailsForDuplicateResourcesInSameHAK(t *testing.T) { root := t.TempDir() mustMkdir(t, filepath.Join(root, "src"))