Skip to content
This repository was archived by the owner on Oct 29, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/texture-packs.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ A replacement that isn't the original's size is packed raw as RGBA32 with its sc

## aliases.json

For packs whose filenames don't match the archive's texture paths. Maps an image path relative to the folder, extension included, to one target or several. A source with no directory matches a file of that name anywhere in the folder. A leading `alt/` on a target is ignored.
For packs whose filenames don't match the archive's texture paths. Maps an image path relative to the folder, extension included, to one target or several. A source with no directory matches a file of that name anywhere in the folder, and so does a source whose folder no longer holds that file. A leading `alt/` on a target is ignored.

When more than one image supplies the same texture, the first in the order GLideN64 loads the folder is used, so the pack shows what the emulator does: depth first, each folder in name order, skipping names that start with `.` or `~!~`.

```json
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,22 +192,39 @@ Future<HashMap<String, ProcessedFilesInFolder>?> processFolder(

// find all images in folder
final supportedExtensions = <String>['.png', '.jpeg', '.jpg'];
final files = Directory(folderPath).listSync(recursive: true);
final texFiles = files
final texFiles = listInGlidenOrder(Directory(folderPath))
.where((file) =>
supportedExtensions.contains(path.extension(file.path).toLowerCase()),)
.toList();
final relativePaths = texFiles
.map((file) => p.normalize(path.relative(file.path, from: folderPath)))
.toSet();
final movedAliases = <String, List<String>>{
for (final alias in aliases.entries)
if (alias.key.contains('/') && !relativePaths.contains(alias.key))
path.basename(alias.key): alias.value,
};

// The first file to supply a texture wins
final suppliedBy = <String, String>{};

for (final rawFile in texFiles) {
final texFile = File(p.normalize(rawFile.path));
final relativePath = p.normalize(path.relative(texFile.path, from: folderPath));
final texPathRelativeToFolder = p.normalize(path.withoutExtension(relativePath));
final name = path.basename(relativePath);
// A source with no directory names a file wherever it sits in the folder.
final targets = aliases[relativePath] ??
aliases[path.basename(relativePath)] ??
aliases[name] ??
movedAliases[name] ??
[texPathRelativeToFolder];

for (final target in targets) {
final earlier = suppliedBy[target];
if (earlier != null) {
log('Skipping $relativePath for $target: $earlier comes first');
continue;
}
var textureName = target;
int? stripIndex;
var manifestEntry = resolveTextureEntry(manifest, target);
Expand Down Expand Up @@ -253,6 +270,7 @@ Future<HashMap<String, ProcessedFilesInFolder>?> processFolder(
log('Staging additive texture: $textureName');
}

suppliedBy[target] = relativePath;
manifestEntry.targetName = textureName;
final pathWithoutFilename = path.dirname(textureName);

Expand All @@ -267,6 +285,27 @@ Future<HashMap<String, ProcessedFilesInFolder>?> processFolder(
return processedFiles;
}

List<File> listInGlidenOrder(Directory dir) {
final entries = dir.listSync()
..sort((a, b) => path
.basename(a.path)
.toUpperCase()
.compareTo(path.basename(b.path).toUpperCase()),);
final files = <File>[];
for (final entry in entries) {
final name = path.basename(entry.path);
if (name.startsWith('.') || name.startsWith('~!~')) {
continue;
}
if (entry is Directory) {
files.addAll(listInGlidenOrder(entry));
} else if (entry is File) {
files.add(entry);
}
}
return files;
}

// The band a target ending in "#<n>" names, or null for anything else.
int? bandIndexOf(String target) {
final hash = target.lastIndexOf('#');
Expand Down
8 changes: 8 additions & 0 deletions lib/games/bk64/bk64_conventions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Bk64TextureConventions implements GameTextureConventions {
static final RegExp _boldFontPattern =
RegExp(r'^(.*)boldfont/(.+_\d+_\d+)_[0-9A-Fa-f]+$');
static final RegExp _langScopedPattern = RegExp(r'^(.*)lang/[^/]+/(.+)$');
static final RegExp _animFramePattern = RegExp(r'^(.+_tex_\d+)_frame_\d+$');
static final RegExp _spriteChunkPattern = RegExp(r'^(.+)_(\d+)_(\d+)$');

@override
Expand Down Expand Up @@ -47,6 +48,13 @@ class Bk64TextureConventions implements GameTextureConventions {
return _additiveFrom(template, TextureEntryKind.additive);
}
}
final frameMatch = _animFramePattern.firstMatch(target);
if (frameMatch != null) {
final template = lookup(frameMatch.group(1)!);
if (template != null) {
return _additiveFrom(template, TextureEntryKind.additive);
}
}

return null;
}
Expand Down
Loading