From 92c74c7bd03ad31d76bc68c5e78ea4861cd3f74f Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 18 Mar 2026 18:53:17 +0000 Subject: [PATCH] asset walker (WIP) --- docs/packer/agendas/README.md | 2 +- ...d Metatile Runtime Binary Layout Agenda.md | 152 ++ .../prometeu-packer-v1/build.gradle.kts | 1 + .../src/main/java/p/packer/Packer.java | 9 +- .../{services => }/PackerWorkspacePaths.java | 7 +- .../java/p/packer/models/PackerFileProbe.java | 10 + .../java/p/packer/models/PackerPaletteV1.java | 6 + .../p/packer/models/PackerProbeResult.java | 10 + .../models/PackerSoundBankRequirements.java | 4 + .../models/PackerTileBankRequirements.java | 4 + .../p/packer/models/PackerTileIndexedV1.java | 7 + .../p/packer/models/PackerWalkResult.java | 16 + .../FileSystemPackerRegistryRepository.java | 16 +- .../PackerAbstractBankWalker.java | 92 ++ .../repositories/PackerAssetWalker.java | 164 +++ .../PackerRegistryRepository.java | 2 +- .../PackerRuntimeLoader.java | 34 +- .../PackerRuntimeRegistry.java | 3 +- .../PackerRuntimeSnapshotLoader.java | 2 +- .../repositories/PackerSoundBankWalker.java | 33 + .../repositories/PackerTileBankWalker.java | 231 +++ .../FileSystemPackerWorkspaceService.java | 30 +- .../PackerAssetActionReadService.java | 2 + .../PackerAssetDeclarationParser.java | 79 +- .../services/PackerAssetDetailsService.java | 2 + .../PackerAssetReferenceResolver.java | 1 + .../services/PackerIdentityAllocator.java | 1 + .../services/PackerOutputContractCatalog.java | 6 +- .../packer/services/PackerRegistryLookup.java | 1 + .../services/PackerWorkspaceFoundation.java | 7 +- .../FileSystemPackerWorkspaceServiceTest.java | 23 +- .../PackerAssetDetailsServiceTest.java | 15 +- .../services/PackerRuntimeRegistryTest.java | 11 +- .../PackerWorkspaceFoundationTest.java | 26 +- test-projects/main/.studio/activities.json | 1260 ++++++++--------- test-projects/main/assets/bigode/asset.json | 2 +- .../recovered/atlas2/confirm.palette.json | 4 + .../atlas2/{sprites => }/confirm.png | Bin .../assets/recovered/atlas2/confirm.tile.json | 5 + .../main/assets/ui/atlas2/asset.json | 2 +- .../assets/ui/atlas2/confirm.palette.json | 4 + .../ui/atlas2/{sprites => }/confirm.png | Bin .../main/assets/ui/atlas2/confirm.tile.json | 5 + 43 files changed, 1565 insertions(+), 726 deletions(-) create mode 100644 docs/packer/agendas/Tilemap and Metatile Runtime Binary Layout Agenda.md rename prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/{services => }/PackerWorkspacePaths.java (84%) create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerFileProbe.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerPaletteV1.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerProbeResult.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerSoundBankRequirements.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileBankRequirements.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileIndexedV1.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerWalkResult.java rename prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/{services => repositories}/FileSystemPackerRegistryRepository.java (92%) create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAbstractBankWalker.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAssetWalker.java rename prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/{services => repositories}/PackerRegistryRepository.java (89%) rename prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/{services => repositories}/PackerRuntimeLoader.java (67%) rename prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/{services => repositories}/PackerRuntimeRegistry.java (98%) rename prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/{services => repositories}/PackerRuntimeSnapshotLoader.java (88%) create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerSoundBankWalker.java create mode 100644 prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerTileBankWalker.java create mode 100644 test-projects/main/assets/recovered/atlas2/confirm.palette.json rename test-projects/main/assets/recovered/atlas2/{sprites => }/confirm.png (100%) create mode 100644 test-projects/main/assets/recovered/atlas2/confirm.tile.json create mode 100644 test-projects/main/assets/ui/atlas2/confirm.palette.json rename test-projects/main/assets/ui/atlas2/{sprites => }/confirm.png (100%) create mode 100644 test-projects/main/assets/ui/atlas2/confirm.tile.json diff --git a/docs/packer/agendas/README.md b/docs/packer/agendas/README.md index a93075eb..97d2bbab 100644 --- a/docs/packer/agendas/README.md +++ b/docs/packer/agendas/README.md @@ -4,7 +4,7 @@ This directory contains active packer discussion agendas. ## Active Agendas -There are currently no active packer agendas. +1. [`Tilemap and Metatile Runtime Binary Layout Agenda.md`](./Tilemap%20and%20Metatile%20Runtime%20Binary%20Layout%20Agenda.md) The first packer agenda wave was consolidated into normative specs under [`../specs/`](../specs/) and didactic material under [`../learn/`](../learn/). diff --git a/docs/packer/agendas/Tilemap and Metatile Runtime Binary Layout Agenda.md b/docs/packer/agendas/Tilemap and Metatile Runtime Binary Layout Agenda.md new file mode 100644 index 00000000..47d39bed --- /dev/null +++ b/docs/packer/agendas/Tilemap and Metatile Runtime Binary Layout Agenda.md @@ -0,0 +1,152 @@ +# Tilemap and Metatile Runtime Binary Layout Agenda + +## Status + +Open + +## Purpose + +Convergir a discussão sobre o contrato de mapa para handheld em duas camadas: + +- formato de autoria/edição (JSON legível), +- formato de execução (layout binário compacto em runtime). + +O objetivo é fechar qual estrutura será adotada para `tilemap bank`, `tileset bank` e dados de colisão/flags, com foco em previsibilidade de memória e streaming de mapas. + +## Domain Owner + +`docs/packer` + +Este tema é owner de packer porque envolve transformação de artefatos (`JSON -> BIN`), contrato de build e layout de saída para consumo do runtime. + +Domínios impactados (cross-domain): + +- `docs/vm-arch` (contrato de leitura em runtime e limites de memória), +- `docs/studio` (edição/preview de mapas no workspace), +- `docs/compiler/pbs` (integração futura com referências de assets em código, quando aplicável). + +## Problem + +Hoje existe alinhamento conceitual de que: + +1. visual e colisão devem ser separados por responsabilidade; +2. o mapa não deve repetir metadados extensos por célula; +3. apenas uma janela ativa de até `9` mapas deve ficar residente em memória. + +Mas ainda não existe decisão formal sobre: + +- layout binário final por célula em runtime, +- orçamento por mapa e por janela ativa, +- responsabilidade exata entre `map bank` e `tileset bank`. + +Sem esse contrato, o packer não fecha especificação de saída e o runtime/studio ficam sem baseline único. + +## Context + +Premissas atuais da discussão: + +- `tileset bank` pode ter tamanho diferente dos demais banks; +- `map bank` não precisa seguir o mesmo tamanho de `tileset bank`; +- mapa deve referenciar IDs compactos (visual, colisão e flags), em vez de duplicar estrutura completa por célula; +- paletas por bank continuam sendo opção válida para preservar decisões artísticas locais; +- orçamento alvo foi discutido no contexto de `64 KiB` por map bank e janela ativa de `3x3` mapas (`9` residentes). + +## Options + +### Option A — Célula `u8` (mapa ultra-compacto) + +- Cada célula armazena apenas `metatileId` (`0..255`). +- Colisão/flags vêm de tabela auxiliar por `metatileId`. + +### Option B — Célula `u16` bit-packed (recomendação inicial) + +- Cada célula usa `16 bits` com divisão sugerida: + - `visualId`: `10 bits` (`0..1023`), + - `collisionId`: `5 bits` (`0..31`), + - `flags`: `1 bit` (`0..1`) ou reservado para evolução. +- Permite desacoplamento visual/lógico sem custo de `u32`. + +### Option C — Célula `u32` (maior flexibilidade) + +- Exemplo de divisão: + - `visualId`: `12 bits`, + - `collisionId`: `8 bits`, + - `flags/event`: `12 bits`. +- Ganho de expressividade para triggers/eventos inline; custo de memória dobra vs `u16`. + +## Tradeoffs + +- Option A minimiza RAM e I/O, mas limita variedade visual e desloca muita semântica para tabelas externas. +- Option B oferece bom equilíbrio para handheld: compacta, previsível e com espaço suficiente para muitos casos de mapa. +- Option C simplifica evolução funcional (eventos por célula), mas pressiona memória e banda de streaming sem necessidade comprovada agora. + +## Runtime Binary Structure (focus) + +Estrutura sugerida para runtime (baseada em Option B): + +1. **Map Header (fixo)** + - `magic` (`4 bytes`) + - `version` (`u16`) + - `width` (`u16`) + - `height` (`u16`) + - `cellEncoding` (`u8`) — ex.: `1 = U16_PACKED_V1` + - `visualBankId` (`u16`) + - `collisionBankId` (`u16`) + - `reserved` / `checksum` (conforme decisão posterior) + +2. **Cell Stream** + - vetor contínuo com `width * height` células `u16`, little-endian; + - leitura linear favorece cache e descompressão simples. + +3. **Optional Chunks (future-proof)** + - bloco opcional de `eventTriggers`; + - bloco opcional de `spawnPoints`; + - bloco opcional de `navHints`. + +Packing de célula (`U16_PACKED_V1`): + +- `bits 0..9` => `visualId` +- `bits 10..14` => `collisionId` +- `bit 15` => `flag0` + +Decodificação de referência: + +- `visualId -> metatile visual table -> 4 subtiles (8x8) + palette/flip/priority` +- `collisionId -> collision/material table -> walk/solid/swim/damage/etc.` + +## Memory Notes for Active Window (`9` maps) + +Assumindo `64 KiB` por map bank: + +- `1` mapa residente: `64 KiB` +- `9` mapas residentes: `576 KiB` + +Capacidade por encoding dentro de `64 KiB`: + +- `u8`: `65,536` células (`256x256` máximo quadrado) +- `u16`: `32,768` células (`~181x181` máximo quadrado, ou retângulos equivalentes) +- `u32`: `16,384` células (`128x128` máximo quadrado) + +## Recommendation + +Adotar `Option B` como baseline para decisão: + +1. autoria em JSON orientada a IDs (`visualId`, `collisionId`, `flags`); +2. empacotamento determinístico para `U16_PACKED_V1` no build; +3. janela ativa de runtime limitada a `9` mapas com budget explícito; +4. extensão para `u32` somente via nova versão de encoding e evidência de necessidade. + +## Open Questions + +1. `collisionId` com `5 bits` (`32` classes) é suficiente para os biomas/projetos previstos? +2. `flag0` deve ser reservado para trigger rápido ou para variação visual contextual? +3. Quais chunks opcionais entram já em `V1` e quais ficam para `V2`? +4. O `map bank` seguirá estritamente `64 KiB` ou terá tamanho variável com metadado de capacidade? +5. Qual política de compressão do stream (`none`, `LZ4`, etc.) será padrão no packer? + +## Expected Follow-up + +1. Abrir `decision` em `docs/packer/decisions` fechando o encoding `U16_PACKED_V1`. +2. Propagar contrato de leitura para `docs/vm-arch`. +3. Definir no `docs/studio/specs` o schema de edição JSON correspondente. +4. Planejar PR de implementação (`packer` + `runtime`) com testes de roundtrip (`JSON -> BIN -> decode`). diff --git a/prometeu-packer/prometeu-packer-v1/build.gradle.kts b/prometeu-packer/prometeu-packer-v1/build.gradle.kts index 5ab69752..379a12be 100644 --- a/prometeu-packer/prometeu-packer-v1/build.gradle.kts +++ b/prometeu-packer/prometeu-packer-v1/build.gradle.kts @@ -5,4 +5,5 @@ plugins { dependencies { implementation(project(":prometeu-infra")) implementation(project(":prometeu-packer:prometeu-packer-api")) + implementation("org.apache.tika:tika-core:3.2.1") } diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/Packer.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/Packer.java index d10b6dda..60576c2b 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/Packer.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/Packer.java @@ -2,6 +2,9 @@ package p.packer; import com.fasterxml.jackson.databind.ObjectMapper; import p.packer.events.PackerEventSink; +import p.packer.repositories.PackerAssetWalker; +import p.packer.repositories.PackerRuntimeLoader; +import p.packer.repositories.PackerRuntimeRegistry; import p.packer.services.*; import java.io.Closeable; @@ -23,9 +26,11 @@ public final class Packer implements Closeable { public static Packer bootstrap(final ObjectMapper mapper, final PackerEventSink eventSink) { final var resolvedEventSink = Objects.requireNonNull(eventSink, "eventSink"); - final var workspaceFoundation = new PackerWorkspaceFoundation(); + final var workspaceFoundation = new PackerWorkspaceFoundation(mapper); final var declarationParser = new PackerAssetDeclarationParser(mapper); - final var runtimeRegistry = new PackerRuntimeRegistry(new PackerRuntimeLoader(workspaceFoundation, declarationParser)); + final var assetWalker = new PackerAssetWalker(mapper); + final var runtimeLoader = new PackerRuntimeLoader(workspaceFoundation, declarationParser, assetWalker); + final var runtimeRegistry = new PackerRuntimeRegistry(runtimeLoader); final var assetReferenceResolver = new PackerAssetReferenceResolver(workspaceFoundation.lookup()); final var assetDetailsService = new PackerAssetDetailsService(runtimeRegistry, assetReferenceResolver); final var assetActionReadService = new PackerAssetActionReadService( diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspacePaths.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/PackerWorkspacePaths.java similarity index 84% rename from prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspacePaths.java rename to prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/PackerWorkspacePaths.java index e73cdaf5..dfc9700b 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspacePaths.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/PackerWorkspacePaths.java @@ -1,4 +1,4 @@ -package p.packer.services; +package p.packer; import p.packer.messages.PackerProjectContext; @@ -9,6 +9,7 @@ public final class PackerWorkspacePaths { private static final String ASSETS_DIR = "assets"; private static final String PROMETEU_DIR = ".prometeu"; private static final String REGISTRY_FILE = "index.json"; + private static final String CACHE_FILE = "cache.json"; private PackerWorkspacePaths() { } @@ -25,6 +26,10 @@ public final class PackerWorkspacePaths { return registryDirectory(project).resolve(REGISTRY_FILE).toAbsolutePath().normalize(); } + public static Path cachePath(PackerProjectContext project) { + return registryDirectory(project).resolve(CACHE_FILE).toAbsolutePath().normalize(); + } + public static Path assetRoot(PackerProjectContext project, String relativeRoot) { return assetsRoot(project).resolve(relativeRoot).toAbsolutePath().normalize(); } diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerFileProbe.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerFileProbe.java new file mode 100644 index 00000000..6d234389 --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerFileProbe.java @@ -0,0 +1,10 @@ +package p.packer.models; + +import java.nio.file.Path; + +public record PackerFileProbe( + Path path, + String mimeType, + long lastModified, + byte[] content) { +} diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerPaletteV1.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerPaletteV1.java new file mode 100644 index 00000000..c67b8b6d --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerPaletteV1.java @@ -0,0 +1,6 @@ +package p.packer.models; + +public record PackerPaletteV1( + int[] originalArgb8888, + short[] convertedRgb565) { +} \ No newline at end of file diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerProbeResult.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerProbeResult.java new file mode 100644 index 00000000..d3e98413 --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerProbeResult.java @@ -0,0 +1,10 @@ +package p.packer.models; + +import java.util.List; +import java.util.Map; + +public record PackerProbeResult( + PackerFileProbe fileProbe, + Map metadata, + List diagnostics) { +} \ No newline at end of file diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerSoundBankRequirements.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerSoundBankRequirements.java new file mode 100644 index 00000000..7030bf05 --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerSoundBankRequirements.java @@ -0,0 +1,4 @@ +package p.packer.models; + +public record PackerSoundBankRequirements(int sampleRate, int channels) { +} \ No newline at end of file diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileBankRequirements.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileBankRequirements.java new file mode 100644 index 00000000..65476b07 --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileBankRequirements.java @@ -0,0 +1,4 @@ +package p.packer.models; + +public record PackerTileBankRequirements(int tileSize) { +} diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileIndexedV1.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileIndexedV1.java new file mode 100644 index 00000000..9754b3ee --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerTileIndexedV1.java @@ -0,0 +1,7 @@ +package p.packer.models; + +public record PackerTileIndexedV1( + int width, + int height, + byte[] paletteIndices) { +} \ No newline at end of file diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerWalkResult.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerWalkResult.java new file mode 100644 index 00000000..8329c63b --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/PackerWalkResult.java @@ -0,0 +1,16 @@ +package p.packer.models; + +import java.util.List; +import java.util.Objects; + +public record PackerWalkResult( + List probeResults, + List diagnostics) { + + public static final PackerWalkResult EMPTY = new PackerWalkResult(List.of(), List.of()); + + public PackerWalkResult { + probeResults = List.copyOf(Objects.requireNonNull(probeResults, "probeResults")); + diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics")); + } +} \ No newline at end of file diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerRegistryRepository.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/FileSystemPackerRegistryRepository.java similarity index 92% rename from prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerRegistryRepository.java rename to prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/FileSystemPackerRegistryRepository.java index 8ad2af02..7223ae56 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerRegistryRepository.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/FileSystemPackerRegistryRepository.java @@ -1,4 +1,4 @@ -package p.packer.services; +package p.packer.repositories; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -7,6 +7,7 @@ import p.packer.exceptions.PackerRegistryException; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRegistryEntry; import p.packer.models.PackerRegistryState; +import p.packer.PackerWorkspacePaths; import java.io.IOException; import java.nio.file.Files; @@ -14,9 +15,14 @@ import java.nio.file.Path; import java.util.*; public final class FileSystemPackerRegistryRepository implements PackerRegistryRepository { - private static final ObjectMapper MAPPER = new ObjectMapper(); private static final int REGISTRY_SCHEMA_VERSION = 1; + private final ObjectMapper mapper; + + public FileSystemPackerRegistryRepository(final ObjectMapper mapper) { + this.mapper = Objects.requireNonNull(mapper, "mapper"); + } + @Override public PackerRegistryState load(PackerProjectContext project) { final Path registryPath = PackerWorkspacePaths.registryPath(project); @@ -25,10 +31,10 @@ public final class FileSystemPackerRegistryRepository implements PackerRegistryR } try { - final RegistryDocument document = MAPPER.readValue(registryPath.toFile(), RegistryDocument.class); + final RegistryDocument document = mapper.readValue(registryPath.toFile(), RegistryDocument.class); final int schemaVersion = document.schemaVersion <= 0 ? REGISTRY_SCHEMA_VERSION : document.schemaVersion; if (schemaVersion != REGISTRY_SCHEMA_VERSION) { - throw new p.packer.exceptions.PackerRegistryException("Unsupported registry schema_version: " + schemaVersion); + throw new PackerRegistryException("Unsupported registry schema_version: " + schemaVersion); } final List entries = new ArrayList<>(); if (document.assets != null) { @@ -73,7 +79,7 @@ public final class FileSystemPackerRegistryRepository implements PackerRegistryR entry.root(), entry.includedInBuild())) .toList(); - MAPPER.writerWithDefaultPrettyPrinter().writeValue(registryPath.toFile(), document); + mapper.writerWithDefaultPrettyPrinter().writeValue(registryPath.toFile(), document); } catch (IOException exception) { throw new p.packer.exceptions.PackerRegistryException("Unable to save registry: " + registryPath, exception); } diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAbstractBankWalker.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAbstractBankWalker.java new file mode 100644 index 00000000..4bc8dcdf --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAbstractBankWalker.java @@ -0,0 +1,92 @@ +package p.packer.repositories; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tika.Tika; +import p.packer.messages.diagnostics.PackerDiagnosticCategory; +import p.packer.messages.diagnostics.PackerDiagnosticSeverity; +import p.packer.models.PackerDiagnostic; +import p.packer.models.PackerFileProbe; +import p.packer.models.PackerProbeResult; +import p.packer.models.PackerWalkResult; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public abstract class PackerAbstractBankWalker { + private final Tika tika = new Tika(); + protected final ObjectMapper mapper; + + protected PackerAbstractBankWalker(ObjectMapper mapper) { + this.mapper = mapper; + } + + protected abstract Set getSupportedMimeTypes(); + protected abstract PackerProbeResult processFileProbe(PackerFileProbe fileProbe, R requirements); + + private Optional getFileProbeIfSupported( + final Path filePath, + final Set supportedMimeTypes) throws IOException { + final var bytes = Files.readAllBytes(filePath); + final var mimeType = tika.detect(bytes); + if (!supportedMimeTypes.contains(mimeType)) { + return Optional.empty(); + } + final var lastModified = filePath.toFile().lastModified(); + final var fileProbe = new PackerFileProbe(filePath, mimeType, lastModified, bytes); + return Optional.of(fileProbe); + } + + private List retrieveValidPaths(final Path assetRoot) throws IOException { + try (final var paths = Files.list(assetRoot) + .filter(Files::isRegularFile) + .filter(path -> !isAssetManifest(path))) { + return paths.toList(); + } + } + + private boolean isAssetManifest(final Path path) { + return path.getFileName().toString().equalsIgnoreCase("asset.json"); + } + + private List processFileProbeWhenSupported( + final Path assetRoot, + final Set supportedMimeTypes, + final List diagnostics) { + final List supportedFileProbes = new ArrayList<>(); + try { + final var validPaths = retrieveValidPaths(assetRoot); + for (final var filePath : validPaths) { + final var fileProbeMaybe = getFileProbeIfSupported(filePath, supportedMimeTypes); + if (fileProbeMaybe.isEmpty()) { + continue; + } + final var fileProbe = fileProbeMaybe.get(); + supportedFileProbes.add(fileProbe); + } + } catch (IOException exception) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + "Unable to walk tile asset root: " + exception.getMessage(), + assetRoot, + true + )); + } + return supportedFileProbes; + } + + public PackerWalkResult walk( + final Path assetRoot, + final R requirements) { + final List rootDiagnostics = new ArrayList<>(); + final List probeResults = new ArrayList<>(); + processFileProbeWhenSupported(assetRoot, getSupportedMimeTypes(), rootDiagnostics) + .forEach(fileProbe -> probeResults.add(processFileProbe(fileProbe, requirements))); + return new PackerWalkResult(probeResults, rootDiagnostics); + } +} diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAssetWalker.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAssetWalker.java new file mode 100644 index 00000000..dff61f35 --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerAssetWalker.java @@ -0,0 +1,164 @@ +package p.packer.repositories; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import p.packer.messages.assets.OutputFormatCatalog; +import p.packer.messages.diagnostics.PackerDiagnosticCategory; +import p.packer.messages.diagnostics.PackerDiagnosticSeverity; +import p.packer.models.*; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class PackerAssetWalker { + private static final Set TILE_BANK_SUPPORTED_FORMATS = Set.of( + OutputFormatCatalog.TILES_INDEXED_V1 + ); + + private static final Set SOUND_BANK_SUPPORTED_FORMATS = Set.of( + OutputFormatCatalog.SOUND_V1 + ); + + private final PackerTileBankWalker tileBankWalker; + private final PackerSoundBankWalker soundBankWalker; + + public PackerAssetWalker(final ObjectMapper mapper) { + this.tileBankWalker = new PackerTileBankWalker(mapper); + this.soundBankWalker = new PackerSoundBankWalker(mapper); + } + + public PackerWalkResult walk( + final Path assetRoot, + final PackerAssetDeclaration declaration) { + final List diagnostics = new ArrayList<>(); + switch (declaration.assetFamily()) { + case TILE_BANK -> { + var metadata = declaration.outputMetadata(); + if (MapUtils.isEmpty(metadata)) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.WARNING, + PackerDiagnosticCategory.HYGIENE, + "Output metadata for tile bank cannot be empty, using default values", + assetRoot, + false)); + metadata = Map.of("tile_size", "16x16"); + } + final var requirementBuildResult = buildTileBankRequirements(declaration, metadata); + if (requirementBuildResult.hasError()) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + requirementBuildResult.errorMessage(), + assetRoot, + true)); + return new PackerWalkResult(List.of(), diagnostics); + } + final var walkResult = tileBankWalker.walk(assetRoot, requirementBuildResult.requirements); + diagnostics.addAll(walkResult.diagnostics()); + return new PackerWalkResult(walkResult.probeResults(), diagnostics); + } + case SOUND_BANK -> { + var metadata = declaration.outputMetadata(); + if (MapUtils.isEmpty(metadata)) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.WARNING, + PackerDiagnosticCategory.HYGIENE, + "Output metadata for tile bank cannot be empty, using default values", + assetRoot, + false)); + metadata = Map.of("sample_rate", "44100", "channels", "1"); + } + final var result = buildSoundBankRequirements(declaration, metadata); + if (result.hasError()) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + result.errorMessage(), + assetRoot, + true)); + return new PackerWalkResult(List.of(), diagnostics); + } + final var walkResult = soundBankWalker.walk(assetRoot, result.requirements); + diagnostics.addAll(walkResult.diagnostics()); + return new PackerWalkResult(walkResult.probeResults(), diagnostics); + } + case UNKNOWN -> { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.WARNING, + PackerDiagnosticCategory.STRUCTURAL, + "Unknown asset family for declaration, skipping content walk", + assetRoot, + false)); + return new PackerWalkResult(List.of(), diagnostics); + } + } + return PackerWalkResult.EMPTY; + } + + private RequirementBuildResult buildTileBankRequirements( + final PackerAssetDeclaration declaration, + final Map metadata) { + if (!TILE_BANK_SUPPORTED_FORMATS.contains(declaration.outputFormat())) { + return RequirementBuildResult.fail("Unsupported output format for tile bank: " + declaration.outputFormat()); + } + final var tileSizeStr = metadata.get("tile_size"); + if (StringUtils.isBlank(tileSizeStr)) { + return RequirementBuildResult.fail("Tile size metadata for tile bank cannot be empty"); + } + final int tileSize; + switch (tileSizeStr) { + case "8x8": { + tileSize = 8; + } break; + case "16x16": { + tileSize = 16; + } break; + case "32x32": { + tileSize = 32; + } break; + default: { + return RequirementBuildResult.fail("Unsupported tile size for tile bank: " + tileSizeStr); + } + } + return RequirementBuildResult.success(new PackerTileBankRequirements(tileSize)); + } + + private RequirementBuildResult buildSoundBankRequirements( + final PackerAssetDeclaration declaration, + final Map metadata) { + if (!SOUND_BANK_SUPPORTED_FORMATS.contains(declaration.outputFormat())) { + return RequirementBuildResult.fail("Unsupported output format for sound bank: " + declaration.outputFormat()); + } + final var sampleRateStr = metadata.get("sample_rate"); + if (StringUtils.isBlank(sampleRateStr)) { + return RequirementBuildResult.fail("Missing sample rate for sound bank"); + } + final var sampleRate = Integer.parseInt(sampleRateStr); + final var channelsStr = metadata.get(""); + if (StringUtils.isBlank(channelsStr)) { + return RequirementBuildResult.fail("Missing channels for sound bank"); + } + final var channels = Integer.parseInt(channelsStr); + return RequirementBuildResult.success(new PackerSoundBankRequirements(sampleRate, channels)); + } + + private record RequirementBuildResult( + T requirements, + String errorMessage) { + static RequirementBuildResult success(T requirements) { + return new RequirementBuildResult<>(requirements, null); + } + + static RequirementBuildResult fail(String errorMessage) { + return new RequirementBuildResult<>(null, errorMessage); + } + + public boolean hasError() { + return StringUtils.isNotBlank(errorMessage); + } + } +} diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryRepository.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRegistryRepository.java similarity index 89% rename from prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryRepository.java rename to prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRegistryRepository.java index d6a220d4..3f4fb050 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryRepository.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRegistryRepository.java @@ -1,4 +1,4 @@ -package p.packer.services; +package p.packer.repositories; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRegistryState; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeLoader.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeLoader.java similarity index 67% rename from prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeLoader.java rename to prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeLoader.java index 9fc277bf..92beddeb 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeLoader.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeLoader.java @@ -1,11 +1,12 @@ -package p.packer.services; +package p.packer.repositories; import p.packer.messages.InitWorkspaceRequest; import p.packer.messages.PackerProjectContext; -import p.packer.models.PackerRegistryEntry; -import p.packer.models.PackerRegistryState; import p.packer.models.PackerRuntimeAsset; import p.packer.models.PackerRuntimeSnapshot; +import p.packer.services.PackerAssetDeclarationParser; +import p.packer.services.PackerWorkspaceFoundation; +import p.packer.PackerWorkspacePaths; import java.io.IOException; import java.nio.file.Files; @@ -17,12 +18,15 @@ import java.util.stream.Collectors; public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader { private final PackerWorkspaceFoundation workspaceFoundation; private final PackerAssetDeclarationParser parser; + private final PackerAssetWalker assetWalker; public PackerRuntimeLoader( - PackerWorkspaceFoundation workspaceFoundation, - PackerAssetDeclarationParser parser) { + final PackerWorkspaceFoundation workspaceFoundation, + final PackerAssetDeclarationParser parser, + final PackerAssetWalker assetWalker) { this.workspaceFoundation = Objects.requireNonNull(workspaceFoundation, "workspaceFoundation"); this.parser = Objects.requireNonNull(parser, "parser"); + this.assetWalker = Objects.requireNonNull(assetWalker, "assetWalker"); } private boolean isAssetJson(Path path, BasicFileAttributes attrs) { @@ -31,11 +35,13 @@ public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader { @Override public PackerRuntimeSnapshot load(PackerProjectContext project, long generation) { - final PackerProjectContext safeProject = Objects.requireNonNull(project, "project"); + final var safeProject = Objects.requireNonNull(project, "project"); workspaceFoundation.initWorkspace(new InitWorkspaceRequest(safeProject)); - final PackerRegistryState registry = workspaceFoundation.loadRegistry(safeProject); - final Map registryByRoot = registry.assets().stream() + final var registry = workspaceFoundation.loadRegistry(safeProject); + final var registryByRoot = registry + .assets() + .stream() .collect(Collectors.toMap( entry -> PackerWorkspacePaths.assetRoot(safeProject, entry.root()), entry -> entry)); @@ -51,11 +57,13 @@ public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader { for (final var manifestPath : manifests) { final var assetRoot = manifestPath.getParent(); final var registryEntry = Optional.ofNullable(registryByRoot.get(assetRoot)); - assets.add(new PackerRuntimeAsset( - assetRoot, - manifestPath, - registryEntry, - parser.parse(manifestPath))); + final var parseResult = parser.parse(manifestPath); + if (parseResult.valid()) { + final var walkResult = assetWalker.walk(assetRoot, parseResult.declaration()); + + } + final var runtimeAsset = new PackerRuntimeAsset(assetRoot, manifestPath, registryEntry, parseResult); + assets.add(runtimeAsset); } } catch (IOException exception) { throw new p.packer.exceptions.PackerRegistryException( diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeRegistry.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeRegistry.java similarity index 98% rename from prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeRegistry.java rename to prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeRegistry.java index 83324874..c1e8ae79 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeRegistry.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeRegistry.java @@ -1,7 +1,8 @@ -package p.packer.services; +package p.packer.repositories; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRuntimeSnapshot; +import p.packer.services.PackerProjectRuntime; import java.nio.file.Path; import java.util.Objects; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeSnapshotLoader.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeSnapshotLoader.java similarity index 88% rename from prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeSnapshotLoader.java rename to prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeSnapshotLoader.java index 65b2bb45..1ef5a1ce 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRuntimeSnapshotLoader.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerRuntimeSnapshotLoader.java @@ -1,4 +1,4 @@ -package p.packer.services; +package p.packer.repositories; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRuntimeSnapshot; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerSoundBankWalker.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerSoundBankWalker.java new file mode 100644 index 00000000..fc567a67 --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerSoundBankWalker.java @@ -0,0 +1,33 @@ +package p.packer.repositories; + +import com.fasterxml.jackson.databind.ObjectMapper; +import p.packer.models.PackerDiagnostic; +import p.packer.models.PackerFileProbe; +import p.packer.models.PackerProbeResult; +import p.packer.models.PackerSoundBankRequirements; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class PackerSoundBankWalker extends PackerAbstractBankWalker { + private static final Set SUPPORTED_MIME_TYPES = Set.of("audio/wav"); + + public PackerSoundBankWalker(ObjectMapper mapper) { + super(mapper); + } + + @Override + protected Set getSupportedMimeTypes() { + return SUPPORTED_MIME_TYPES; + } + + @Override + protected PackerProbeResult processFileProbe( + final PackerFileProbe fileProbe, + final PackerSoundBankRequirements requirements) { + final List diagnostics = new ArrayList<>(); + return new PackerProbeResult(fileProbe, Map.of(), diagnostics); + } +} diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerTileBankWalker.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerTileBankWalker.java new file mode 100644 index 00000000..614c001f --- /dev/null +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/repositories/PackerTileBankWalker.java @@ -0,0 +1,231 @@ +package p.packer.repositories; + +import com.fasterxml.jackson.databind.ObjectMapper; +import p.packer.messages.diagnostics.PackerDiagnosticCategory; +import p.packer.messages.diagnostics.PackerDiagnosticSeverity; +import p.packer.models.*; + +import javax.imageio.ImageIO; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public class PackerTileBankWalker extends PackerAbstractBankWalker { + private static final int MAX_COLORS_PER_PALETTE = 15; + private static final int COLOR_KEY_RGB = 0x00FF00FF; + private static final Set SUPPORTED_MIME_TYPES = Set.of( + "image/png" + ); + + public PackerTileBankWalker(ObjectMapper mapper) { + super(mapper); + } + + @Override + protected Set getSupportedMimeTypes() { + return SUPPORTED_MIME_TYPES; + } + + @Override + protected PackerProbeResult processFileProbe( + final PackerFileProbe fileProbe, + final PackerTileBankRequirements requirements) { + final List diagnostics = new ArrayList<>(); + + final var image = readImage(fileProbe, diagnostics); + if (image == null) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + "Unable to decode tile image content: " + fileProbe.path().getFileName(), + fileProbe.path(), + true + )); + return new PackerProbeResult(fileProbe, Map.of(), diagnostics); + } + + final var width = image.getWidth(); + final var height = image.getHeight(); + if (width != requirements.tileSize() || height != requirements.tileSize()) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + "Invalid tile dimensions for " + fileProbe.path().getFileName() + + ": expected " + requirements.tileSize() + "x" + requirements.tileSize() + + " but got " + width + "x" + height, + fileProbe.path(), + true + )); + } + + final Map paletteIndexByRgb = new LinkedHashMap<>(); + final byte[] paletteIndices = new byte[image.getWidth() * image.getHeight()]; + boolean partialAlphaFound = false; + boolean exceededPaletteLimit = false; + + int offset = 0; + for (var y = 0; y < image.getHeight(); y++) { + for (var x = 0; x < image.getWidth(); x++) { + final int argb = image.getRGB(x, y); + final int alpha = (argb >>> 24) & 0xFF; + final int rgb = argb & 0x00FFFFFF; + + if (alpha == 0 || rgb == COLOR_KEY_RGB) { + paletteIndices[offset++] = 0; + continue; + } + + if (alpha < 0xFF) { + partialAlphaFound = true; + } + + Integer paletteIndex = paletteIndexByRgb.get(rgb); + if (paletteIndex == null) { + if (paletteIndexByRgb.size() >= MAX_COLORS_PER_PALETTE) { + exceededPaletteLimit = true; + paletteIndices[offset++] = 0; + continue; + } + paletteIndex = paletteIndexByRgb.size() + 1; + paletteIndexByRgb.put(rgb, paletteIndex); + } + + paletteIndices[offset++] = paletteIndex.byteValue(); + } + } + + if (partialAlphaFound) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.WARNING, + PackerDiagnosticCategory.HYGIENE, + "Tile contains partial alpha; flattening to RGB and ignoring alpha channel", + fileProbe.path(), + false + )); + } + + if (exceededPaletteLimit) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + "Tile image exceeds color limit for " + fileProbe.path().getFileName() + + ": expected at most " + MAX_COLORS_PER_PALETTE + " colors for indices 1..15", + fileProbe.path(), + true + )); + } + + if (diagnostics.stream().anyMatch(PackerDiagnostic::blocking)) { + return new PackerProbeResult(fileProbe, Map.of(), diagnostics); + } + + final var tile = new PackerTileIndexedV1(image.getWidth(), image.getHeight(), paletteIndices); + final var palette = buildPalette(paletteIndexByRgb); + serializeProbeArtifacts(fileProbe, tile, palette, diagnostics); + return new PackerProbeResult(fileProbe, Map.of("tile", tile, "palette", palette), diagnostics); + } + + private void serializeProbeArtifacts( + final PackerFileProbe fileProbe, + final PackerTileIndexedV1 tile, + final PackerPaletteV1 palette, + final List diagnostics) { + final Path basePath = baseOutputPath(fileProbe.path()); + final Path tileOutputPath = Path.of(basePath + ".tile.json"); + final Path paletteOutputPath = Path.of(basePath + ".palette.json"); + + try { + Files.createDirectories(Objects.requireNonNull(tileOutputPath.getParent())); + mapper.writerWithDefaultPrettyPrinter().writeValue(tileOutputPath.toFile(), toTilePayload(tile)); + mapper.writerWithDefaultPrettyPrinter().writeValue(paletteOutputPath.toFile(), toPalettePayload(palette)); + } catch (IOException exception) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + "Unable to serialize tile/palette artifacts for " + fileProbe.path().getFileName() + + ": " + exception.getMessage(), + fileProbe.path(), + true + )); + } + } + + private Path baseOutputPath(final Path sourcePath) { + final String fileName = sourcePath.getFileName().toString(); + final int extensionStart = fileName.lastIndexOf('.'); + final String outputBaseName = extensionStart > 0 ? fileName.substring(0, extensionStart) : fileName; + final Path parent = sourcePath.getParent(); + if (parent == null) { + return Path.of(outputBaseName); + } + return parent.resolve(outputBaseName); + } + + private Map toTilePayload(final PackerTileIndexedV1 tile) { + final List indices = new ArrayList<>(tile.paletteIndices().length); + for (final byte value : tile.paletteIndices()) { + indices.add(Byte.toUnsignedInt(value)); + } + + return Map.of( + "width", tile.width(), + "height", tile.height(), + "paletteIndices", indices + ); + } + + private Map toPalettePayload(final PackerPaletteV1 palette) { + final List convertedRgb565 = new ArrayList<>(palette.convertedRgb565().length); + for (final short value : palette.convertedRgb565()) { + convertedRgb565.add(Short.toUnsignedInt(value)); + } + + return Map.of( + "originalArgb8888", palette.originalArgb8888(), + "convertedRgb565", convertedRgb565 + ); + } + + private PackerPaletteV1 buildPalette(final Map paletteIndexByRgb) { + final int[] originalArgb8888 = new int[paletteIndexByRgb.size()]; + final short[] convertedRgb565 = new short[paletteIndexByRgb.size()]; + for (final var entry : paletteIndexByRgb.entrySet()) { + final int rgb = entry.getKey(); + final int index = entry.getValue() - 1; + final int argb = 0xFF000000 | rgb; + originalArgb8888[index] = argb; + convertedRgb565[index] = convertToRgb565(argb); + } + return new PackerPaletteV1(originalArgb8888, convertedRgb565); + } + + private short convertToRgb565(final int argb) { + final int red = (argb >> 16) & 0xFF; + final int green = (argb >> 8) & 0xFF; + final int blue = argb & 0xFF; + + final int r5 = (red >> 3) & 0x1F; + final int g6 = (green >> 2) & 0x3F; + final int b5 = (blue >> 3) & 0x1F; + return (short) ((r5 << 11) | (g6 << 5) | b5); + } + + private java.awt.image.BufferedImage readImage( + final PackerFileProbe fileProbe, + final List diagnostics) { + try { + return ImageIO.read(new ByteArrayInputStream(fileProbe.content())); + } catch (Exception exception) { + diagnostics.add(new PackerDiagnostic( + PackerDiagnosticSeverity.ERROR, + PackerDiagnosticCategory.STRUCTURAL, + "Unable to decode tile image content: " + exception.getMessage(), + fileProbe.path(), + true + )); + return null; + } + } +} diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerWorkspaceService.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerWorkspaceService.java index d4302bfc..63ed0087 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerWorkspaceService.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/FileSystemPackerWorkspaceService.java @@ -3,6 +3,7 @@ package p.packer.services; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import p.packer.PackerWorkspacePaths; import p.packer.PackerWorkspaceService; import p.packer.events.PackerEventKind; import p.packer.events.PackerEventSink; @@ -12,6 +13,7 @@ import p.packer.messages.assets.*; import p.packer.messages.diagnostics.PackerDiagnosticCategory; import p.packer.messages.diagnostics.PackerDiagnosticSeverity; import p.packer.models.*; +import p.packer.repositories.PackerRuntimeRegistry; import java.io.IOException; import java.nio.file.Files; @@ -57,14 +59,14 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe @Override public ListAssetsResult listAssets(ListAssetsRequest request) { - final PackerProjectContext project = Objects.requireNonNull(request, "request").project(); - final PackerRuntimeSnapshot snapshot = request.deepSync() + final var project = Objects.requireNonNull(request, "request").project(); + final var snapshot = request.deepSync() ? runtimeRegistry.refresh(project).snapshot() : runtimeRegistry.getOrLoad(project).snapshot(); - final PackerOperationEventEmitter events = new PackerOperationEventEmitter(project, eventSink); - final PackerRegistryState registry = snapshot.registry(); + final var events = new PackerOperationEventEmitter(project, eventSink); + final var registry = snapshot.registry(); final Map registryByRoot = new HashMap<>(); - for (PackerRegistryEntry entry : registry.assets()) { + for (final var entry : registry.assets()) { registryByRoot.put(PackerWorkspacePaths.assetRoot(project, entry.root()), entry); } @@ -75,15 +77,15 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe final List runtimeAssets = snapshot.assets(); final int total = runtimeAssets.size(); for (int index = 0; index < runtimeAssets.size(); index += 1) { - final PackerRuntimeAsset runtimeAsset = runtimeAssets.get(index); - final Path assetRoot = runtimeAsset.assetRoot(); - final Path assetManifestPath = runtimeAsset.manifestPath(); + final var runtimeAsset = runtimeAssets.get(index); + final var assetRoot = runtimeAsset.assetRoot(); + final var assetManifestPath = runtimeAsset.manifestPath(); discoveredRoots.add(assetRoot); - final PackerRegistryEntry registryEntry = registryByRoot.get(assetRoot); - final PackerAssetDeclarationParseResult parsed = runtimeAsset.parsedDeclaration(); + final var registryEntry = registryByRoot.get(assetRoot); + final var parsed = runtimeAsset.parsedDeclaration(); diagnostics.addAll(parsed.diagnostics()); diagnostics.addAll(identityMismatchDiagnostics(registryEntry, parsed, assetManifestPath)); - final PackerAssetSummary summary = buildSummary(project, assetRoot, registryEntry, parsed); + final var summary = buildSummary(project, assetRoot, registryEntry, parsed); assets.add(summary); events.emit( PackerEventKind.ASSET_DISCOVERED, @@ -92,8 +94,8 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe List.of(summary.identity().assetName())); } - for (PackerRegistryEntry entry : registry.assets()) { - final Path registeredRoot = PackerWorkspacePaths.assetRoot(project, entry.root()); + for (final var entry : registry.assets()) { + final var registeredRoot = PackerWorkspacePaths.assetRoot(project, entry.root()); if (!discoveredRoots.contains(registeredRoot)) { diagnostics.add(new PackerDiagnostic( PackerDiagnosticSeverity.ERROR, @@ -107,7 +109,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe assets.sort(Comparator .comparing((PackerAssetSummary asset) -> asset.identity().assetRoot().toString(), String.CASE_INSENSITIVE_ORDER) .thenComparing(summary -> summary.identity().assetName(), String.CASE_INSENSITIVE_ORDER)); - final PackerOperationStatus status = diagnostics.stream().anyMatch(PackerDiagnostic::blocking) + final var status = diagnostics.stream().anyMatch(PackerDiagnostic::blocking) ? PackerOperationStatus.PARTIAL : PackerOperationStatus.SUCCESS; if (!diagnostics.isEmpty()) { diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetActionReadService.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetActionReadService.java index 728d3471..3a8a3c5f 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetActionReadService.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetActionReadService.java @@ -1,10 +1,12 @@ package p.packer.services; +import p.packer.PackerWorkspacePaths; import p.packer.messages.*; import p.packer.messages.assets.AssetAction; import p.packer.messages.diagnostics.PackerDiagnosticCategory; import p.packer.messages.diagnostics.PackerDiagnosticSeverity; import p.packer.models.*; +import p.packer.repositories.PackerRuntimeRegistry; import java.nio.file.Path; import java.util.ArrayList; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDeclarationParser.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDeclarationParser.java index 8c3c12ed..fa3ce882 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDeclarationParser.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDeclarationParser.java @@ -16,7 +16,7 @@ import java.nio.file.Path; import java.util.*; public final class PackerAssetDeclarationParser { - private static final int SUPPORTED_SCHEMA_VERSION = 1; + private static final Set SUPPORTED_SCHEMA_VERSIONS = Set.of(1); private final ObjectMapper mapper; @@ -40,17 +40,17 @@ public final class PackerAssetDeclarationParser { return new PackerAssetDeclarationParseResult(null, diagnostics); } - final Integer schemaVersion = requiredInt(root, "schema_version", diagnostics, manifestPath); - final String assetUuid = requiredText(root, "asset_uuid", diagnostics, manifestPath); - final String name = requiredText(root, "name", diagnostics, manifestPath); - final AssetFamilyCatalog assetFamily = requiredAssetFamily(root, diagnostics, manifestPath); - final Map> inputsByRole = requiredInputs(root.path("inputs"), diagnostics, manifestPath); - final OutputFormatCatalog outputFormat = requiredOutputFormat(root.path("output"), diagnostics, manifestPath); - final OutputCodecCatalog outputCodec = requiredOutputCodec(root.path("output"), diagnostics, manifestPath); - final Map outputMetadata = optionalOutputMetadata(root.path("output"), diagnostics, manifestPath); - final Boolean preloadEnabled = requiredBoolean(root.path("preload"), "enabled", diagnostics, manifestPath); + final var schemaVersion = requiredInt(root, "schema_version", diagnostics, manifestPath); + final var assetUuid = requiredText(root, "asset_uuid", diagnostics, manifestPath); + final var name = requiredText(root, "name", diagnostics, manifestPath); + final var assetFamily = requiredAssetFamily(root, diagnostics, manifestPath); + final var inputsByRole = requiredInputs(root.path("inputs"), diagnostics, manifestPath); + final var outputFormat = requiredOutputFormat(root.path("output"), diagnostics, manifestPath); + final var outputCodec = requiredOutputCodec(root.path("output"), diagnostics, manifestPath); + final var outputMetadata = optionalOutputMetadata(root.path("output"), diagnostics, manifestPath); + final var preloadEnabled = requiredBoolean(root.path("preload"), "enabled", diagnostics, manifestPath); - if (schemaVersion != null && schemaVersion != SUPPORTED_SCHEMA_VERSION) { + if (schemaVersion != null && !SUPPORTED_SCHEMA_VERSIONS.contains(schemaVersion)) { diagnostics.add(new PackerDiagnostic( PackerDiagnosticSeverity.ERROR, PackerDiagnosticCategory.VERSIONING, @@ -77,7 +77,11 @@ public final class PackerAssetDeclarationParser { diagnostics); } - private Integer requiredInt(JsonNode node, String fieldName, List diagnostics, Path manifestPath) { + private Integer requiredInt( + final JsonNode node, + final String fieldName, + final List diagnostics, + final Path manifestPath) { final JsonNode field = node.path(fieldName); if (!field.isInt()) { diagnostics.add(missingOrInvalid(fieldName, "integer", manifestPath)); @@ -86,7 +90,11 @@ public final class PackerAssetDeclarationParser { return field.intValue(); } - private String requiredText(JsonNode node, String fieldName, List diagnostics, Path manifestPath) { + private String requiredText( + final JsonNode node, + final String fieldName, + final List diagnostics, + final Path manifestPath) { final JsonNode field = node.path(fieldName); if (!field.isTextual() || field.asText().isBlank()) { diagnostics.add(missingOrInvalid(fieldName, "non-blank string", manifestPath)); @@ -95,7 +103,10 @@ public final class PackerAssetDeclarationParser { return field.asText().trim(); } - private AssetFamilyCatalog requiredAssetFamily(JsonNode node, List diagnostics, Path manifestPath) { + private AssetFamilyCatalog requiredAssetFamily( + final JsonNode node, + final List diagnostics, + final Path manifestPath) { final String manifestType = requiredText(node, "type", diagnostics, manifestPath); if (manifestType == null) { return null; @@ -113,7 +124,11 @@ public final class PackerAssetDeclarationParser { return assetFamily; } - private Boolean requiredBoolean(JsonNode node, String fieldName, List diagnostics, Path manifestPath) { + private Boolean requiredBoolean( + final JsonNode node, + final String fieldName, + final List diagnostics, + final Path manifestPath) { final JsonNode field = node.path(fieldName); if (!field.isBoolean()) { diagnostics.add(missingOrInvalid(fieldName, "boolean", manifestPath)); @@ -122,7 +137,10 @@ public final class PackerAssetDeclarationParser { return field.booleanValue(); } - private OutputFormatCatalog requiredOutputFormat(JsonNode node, List diagnostics, Path manifestPath) { + private OutputFormatCatalog requiredOutputFormat( + final JsonNode node, + final List diagnostics, + final Path manifestPath) { final String fmtValue = requiredText(node, "format", diagnostics, manifestPath); if (fmtValue == null) { return null; @@ -140,7 +158,10 @@ public final class PackerAssetDeclarationParser { return outputFormat; } - private OutputCodecCatalog requiredOutputCodec(JsonNode node, List diagnostics, Path manifestPath) { + private OutputCodecCatalog requiredOutputCodec( + final JsonNode node, + final List diagnostics, + final Path manifestPath) { final String codecValue = requiredText(node, "codec", diagnostics, manifestPath); if (codecValue == null) { return null; @@ -159,10 +180,10 @@ public final class PackerAssetDeclarationParser { } private Map optionalOutputMetadata( - JsonNode outputNode, - List diagnostics, - Path manifestPath) { - final JsonNode metadataNode = outputNode.path("metadata"); + final JsonNode node, + final List diagnostics, + final Path manifestPath) { + final JsonNode metadataNode = node.path("metadata"); if (metadataNode.isMissingNode() || metadataNode.isNull()) { return Map.of(); } @@ -205,14 +226,17 @@ public final class PackerAssetDeclarationParser { return Map.copyOf(metadata); } - private Map> requiredInputs(JsonNode inputsNode, List diagnostics, Path manifestPath) { - if (!inputsNode.isObject()) { + private Map> requiredInputs( + final JsonNode node, + final List diagnostics, + final Path manifestPath) { + if (!node.isObject()) { diagnostics.add(missingOrInvalid("inputs", "object of input roles", manifestPath)); return Map.of(); } final Map> result = new LinkedHashMap<>(); - inputsNode.fields().forEachRemaining(entry -> { + node.fields().forEachRemaining(entry -> { if (!entry.getValue().isArray()) { diagnostics.add(new PackerDiagnostic( PackerDiagnosticSeverity.ERROR, @@ -250,12 +274,15 @@ public final class PackerAssetDeclarationParser { return Map.copyOf(result); } - private boolean isTrustedRelativePath(String value) { + private boolean isTrustedRelativePath(final String value) { final Path path = Path.of(value).normalize(); return !path.isAbsolute() && !path.startsWith(".."); } - private PackerDiagnostic missingOrInvalid(String fieldName, String expected, Path manifestPath) { + private PackerDiagnostic missingOrInvalid( + final String fieldName, + final String expected, + final Path manifestPath) { return new PackerDiagnostic( PackerDiagnosticSeverity.ERROR, PackerDiagnosticCategory.STRUCTURAL, diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDetailsService.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDetailsService.java index d8be1968..a4c9de90 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDetailsService.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetDetailsService.java @@ -1,10 +1,12 @@ package p.packer.services; +import p.packer.PackerWorkspacePaths; import p.packer.messages.*; import p.packer.messages.assets.*; import p.packer.messages.diagnostics.PackerDiagnosticCategory; import p.packer.messages.diagnostics.PackerDiagnosticSeverity; import p.packer.models.*; +import p.packer.repositories.PackerRuntimeRegistry; import java.nio.file.Path; import java.util.*; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetReferenceResolver.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetReferenceResolver.java index b7024d2c..99eedf44 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetReferenceResolver.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerAssetReferenceResolver.java @@ -1,5 +1,6 @@ package p.packer.services; +import p.packer.PackerWorkspacePaths; import p.packer.messages.AssetReference; import p.packer.messages.PackerProjectContext; import p.packer.messages.diagnostics.PackerDiagnosticCategory; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerIdentityAllocator.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerIdentityAllocator.java index 55e3c664..4e621835 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerIdentityAllocator.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerIdentityAllocator.java @@ -1,5 +1,6 @@ package p.packer.services; +import p.packer.PackerWorkspacePaths; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRegistryEntry; import p.packer.models.PackerRegistryState; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerOutputContractCatalog.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerOutputContractCatalog.java index a30062f0..8c67f06b 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerOutputContractCatalog.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerOutputContractCatalog.java @@ -58,13 +58,13 @@ final class PackerOutputContractCatalog { "tile_size", "TileSize", PackerCodecConfigurationFieldType.ENUM, - "16", + "16x16", true, - List.of("8", "16", "32"))); + List.of("8x8", "16x16", "32x32"))); case SOUND_V1 -> List.of( new PackerCodecConfigurationField( "sample_rate", - "Frame Rate", + "Sample Rate", PackerCodecConfigurationFieldType.ENUM, "44100", true, diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryLookup.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryLookup.java index 1cb608d0..ff4924a3 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryLookup.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerRegistryLookup.java @@ -1,5 +1,6 @@ package p.packer.services; +import p.packer.PackerWorkspacePaths; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRegistryEntry; import p.packer.models.PackerRegistryState; diff --git a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspaceFoundation.java b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspaceFoundation.java index fd2fa665..e9d17e66 100644 --- a/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspaceFoundation.java +++ b/prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/PackerWorkspaceFoundation.java @@ -1,11 +1,14 @@ package p.packer.services; +import com.fasterxml.jackson.databind.ObjectMapper; +import p.packer.PackerWorkspacePaths; import p.packer.messages.InitWorkspaceRequest; import p.packer.messages.InitWorkspaceResult; import p.packer.messages.PackerOperationStatus; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRegistryEntry; import p.packer.models.PackerRegistryState; +import p.packer.repositories.FileSystemPackerRegistryRepository; import java.nio.file.Files; import java.nio.file.Path; @@ -17,8 +20,8 @@ public final class PackerWorkspaceFoundation { private final PackerIdentityAllocator identityAllocator; private final PackerRegistryLookup registryLookup; - public PackerWorkspaceFoundation() { - this(new FileSystemPackerRegistryRepository(), new PackerIdentityAllocator(), new PackerRegistryLookup()); + public PackerWorkspaceFoundation(final ObjectMapper mapper) { + this(new FileSystemPackerRegistryRepository(mapper), new PackerIdentityAllocator(), new PackerRegistryLookup()); } public PackerWorkspaceFoundation( diff --git a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/FileSystemPackerWorkspaceServiceTest.java b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/FileSystemPackerWorkspaceServiceTest.java index 420c7e5e..a08f6fd7 100644 --- a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/FileSystemPackerWorkspaceServiceTest.java +++ b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/FileSystemPackerWorkspaceServiceTest.java @@ -7,6 +7,10 @@ import p.packer.events.PackerEvent; import p.packer.events.PackerEventKind; import p.packer.messages.*; import p.packer.messages.assets.*; +import p.packer.repositories.PackerAssetWalker; +import p.packer.repositories.PackerRuntimeLoader; +import p.packer.repositories.PackerRuntimeRegistry; +import p.packer.repositories.PackerRuntimeSnapshotLoader; import p.packer.testing.PackerFixtureLocator; import java.nio.file.Files; @@ -193,7 +197,7 @@ final class FileSystemPackerWorkspaceServiceTest { "NONE:packMode", "tight", "NONE:palette", "mono"), Map.of( - "tile_size", "16", + "tile_size", "16x16", "channels", "1"))); assertTrue(result.success()); @@ -205,7 +209,7 @@ final class FileSystemPackerWorkspaceServiceTest { assertEquals("NONE", manifest.path("output").path("codec").asText()); assertEquals("tight", manifest.path("output").path("codec_configuration").path("packMode").asText()); assertEquals("mono", manifest.path("output").path("codec_configuration").path("palette").asText()); - assertEquals("16", manifest.path("output").path("metadata").path("tile_size").asText()); + assertEquals("16x16", manifest.path("output").path("metadata").path("tile_size").asText()); assertEquals("1", manifest.path("output").path("metadata").path("channels").asText()); } @@ -672,9 +676,10 @@ final class FileSystemPackerWorkspaceServiceTest { private FileSystemPackerWorkspaceService service( p.packer.events.PackerEventSink eventSink, PackerRuntimeSnapshotLoader loader) { - final var foundation = new p.packer.services.PackerWorkspaceFoundation(); - final var parser = new p.packer.services.PackerAssetDeclarationParser(new ObjectMapper()); - final var runtimeRegistry = new p.packer.services.PackerRuntimeRegistry(loader); + final var mapper = new ObjectMapper(); + final var foundation = new p.packer.services.PackerWorkspaceFoundation(mapper); + final var parser = new p.packer.services.PackerAssetDeclarationParser(mapper); + final var runtimeRegistry = new PackerRuntimeRegistry(loader); final var resolver = new p.packer.services.PackerAssetReferenceResolver(foundation.lookup()); final var detailsService = new p.packer.services.PackerAssetDetailsService(runtimeRegistry, resolver); final var actionReadService = new p.packer.services.PackerAssetActionReadService(runtimeRegistry, resolver, foundation.lookup()); @@ -692,9 +697,11 @@ final class FileSystemPackerWorkspaceServiceTest { } private CountingLoader countingLoader() { - final var foundation = new p.packer.services.PackerWorkspaceFoundation(); - final var parser = new p.packer.services.PackerAssetDeclarationParser(new ObjectMapper()); - return new CountingLoader(new p.packer.services.PackerRuntimeLoader(foundation, parser)); + final var mapper = new ObjectMapper(); + final var foundation = new p.packer.services.PackerWorkspaceFoundation(mapper); + final var parser = new p.packer.services.PackerAssetDeclarationParser(mapper); + final var assetWalker = new PackerAssetWalker(mapper); + return new CountingLoader(new PackerRuntimeLoader(foundation, parser, assetWalker)); } private static final class CountingLoader implements PackerRuntimeSnapshotLoader { diff --git a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerAssetDetailsServiceTest.java b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerAssetDetailsServiceTest.java index 0ecd0456..e5c9c60e 100644 --- a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerAssetDetailsServiceTest.java +++ b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerAssetDetailsServiceTest.java @@ -11,6 +11,9 @@ import p.packer.messages.PackerProjectContext; import p.packer.messages.assets.OutputCodecCatalog; import p.packer.messages.assets.PackerAssetState; import p.packer.messages.assets.PackerBuildParticipation; +import p.packer.repositories.PackerAssetWalker; +import p.packer.repositories.PackerRuntimeLoader; +import p.packer.repositories.PackerRuntimeRegistry; import p.packer.testing.PackerFixtureLocator; import java.nio.file.Files; @@ -68,7 +71,7 @@ final class PackerAssetDetailsServiceTest { final var manifest = mapper.readTree(manifestPath.toFile()); final ObjectNode output = (ObjectNode) manifest.path("output"); final ObjectNode metadata = output.putObject("metadata"); - metadata.put("tile_size", "16"); + metadata.put("tile_size", "16x16"); metadata.put("channels", "1"); mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest); @@ -77,7 +80,7 @@ final class PackerAssetDetailsServiceTest { assertEquals(PackerOperationStatus.SUCCESS, result.status()); assertEquals( - Map.of("tile_size", "16", "channels", "1"), + Map.of("tile_size", "16x16", "channels", "1"), result.details().metadataFields().stream().collect(java.util.stream.Collectors.toMap( field -> field.key(), field -> field.value()))); @@ -130,9 +133,11 @@ final class PackerAssetDetailsServiceTest { } private PackerAssetDetailsService service() { - final var foundation = new p.packer.services.PackerWorkspaceFoundation(); - final var parser = new PackerAssetDeclarationParser(new ObjectMapper()); - final var runtimeRegistry = new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser)); + final var mapper = new ObjectMapper(); + final var foundation = new PackerWorkspaceFoundation(mapper); + final var parser = new PackerAssetDeclarationParser(mapper); + final var assetWalker = new PackerAssetWalker(mapper); + final var runtimeRegistry = new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser, assetWalker)); final var resolver = new PackerAssetReferenceResolver(foundation.lookup()); return new PackerAssetDetailsService(runtimeRegistry, resolver); } diff --git a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerRuntimeRegistryTest.java b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerRuntimeRegistryTest.java index b6b44862..24ab8722 100644 --- a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerRuntimeRegistryTest.java +++ b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerRuntimeRegistryTest.java @@ -4,6 +4,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import p.packer.messages.PackerProjectContext; +import p.packer.repositories.PackerAssetWalker; +import p.packer.repositories.PackerRuntimeLoader; +import p.packer.repositories.PackerRuntimeRegistry; import p.packer.testing.PackerFixtureLocator; import java.nio.file.Files; @@ -78,9 +81,11 @@ final class PackerRuntimeRegistryTest { } private PackerRuntimeRegistry runtimeRegistry() { - final var foundation = new PackerWorkspaceFoundation(); - final var parser = new PackerAssetDeclarationParser(new ObjectMapper()); - return new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser)); + final var mapper = new ObjectMapper(); + final var foundation = new PackerWorkspaceFoundation(mapper); + final var parser = new PackerAssetDeclarationParser(mapper); + final var assetWalker = new PackerAssetWalker(mapper); + return new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser, assetWalker)); } private PackerProjectContext project(Path root) { diff --git a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerWorkspaceFoundationTest.java b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerWorkspaceFoundationTest.java index 4f2e8f42..05e80089 100644 --- a/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerWorkspaceFoundationTest.java +++ b/prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/PackerWorkspaceFoundationTest.java @@ -1,11 +1,13 @@ package p.packer.services; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import p.packer.messages.InitWorkspaceRequest; import p.packer.messages.PackerProjectContext; import p.packer.models.PackerRegistryEntry; import p.packer.models.PackerRegistryState; +import p.packer.repositories.FileSystemPackerRegistryRepository; import java.nio.file.Files; import java.nio.file.Path; @@ -18,7 +20,8 @@ final class PackerWorkspaceFoundationTest { @Test void initWorkspaceCreatesAssetsControlStructureAndRegistry() throws Exception { - final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(); + final var mapper = new ObjectMapper(); + final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(mapper); final PackerProjectContext project = project(tempDir.resolve("main")); final var result = foundation.initWorkspace(new InitWorkspaceRequest(project)); @@ -35,7 +38,8 @@ final class PackerWorkspaceFoundationTest { @Test void registryRoundTripPreservesAllocatorAndEntries() throws Exception { final Path projectRoot = tempDir.resolve("main"); - final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(); + final var mapper = new ObjectMapper(); + final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(mapper); final PackerProjectContext project = project(projectRoot); foundation.initWorkspace(new InitWorkspaceRequest(project)); @@ -58,7 +62,8 @@ final class PackerWorkspaceFoundationTest { @Test void allocatorIsMonotonicAndPersistedAcrossSaveLoad() throws Exception { final Path projectRoot = tempDir.resolve("main"); - final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(); + final var mapper = new ObjectMapper(); + final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(mapper); final PackerProjectContext project = project(projectRoot); foundation.initWorkspace(new InitWorkspaceRequest(project)); Files.createDirectories(projectRoot.resolve("assets/ui/atlas")); @@ -89,7 +94,8 @@ final class PackerWorkspaceFoundationTest { ] } """); - final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(); + final var mapper = new ObjectMapper(); + final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(mapper); final p.packer.exceptions.PackerRegistryException exception = assertThrows(p.packer.exceptions.PackerRegistryException.class, () -> repository.load(project(projectRoot))); @@ -101,7 +107,8 @@ final class PackerWorkspaceFoundationTest { final Path projectRoot = tempDir.resolve("main"); Files.createDirectories(projectRoot.resolve("assets/.prometeu")); Files.writeString(projectRoot.resolve("assets/.prometeu/index.json"), "{ nope "); - final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(); + final var mapper = new ObjectMapper(); + final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(mapper); final p.packer.exceptions.PackerRegistryException exception = assertThrows(p.packer.exceptions.PackerRegistryException.class, () -> repository.load(project(projectRoot))); @@ -119,7 +126,8 @@ final class PackerWorkspaceFoundationTest { "assets": [] } """); - final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(); + final var mapper = new ObjectMapper(); + final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(mapper); final p.packer.exceptions.PackerRegistryException exception = assertThrows(p.packer.exceptions.PackerRegistryException.class, () -> repository.load(project(projectRoot))); @@ -139,7 +147,8 @@ final class PackerWorkspaceFoundationTest { ] } """); - final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(); + final var mapper = new ObjectMapper(); + final FileSystemPackerRegistryRepository repository = new FileSystemPackerRegistryRepository(mapper); final p.packer.exceptions.PackerRegistryException exception = assertThrows(p.packer.exceptions.PackerRegistryException.class, () -> repository.load(project(projectRoot))); @@ -149,7 +158,8 @@ final class PackerWorkspaceFoundationTest { @Test void lookupResolvesByIdUuidAndRootAndFailsOnMissingRoot() throws Exception { final Path projectRoot = tempDir.resolve("main"); - final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(); + final var mapper = new ObjectMapper(); + final PackerWorkspaceFoundation foundation = new PackerWorkspaceFoundation(mapper); final PackerProjectContext project = project(projectRoot); foundation.initWorkspace(new InitWorkspaceRequest(project)); Files.createDirectories(projectRoot.resolve("assets/ui/atlas")); diff --git a/test-projects/main/.studio/activities.json b/test-projects/main/.studio/activities.json index 9b0fcd65..ab1bb565 100644 --- a/test-projects/main/.studio/activities.json +++ b/test-projects/main/.studio/activities.json @@ -48,6 +48,636 @@ "message" : "7 assets loaded", "severity" : "SUCCESS", "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "7 assets loaded", + "severity" : "SUCCESS", + "sticky" : false }, { "source" : "Assets", "message" : "Asset scan diagnostics updated.", @@ -1868,634 +2498,4 @@ "message" : "Discovered asset: test", "severity" : "INFO", "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset deleted: recovered/new asset", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "8 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: new asset", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "8 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: new asset", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset deleted: recovered/new asset", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "8 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: new asset", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "8 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: new asset", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: test", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false } ] \ No newline at end of file diff --git a/test-projects/main/assets/bigode/asset.json b/test-projects/main/assets/bigode/asset.json index cefac2dd..f3be3233 100644 --- a/test-projects/main/assets/bigode/asset.json +++ b/test-projects/main/assets/bigode/asset.json @@ -9,7 +9,7 @@ "codec" : "NONE", "codec_configuration" : { }, "metadata" : { - "tile_size" : "16" + "tile_size" : "16x16" } }, "preload" : { diff --git a/test-projects/main/assets/recovered/atlas2/confirm.palette.json b/test-projects/main/assets/recovered/atlas2/confirm.palette.json new file mode 100644 index 00000000..33afab5a --- /dev/null +++ b/test-projects/main/assets/recovered/atlas2/confirm.palette.json @@ -0,0 +1,4 @@ +{ + "convertedRgb565" : [ 65414 ], + "originalArgb8888" : [ -265674 ] +} \ No newline at end of file diff --git a/test-projects/main/assets/recovered/atlas2/sprites/confirm.png b/test-projects/main/assets/recovered/atlas2/confirm.png similarity index 100% rename from test-projects/main/assets/recovered/atlas2/sprites/confirm.png rename to test-projects/main/assets/recovered/atlas2/confirm.png diff --git a/test-projects/main/assets/recovered/atlas2/confirm.tile.json b/test-projects/main/assets/recovered/atlas2/confirm.tile.json new file mode 100644 index 00000000..ca73da31 --- /dev/null +++ b/test-projects/main/assets/recovered/atlas2/confirm.tile.json @@ -0,0 +1,5 @@ +{ + "height" : 16, + "width" : 16, + "paletteIndices" : [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ] +} \ No newline at end of file diff --git a/test-projects/main/assets/ui/atlas2/asset.json b/test-projects/main/assets/ui/atlas2/asset.json index e6f0f63b..7ba38262 100644 --- a/test-projects/main/assets/ui/atlas2/asset.json +++ b/test-projects/main/assets/ui/atlas2/asset.json @@ -3,7 +3,7 @@ "asset_uuid": "21953cb8-4101-4790-9e5e-d95f5fbc9b5a", "name": "ui_atlas", "type": "tile_bank", - "inputs": { "sprites": ["sprites/confirm.png"] }, + "inputs": { }, "output": { "format": "TILES/indexed_v1", "codec": "NONE" }, "preload": { "enabled": true } } diff --git a/test-projects/main/assets/ui/atlas2/confirm.palette.json b/test-projects/main/assets/ui/atlas2/confirm.palette.json new file mode 100644 index 00000000..33afab5a --- /dev/null +++ b/test-projects/main/assets/ui/atlas2/confirm.palette.json @@ -0,0 +1,4 @@ +{ + "convertedRgb565" : [ 65414 ], + "originalArgb8888" : [ -265674 ] +} \ No newline at end of file diff --git a/test-projects/main/assets/ui/atlas2/sprites/confirm.png b/test-projects/main/assets/ui/atlas2/confirm.png similarity index 100% rename from test-projects/main/assets/ui/atlas2/sprites/confirm.png rename to test-projects/main/assets/ui/atlas2/confirm.png diff --git a/test-projects/main/assets/ui/atlas2/confirm.tile.json b/test-projects/main/assets/ui/atlas2/confirm.tile.json new file mode 100644 index 00000000..ca73da31 --- /dev/null +++ b/test-projects/main/assets/ui/atlas2/confirm.tile.json @@ -0,0 +1,5 @@ +{ + "height" : 16, + "width" : 16, + "paletteIndices" : [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 ] +} \ No newline at end of file