Asset Entry Metadata Normalization Contract
This commit is contained in:
parent
9228fb1e29
commit
52d4a91c71
@ -186,29 +186,22 @@ impl AssetManager {
|
||||
fn decode_tile_bank_layout(
|
||||
entry: &AssetEntry,
|
||||
) -> Result<(TileSize, usize, usize, usize), String> {
|
||||
let tile_size_val =
|
||||
entry.metadata.get("tile_size").and_then(|v| v.as_u64()).ok_or("Missing tile_size")?;
|
||||
let width =
|
||||
entry.metadata.get("width").and_then(|v| v.as_u64()).ok_or("Missing width")? as usize;
|
||||
let height =
|
||||
entry.metadata.get("height").and_then(|v| v.as_u64()).ok_or("Missing height")? as usize;
|
||||
let palette_count = entry
|
||||
.metadata
|
||||
.get("palette_count")
|
||||
.and_then(|v| v.as_u64())
|
||||
.ok_or("Missing palette_count")? as usize;
|
||||
let meta = entry.metadata_as_tiles()?;
|
||||
|
||||
let tile_size = match tile_size_val {
|
||||
let tile_size = match meta.tile_size {
|
||||
8 => TileSize::Size8,
|
||||
16 => TileSize::Size16,
|
||||
32 => TileSize::Size32,
|
||||
_ => return Err(format!("Invalid tile_size: {}", tile_size_val)),
|
||||
_ => return Err(format!("Invalid tile_size: {}", meta.tile_size)),
|
||||
};
|
||||
|
||||
if palette_count != TILE_BANK_PALETTE_COUNT_V1 {
|
||||
return Err(format!("Invalid palette_count: {}", palette_count));
|
||||
if meta.palette_count as usize != TILE_BANK_PALETTE_COUNT_V1 {
|
||||
return Err(format!("Invalid palette_count: {}", meta.palette_count));
|
||||
}
|
||||
|
||||
let width = meta.width as usize;
|
||||
let height = meta.height as usize;
|
||||
|
||||
let logical_pixels = width.checked_mul(height).ok_or("TileBank dimensions overflow")?;
|
||||
let serialized_pixel_bytes = logical_pixels.div_ceil(2);
|
||||
let serialized_size = serialized_pixel_bytes
|
||||
@ -637,8 +630,8 @@ impl AssetManager {
|
||||
entry: &AssetEntry,
|
||||
buffer: &[u8],
|
||||
) -> Result<SoundBank, String> {
|
||||
let sample_rate =
|
||||
entry.metadata.get("sample_rate").and_then(|v| v.as_u64()).unwrap_or(44100) as u32;
|
||||
let meta = entry.metadata_as_sounds()?;
|
||||
let sample_rate = meta.sample_rate;
|
||||
|
||||
let mut data = Vec::with_capacity(buffer.len() / 2);
|
||||
for i in (0..buffer.len()).step_by(2) {
|
||||
|
||||
@ -24,6 +24,43 @@ pub struct AssetEntry {
|
||||
pub metadata: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct TilesMetadata {
|
||||
pub tile_size: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub palette_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct SoundsMetadata {
|
||||
pub sample_rate: u32,
|
||||
}
|
||||
|
||||
impl AssetEntry {
|
||||
pub fn metadata_as_tiles(&self) -> Result<TilesMetadata, String> {
|
||||
if self.bank_type != BankType::TILES {
|
||||
return Err(format!(
|
||||
"Asset {} is not a TILES bank (type: {:?})",
|
||||
self.asset_id, self.bank_type
|
||||
));
|
||||
}
|
||||
serde_json::from_value(self.metadata.clone())
|
||||
.map_err(|e| format!("Invalid TILES metadata for asset {}: {}", self.asset_id, e))
|
||||
}
|
||||
|
||||
pub fn metadata_as_sounds(&self) -> Result<SoundsMetadata, String> {
|
||||
if self.bank_type != BankType::SOUNDS {
|
||||
return Err(format!(
|
||||
"Asset {} is not a SOUNDS bank (type: {:?})",
|
||||
self.asset_id, self.bank_type
|
||||
));
|
||||
}
|
||||
serde_json::from_value(self.metadata.clone())
|
||||
.map_err(|e| format!("Invalid SOUNDS metadata for asset {}: {}", self.asset_id, e))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct PreloadEntry {
|
||||
pub asset_id: AssetId,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":3,"PLN":3,"LSN":23,"CLSN":1}}
|
||||
{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":5,"PLN":3,"LSN":24,"CLSN":1}}
|
||||
... (mantendo as linhas anteriores) ...
|
||||
{"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
||||
@ -17,6 +17,6 @@
|
||||
{"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]}
|
||||
{"type":"discussion","id":"DSC-0017","status":"open","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0017","status":"done","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0004","file":"workflow/decisions/DEC-0004-asset-entry-metadata-normalization-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"plans":[],"lessons":[{"id":"LSN-0023","file":"lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]}
|
||||
{"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
|
||||
{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"workflow/agendas/AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"workflow/decisions/DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"workflow/plans/PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
# LSN-0023: Typed Helpers for Asset Metadata
|
||||
|
||||
Status: Published
|
||||
Decisions: [[DEC-0004]]
|
||||
Tags: [asset, runtime, rust, pattern]
|
||||
|
||||
## Contexto
|
||||
|
||||
A decisão [[DEC-0004]] estabeleceu um contrato de metadados segmentados para assets, mantendo campos críticos na raiz do JSON e detalhes técnicos em subárvores (`codec`, `pipeline`). No entanto, o `AssetEntry.metadata` no runtime é um `serde_json::Value` dinâmico.
|
||||
|
||||
## Lição
|
||||
|
||||
O uso de `serde_json::Value` diretamente nos loaders do runtime introduz riscos de runtime (erros de tipo, campos ausentes) e polui o código com chamadas repetitivas de `.get()`, `.as_u64()`, etc.
|
||||
|
||||
### Abordagem Adotada
|
||||
|
||||
Para mitigar isso, implementamos o padrão de **Typed Metadata Helpers**:
|
||||
|
||||
1. **Structs Dedicadas**: Criamos structs Rust (ex: `TilesMetadata`, `SoundsMetadata`) que representam o contrato exato de cada banco.
|
||||
2. **Conversion Methods**: Adicionamos métodos ao `AssetEntry` (ex: `metadata_as_tiles()`) que utilizam `serde_json::from_value` para realizar o "cast" do JSON dinâmico para a struct tipada.
|
||||
3. **Fail-Fast**: A falha no parsing dos metadados deve ser tratada como erro de carregamento do asset, garantindo que o motor não opere com metadados corrompidos ou incompletos.
|
||||
|
||||
### Benefícios
|
||||
|
||||
- **Segurança de Tipo**: Erros de estrutura de metadados são detectados no momento do carregamento.
|
||||
- **Ergonomia**: O código dos drivers passa a usar `meta.tile_size` em vez de parsing manual.
|
||||
- **Desacoplamento**: A complexidade do JSON fica encapsulada nos helpers de conversão.
|
||||
|
||||
## Referências
|
||||
|
||||
- DEC-0004: Asset Entry Metadata Normalization Contract
|
||||
- `prometeu-hal/src/asset.rs`
|
||||
- `prometeu-drivers/src/asset.rs`
|
||||
@ -1,100 +0,0 @@
|
||||
---
|
||||
id: AGD-0016
|
||||
ticket: asset-entry-metadata-normalization-contract
|
||||
title: Asset Entry Metadata Normalization Contract
|
||||
status: open
|
||||
created: 2026-03-27
|
||||
resolved:
|
||||
decision:
|
||||
tags: []
|
||||
---
|
||||
|
||||
# Asset Entry Metadata Normalization Contract
|
||||
|
||||
Status: Open
|
||||
Domain Owner: `docs/runtime`
|
||||
Cross-Domain Impact: `../studio/docs/packer`, `shipper`, `asset` loader
|
||||
|
||||
## Purpose
|
||||
|
||||
Normatizar como `AssetEntry.metadata` deve preservar a convergencia entre metadata autoral, metadata de codec e metadata de pipeline sem colapsar tudo num mapa plano ambiguo.
|
||||
|
||||
## Problem
|
||||
|
||||
O lado produtor (`packer`) ja convergiu para um contrato em que o runtime precisa ler campos obrigatorios diretamente de `AssetEntry.metadata`, mas tambem precisa manter segmentacao suficiente para nao perder significado entre:
|
||||
|
||||
- `asset.json.output.metadata`
|
||||
- `asset.json.output.codec_configuration`
|
||||
- `asset.json.output.pipeline`
|
||||
|
||||
Sem um contrato explicito no runtime:
|
||||
|
||||
- o packer pode materializar estruturas diferentes entre formatos;
|
||||
- o loader/runtime pode passar a depender de flattening incidental;
|
||||
- tooling e debug surfaces perdem previsibilidade;
|
||||
- futuros formatos tendem a misturar metadata efetiva com detalhe interno de pipeline.
|
||||
|
||||
## Context
|
||||
|
||||
No ciclo atual de `tile bank`, o produtor ja fechou esta direcao:
|
||||
|
||||
- `asset.json.output.metadata` -> `AssetEntry.metadata`
|
||||
- `asset.json.output.codec_configuration` -> `AssetEntry.metadata.codec`
|
||||
- `asset.json.output.pipeline` -> `AssetEntry.metadata.pipeline`
|
||||
|
||||
Ao mesmo tempo, o runtime ainda consome alguns campos obrigatorios do tile bank diretamente no nivel raiz de `AssetEntry.metadata`, em especial:
|
||||
|
||||
- `tile_size`
|
||||
- `width`
|
||||
- `height`
|
||||
- `palette_count`
|
||||
|
||||
A agenda precisa fechar se esse shape vira contrato geral de runtime para metadata normalizada de assets, e como o consumidor deve tratar campos obrigatorios format-specific versus subtrees segmentadas.
|
||||
|
||||
## Options
|
||||
|
||||
### Option A - Flat effective metadata map only
|
||||
|
||||
Tudo converge para um unico mapa plano em `AssetEntry.metadata`.
|
||||
|
||||
### Option B - Root effective metadata plus stable segmented subtrees
|
||||
|
||||
Campos runtime-obrigatorios ficam legiveis no nivel raiz, enquanto dados de codec e pipeline ficam em subtrees estaveis:
|
||||
|
||||
- `metadata.<field>`
|
||||
- `metadata.codec.<field>`
|
||||
- `metadata.pipeline.<field>`
|
||||
|
||||
### Option C - Fully segmented metadata only
|
||||
|
||||
Nada fica no nivel raiz; todo consumo passa por subtrees por origem.
|
||||
|
||||
## Tradeoffs
|
||||
|
||||
- Option A simplifica leitura curta, mas perde origem semantica e aumenta risco de colisao.
|
||||
- Option B preserva leitura direta para campos obrigatorios do runtime e mantem segmentacao estavel para evolucao futura.
|
||||
- Option C e semanticamente limpa, mas quebra o consumo direto atual de formatos como `tile bank` e introduz custo de migracao desnecessario agora.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Adotar `Option B`.
|
||||
|
||||
Direcao recomendada:
|
||||
|
||||
- campos format-specific obrigatorios para decode/runtime continuam legiveis no nivel raiz de `AssetEntry.metadata`;
|
||||
- `output.codec_configuration` materializa em `AssetEntry.metadata.codec`;
|
||||
- `output.pipeline` materializa em `AssetEntry.metadata.pipeline`;
|
||||
- o runtime nao deve exigir flattening total para consumir metadata segmentada;
|
||||
- specs format-specific devem declarar explicitamente quais campos sao obrigatorios no nivel raiz.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. O contrato deve tratar o subtree raiz como semanticamente equivalente a `output.metadata` ou como effective metadata map mais amplo?
|
||||
2. Quais readers/helpers do runtime devem ser criados para evitar parsing manual disperso de `metadata.codec` e `metadata.pipeline`?
|
||||
|
||||
## Expected Follow-up
|
||||
|
||||
1. Converter esta agenda em decision no `runtime`.
|
||||
2. Propagar a decisao para `15-asset-management.md`.
|
||||
3. Ajustar loaders format-specific para usar helpers consistentes de metadata quando necessario.
|
||||
4. Alinhar o `packer` e testes de conformance com o shape final.
|
||||
@ -110,37 +110,46 @@ This table describes content identity and storage layout, not live residency.
|
||||
|
||||
### 4.1 `TILES` asset contract in v1
|
||||
|
||||
For `BankType::TILES`, the runtime-facing v1 contract is:
|
||||
Para `BankType::TILES`, o contrato v1 voltado para o runtime é:
|
||||
|
||||
- `codec = NONE`
|
||||
- serialized pixels use packed `u4` palette indices
|
||||
- serialized palettes use `RGB565` (`u16`, little-endian)
|
||||
- pixels serializados usam índices de paleta `u4` empacotados
|
||||
- paletas serializadas usam `RGB565` (`u16`, little-endian)
|
||||
- `palette_count = 64`
|
||||
- runtime materialization may expand pixel indices to one `u8` per pixel
|
||||
- a materialização em runtime pode expandir índices de pixel para um `u8` por pixel
|
||||
|
||||
`NONE` for `TILES` means there is no additional generic codec layer beyond the bank contract itself.
|
||||
`NONE` para `TILES` significa que não há camada de codec genérica adicional além do próprio contrato do banco.
|
||||
|
||||
For the current transition window:
|
||||
Para a janela de transição atual:
|
||||
|
||||
- `RAW` is a deprecated legacy alias of `NONE`
|
||||
- new published material must use `NONE` as the canonical value
|
||||
- `RAW` é um alias legado e depreciado de `NONE`
|
||||
- novos materiais publicados devem usar `NONE` como valor canônico
|
||||
|
||||
Even with `codec = NONE`, `TILES` still requires deterministic bank-specific decode from its serialized payload. The serialized byte layout is therefore not required to be identical to the in-memory layout.
|
||||
Mesmo com `codec = NONE`, `TILES` ainda requer decode específico de banco a partir de seu payload serializado. O layout de bytes serializados não precisa, portanto, ser idêntico ao layout em memória.
|
||||
|
||||
Required `AssetEntry.metadata` fields for `TILES`:
|
||||
#### 4.1.1 Metadata Normalization
|
||||
|
||||
- `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32`
|
||||
- `width`: full bank sheet width in pixels
|
||||
- `height`: full bank sheet height in pixels
|
||||
- `palette_count`: number of palettes serialized for the bank
|
||||
Seguindo a `DEC-0004`, o campo `AssetEntry.metadata` deve ser estruturado de forma segmentada para evitar ambiguidades.
|
||||
|
||||
Validation rules for `TILES` v1:
|
||||
Campos de metadados obrigatórios (efetivos) para `TILES` no nível raiz:
|
||||
|
||||
- `palette_count` must be `64`
|
||||
- `width * height` defines the number of logical indexed pixels in the decoded sheet
|
||||
- additional metadata may exist, but the runtime contract must not depend on it to reconstruct the bank in memory
|
||||
- `tile_size`: aresta do tile em pixels; valores válidos são `8`, `16`, ou `32`
|
||||
- `width`: largura total da folha do banco em pixels
|
||||
- `height`: altura total da folha do banco em pixels
|
||||
- `palette_count`: número de paletas serializadas para o banco
|
||||
|
||||
Serialized payload layout for `TILES` v1:
|
||||
Subárvores opcionais e informativas:
|
||||
|
||||
- `metadata.codec`: Configuração específica do codec/compressor (ex: dicionários, flags de compressão).
|
||||
- `metadata.pipeline`: Metadados informativos do processo de build do Studio (ex: source hashes, timestamps, tool versions).
|
||||
|
||||
Regras de validação para `TILES` v1:
|
||||
|
||||
- `palette_count` deve ser `64`
|
||||
- `width * height` define o número de pixels indexados lógicos na folha decodificada
|
||||
- metadados adicionais podem existir, mas o contrato do runtime não deve depender deles para reconstruir o banco em memória (exceto se definidos na raiz como campos efetivos).
|
||||
|
||||
#### 4.1.2 Payload Layout
|
||||
|
||||
1. packed indexed pixels for the full sheet, using `ceil(width * height / 2)` bytes;
|
||||
2. palette table, using `palette_count * 16 * 2` bytes.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user