afegit resource::cache
normalitzat Audio
This commit is contained in:
272
source/core/resources/resource_cache.cpp
Normal file
272
source/core/resources/resource_cache.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
#include "core/resources/resource_cache.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "core/audio/jail_audio.hpp"
|
||||
#include "core/resources/resource_helper.hpp"
|
||||
#include "core/resources/resource_list.hpp"
|
||||
|
||||
// gif.h ja s'inclou des de jdraw8.cpp i text.cpp; el seu codi no és static
|
||||
// ni inline, així que no podem tornar-lo a incloure aquí. Ens fiem de les
|
||||
// declaracions extern dels símbols que ens calen (linkatge C++ normal,
|
||||
// igual que fa text.cpp).
|
||||
extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h);
|
||||
extern unsigned char* LoadPalette(unsigned char* data);
|
||||
|
||||
namespace Resource {
|
||||
|
||||
Cache* Cache::instance = nullptr;
|
||||
|
||||
void Cache::init() { instance = new Cache(); }
|
||||
void Cache::destroy() {
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
auto Cache::get() -> Cache* { return instance; }
|
||||
|
||||
namespace {
|
||||
auto basename(const std::string& path) -> std::string {
|
||||
auto pos = path.find_last_of("/\\");
|
||||
return pos == std::string::npos ? path : path.substr(pos + 1);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
auto Cache::getMusic(const std::string& name) -> JA_Music_t* {
|
||||
auto it = std::ranges::find_if(musics_, [&](const auto& m) { return m.name == name; });
|
||||
if (it != musics_.end()) {
|
||||
return it->music.get();
|
||||
}
|
||||
std::cerr << "Resource::Cache: música no trobada: " << name << '\n';
|
||||
throw std::runtime_error("Music not found: " + name);
|
||||
}
|
||||
|
||||
auto Cache::getSound(const std::string& name) -> JA_Sound_t* {
|
||||
auto it = std::ranges::find_if(sounds_, [&](const auto& s) { return s.name == name; });
|
||||
if (it != sounds_.end()) {
|
||||
return it->sound.get();
|
||||
}
|
||||
std::cerr << "Resource::Cache: so no trobat: " << name << '\n';
|
||||
throw std::runtime_error("Sound not found: " + name);
|
||||
}
|
||||
|
||||
auto Cache::getSurfacePixels(const std::string& name) -> const std::vector<Uint8>& {
|
||||
auto it = std::ranges::find_if(surfaces_, [&](const auto& s) { return s.name == name; });
|
||||
if (it != surfaces_.end()) {
|
||||
return it->pixels;
|
||||
}
|
||||
std::cerr << "Resource::Cache: surface no trobada: " << name << '\n';
|
||||
throw std::runtime_error("Surface not found: " + name);
|
||||
}
|
||||
|
||||
auto Cache::getPaletteBytes(const std::string& name) -> const std::vector<Uint8>& {
|
||||
auto it = std::ranges::find_if(surfaces_, [&](const auto& s) { return s.name == name; });
|
||||
if (it != surfaces_.end()) {
|
||||
return it->palette;
|
||||
}
|
||||
std::cerr << "Resource::Cache: paleta no trobada: " << name << '\n';
|
||||
throw std::runtime_error("Palette not found: " + name);
|
||||
}
|
||||
|
||||
auto Cache::getTextFile(const std::string& name) -> const std::vector<uint8_t>& {
|
||||
auto it = std::ranges::find_if(text_files_, [&](const auto& t) { return t.name == name; });
|
||||
if (it != text_files_.end()) {
|
||||
return it->bytes;
|
||||
}
|
||||
std::cerr << "Resource::Cache: text file no trobat: " << name << '\n';
|
||||
throw std::runtime_error("TextFile not found: " + name);
|
||||
}
|
||||
|
||||
void Cache::calculateTotal() {
|
||||
auto* list = List::get();
|
||||
total_count_ = static_cast<int>(
|
||||
list->getListByType(List::Type::MUSIC).size() +
|
||||
list->getListByType(List::Type::SOUND).size() +
|
||||
list->getListByType(List::Type::BITMAP).size() +
|
||||
list->getListByType(List::Type::DATA).size() +
|
||||
list->getListByType(List::Type::FONT).size());
|
||||
loaded_count_ = 0;
|
||||
}
|
||||
|
||||
auto Cache::getProgress() const -> float {
|
||||
if (total_count_ == 0) return 1.0F;
|
||||
return static_cast<float>(loaded_count_) / static_cast<float>(total_count_);
|
||||
}
|
||||
|
||||
void Cache::beginLoad() {
|
||||
calculateTotal();
|
||||
stage_ = LoadStage::MUSICS;
|
||||
stage_index_ = 0;
|
||||
std::cout << "Resource::Cache: precarregant " << total_count_ << " assets\n";
|
||||
}
|
||||
|
||||
auto Cache::loadStep(int budget_ms) -> bool {
|
||||
if (stage_ == LoadStage::DONE) return true;
|
||||
|
||||
const Uint64 start_ns = SDL_GetTicksNS();
|
||||
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
|
||||
auto* list = List::get();
|
||||
|
||||
while (stage_ != LoadStage::DONE) {
|
||||
switch (stage_) {
|
||||
case LoadStage::MUSICS: {
|
||||
auto items = list->getListByType(List::Type::MUSIC);
|
||||
if (stage_index_ == 0) musics_.clear();
|
||||
if (stage_index_ >= items.size()) {
|
||||
stage_ = LoadStage::SOUNDS;
|
||||
stage_index_ = 0;
|
||||
break;
|
||||
}
|
||||
loadOneMusic(stage_index_++);
|
||||
break;
|
||||
}
|
||||
case LoadStage::SOUNDS: {
|
||||
auto items = list->getListByType(List::Type::SOUND);
|
||||
if (stage_index_ == 0) sounds_.clear();
|
||||
if (stage_index_ >= items.size()) {
|
||||
stage_ = LoadStage::BITMAPS;
|
||||
stage_index_ = 0;
|
||||
break;
|
||||
}
|
||||
loadOneSound(stage_index_++);
|
||||
break;
|
||||
}
|
||||
case LoadStage::BITMAPS: {
|
||||
auto items = list->getListByType(List::Type::BITMAP);
|
||||
if (stage_index_ == 0) surfaces_.clear();
|
||||
if (stage_index_ >= items.size()) {
|
||||
stage_ = LoadStage::TEXT_FILES;
|
||||
stage_index_ = 0;
|
||||
break;
|
||||
}
|
||||
loadOneBitmap(stage_index_++);
|
||||
break;
|
||||
}
|
||||
case LoadStage::TEXT_FILES: {
|
||||
auto data_items = list->getListByType(List::Type::DATA);
|
||||
auto font_items = list->getListByType(List::Type::FONT);
|
||||
auto items = data_items;
|
||||
items.insert(items.end(), font_items.begin(), font_items.end());
|
||||
if (stage_index_ == 0) text_files_.clear();
|
||||
if (stage_index_ >= items.size()) {
|
||||
stage_ = LoadStage::DONE;
|
||||
stage_index_ = 0;
|
||||
std::cout << "Resource::Cache: precarrega completada (" << loaded_count_ << "/" << total_count_ << ")\n";
|
||||
break;
|
||||
}
|
||||
loadOneTextFile(stage_index_++);
|
||||
break;
|
||||
}
|
||||
case LoadStage::DONE:
|
||||
break;
|
||||
}
|
||||
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) break;
|
||||
}
|
||||
|
||||
return stage_ == LoadStage::DONE;
|
||||
}
|
||||
|
||||
void Cache::loadOneMusic(size_t index) {
|
||||
auto items = List::get()->getListByType(List::Type::MUSIC);
|
||||
const auto& path = items[index];
|
||||
auto name = basename(path);
|
||||
current_loading_name_ = name;
|
||||
|
||||
auto bytes = ResourceHelper::loadFile(path);
|
||||
if (bytes.empty()) {
|
||||
std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n';
|
||||
return;
|
||||
}
|
||||
JA_Music_t* music = JA_LoadMusic(bytes.data(), static_cast<Uint32>(bytes.size()), path.c_str());
|
||||
if (music == nullptr) {
|
||||
std::cerr << "Resource::Cache: JA_LoadMusic ha fallat per " << path << '\n';
|
||||
return;
|
||||
}
|
||||
musics_.push_back(MusicResource{.name = name, .music = std::unique_ptr<JA_Music_t, MusicDeleter>(music)});
|
||||
++loaded_count_;
|
||||
std::cout << " [music ] " << name << '\n';
|
||||
}
|
||||
|
||||
void Cache::loadOneSound(size_t index) {
|
||||
auto items = List::get()->getListByType(List::Type::SOUND);
|
||||
const auto& path = items[index];
|
||||
auto name = basename(path);
|
||||
current_loading_name_ = name;
|
||||
|
||||
auto bytes = ResourceHelper::loadFile(path);
|
||||
if (bytes.empty()) {
|
||||
std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n';
|
||||
return;
|
||||
}
|
||||
JA_Sound_t* sound = JA_LoadSound(bytes.data(), static_cast<uint32_t>(bytes.size()));
|
||||
if (sound == nullptr) {
|
||||
std::cerr << "Resource::Cache: JA_LoadSound ha fallat per " << path << '\n';
|
||||
return;
|
||||
}
|
||||
sounds_.push_back(SoundResource{.name = name, .sound = std::unique_ptr<JA_Sound_t, SoundDeleter>(sound)});
|
||||
++loaded_count_;
|
||||
std::cout << " [sound ] " << name << '\n';
|
||||
}
|
||||
|
||||
void Cache::loadOneBitmap(size_t index) {
|
||||
auto items = List::get()->getListByType(List::Type::BITMAP);
|
||||
const auto& path = items[index];
|
||||
auto name = basename(path);
|
||||
current_loading_name_ = name;
|
||||
|
||||
auto bytes = ResourceHelper::loadFile(path);
|
||||
if (bytes.empty()) {
|
||||
std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
// Decodifica píxels.
|
||||
unsigned short w = 0;
|
||||
unsigned short h = 0;
|
||||
unsigned char* pixels = LoadGif(bytes.data(), &w, &h);
|
||||
if (pixels == nullptr) {
|
||||
std::cerr << "Resource::Cache: LoadGif ha fallat per " << path << '\n';
|
||||
return;
|
||||
}
|
||||
SurfaceResource res;
|
||||
res.name = name;
|
||||
res.pixels.assign(pixels, pixels + 64000);
|
||||
std::free(pixels);
|
||||
|
||||
// Decodifica paleta des del mateix GIF (necessita una segona passada
|
||||
// perquè LoadGif no exposa la paleta).
|
||||
unsigned char* palette = LoadPalette(bytes.data());
|
||||
if (palette != nullptr) {
|
||||
res.palette.assign(palette, palette + 768);
|
||||
std::free(palette);
|
||||
}
|
||||
|
||||
surfaces_.push_back(std::move(res));
|
||||
++loaded_count_;
|
||||
std::cout << " [bitmap] " << name << '\n';
|
||||
}
|
||||
|
||||
void Cache::loadOneTextFile(size_t index) {
|
||||
auto data_items = List::get()->getListByType(List::Type::DATA);
|
||||
auto font_items = List::get()->getListByType(List::Type::FONT);
|
||||
auto items = data_items;
|
||||
items.insert(items.end(), font_items.begin(), font_items.end());
|
||||
const auto& path = items[index];
|
||||
auto name = basename(path);
|
||||
current_loading_name_ = name;
|
||||
|
||||
auto bytes = ResourceHelper::loadFile(path);
|
||||
if (bytes.empty()) {
|
||||
std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n';
|
||||
return;
|
||||
}
|
||||
text_files_.push_back(TextFileResource{.name = name, .bytes = std::move(bytes)});
|
||||
++loaded_count_;
|
||||
std::cout << " [text ] " << name << '\n';
|
||||
}
|
||||
|
||||
} // namespace Resource
|
||||
72
source/core/resources/resource_cache.hpp
Normal file
72
source/core/resources/resource_cache.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/resources/resource_types.hpp"
|
||||
|
||||
namespace Resource {
|
||||
|
||||
// Cache singleton: precarga + decode dels assets llistats al
|
||||
// `Resource::List`. Implementa carrega incremental amb pressupost
|
||||
// de temps per frame (`loadStep`) per a poder mostrar una barra de
|
||||
// progrés des de l'escena `BootLoader`.
|
||||
class Cache {
|
||||
public:
|
||||
static void init();
|
||||
static void destroy();
|
||||
static auto get() -> Cache*;
|
||||
|
||||
Cache(const Cache&) = delete;
|
||||
auto operator=(const Cache&) -> Cache& = delete;
|
||||
|
||||
// Getters: throw runtime_error si el nom no existeix al cache.
|
||||
auto getMusic(const std::string& name) -> JA_Music_t*;
|
||||
auto getSound(const std::string& name) -> JA_Sound_t*;
|
||||
auto getSurfacePixels(const std::string& name) -> const std::vector<Uint8>&;
|
||||
auto getPaletteBytes(const std::string& name) -> const std::vector<Uint8>&;
|
||||
auto getTextFile(const std::string& name) -> const std::vector<uint8_t>&;
|
||||
|
||||
// Loader incremental.
|
||||
void beginLoad();
|
||||
auto loadStep(int budget_ms) -> bool; // true → DONE
|
||||
[[nodiscard]] auto isLoadDone() const -> bool { return stage_ == LoadStage::DONE; }
|
||||
[[nodiscard]] auto getProgress() const -> float; // 0.0..1.0
|
||||
[[nodiscard]] auto getCurrentLoadingName() const -> const std::string& { return current_loading_name_; }
|
||||
|
||||
private:
|
||||
Cache() = default;
|
||||
~Cache() = default;
|
||||
|
||||
enum class LoadStage {
|
||||
MUSICS,
|
||||
SOUNDS,
|
||||
BITMAPS,
|
||||
TEXT_FILES,
|
||||
DONE,
|
||||
};
|
||||
|
||||
void calculateTotal();
|
||||
void loadOneMusic(size_t index);
|
||||
void loadOneSound(size_t index);
|
||||
void loadOneBitmap(size_t index);
|
||||
void loadOneTextFile(size_t index);
|
||||
|
||||
std::vector<MusicResource> musics_;
|
||||
std::vector<SoundResource> sounds_;
|
||||
std::vector<SurfaceResource> surfaces_;
|
||||
std::vector<TextFileResource> text_files_;
|
||||
|
||||
LoadStage stage_{LoadStage::DONE};
|
||||
size_t stage_index_{0};
|
||||
int total_count_{0};
|
||||
int loaded_count_{0};
|
||||
std::string current_loading_name_;
|
||||
|
||||
static Cache* instance;
|
||||
};
|
||||
|
||||
} // namespace Resource
|
||||
114
source/core/resources/resource_list.cpp
Normal file
114
source/core/resources/resource_list.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "core/resources/resource_list.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "core/resources/resource_helper.hpp"
|
||||
#include "external/fkyaml_node.hpp"
|
||||
|
||||
namespace Resource {
|
||||
|
||||
List* List::instance = nullptr;
|
||||
|
||||
void List::init(const std::string& yaml_path) {
|
||||
instance = new List();
|
||||
instance->loadFromYaml(yaml_path);
|
||||
}
|
||||
|
||||
void List::destroy() {
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
auto List::get() -> List* { return instance; }
|
||||
|
||||
void List::loadFromYaml(const std::string& yaml_path) {
|
||||
auto bytes = ResourceHelper::loadFile(yaml_path);
|
||||
if (bytes.empty()) {
|
||||
std::cout << "Resource::List: cannot load manifest " << yaml_path << '\n';
|
||||
return;
|
||||
}
|
||||
std::string content(bytes.begin(), bytes.end());
|
||||
loadFromString(content);
|
||||
}
|
||||
|
||||
void List::loadFromString(const std::string& yaml_content) {
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(yaml_content);
|
||||
if (!yaml.contains("assets")) {
|
||||
std::cout << "Resource::List: missing 'assets' root key\n";
|
||||
return;
|
||||
}
|
||||
const auto& assets = yaml["assets"];
|
||||
for (auto cat_it = assets.begin(); cat_it != assets.end(); ++cat_it) {
|
||||
const auto& category_node = cat_it.value();
|
||||
if (!category_node.is_mapping()) {
|
||||
continue;
|
||||
}
|
||||
for (auto type_it = category_node.begin(); type_it != category_node.end(); ++type_it) {
|
||||
auto type_str = type_it.key().get_value<std::string>();
|
||||
Type type = parseAssetType(type_str);
|
||||
const auto& items = type_it.value();
|
||||
if (!items.is_sequence()) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& item : items) {
|
||||
if (item.is_string()) {
|
||||
addToMap(item.get_value<std::string>(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "Resource::List: loaded " << file_list_.size() << " assets from manifest\n";
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "Resource::List: YAML parse error: " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void List::addToMap(const std::string& path, Type type) {
|
||||
auto key = basename(path);
|
||||
if (file_list_.contains(key)) {
|
||||
std::cout << "Resource::List: duplicate asset key '" << key << "', overwriting\n";
|
||||
}
|
||||
file_list_.emplace(key, Item{path, type});
|
||||
}
|
||||
|
||||
auto List::get(const std::string& filename) const -> std::string {
|
||||
auto it = file_list_.find(filename);
|
||||
if (it != file_list_.end()) {
|
||||
return it->second.path;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
auto List::getListByType(Type type) const -> std::vector<std::string> {
|
||||
std::vector<std::string> list;
|
||||
for (const auto& [filename, item] : file_list_) {
|
||||
if (item.type == type) {
|
||||
list.push_back(item.path);
|
||||
}
|
||||
}
|
||||
std::ranges::sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
auto List::exists(const std::string& filename) const -> bool {
|
||||
return file_list_.contains(filename);
|
||||
}
|
||||
|
||||
auto List::parseAssetType(const std::string& type_str) -> Type {
|
||||
if (type_str == "DATA") return Type::DATA;
|
||||
if (type_str == "BITMAP") return Type::BITMAP;
|
||||
if (type_str == "MUSIC") return Type::MUSIC;
|
||||
if (type_str == "SOUND") return Type::SOUND;
|
||||
if (type_str == "FONT") return Type::FONT;
|
||||
throw std::runtime_error("Unknown asset type: " + type_str);
|
||||
}
|
||||
|
||||
auto List::basename(const std::string& path) -> std::string {
|
||||
auto pos = path.find_last_of("/\\");
|
||||
return pos == std::string::npos ? path : path.substr(pos + 1);
|
||||
}
|
||||
|
||||
} // namespace Resource
|
||||
61
source/core/resources/resource_list.hpp
Normal file
61
source/core/resources/resource_list.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Resource {
|
||||
|
||||
// Registre lleuger d'assets carregat des de `data/config/assets.yaml`.
|
||||
// Map<basename → Item> per a lookup O(1). Cache l'utilitza per a
|
||||
// iterar per categoria a l'hora de carregar.
|
||||
class List {
|
||||
public:
|
||||
enum class Type : int {
|
||||
DATA,
|
||||
BITMAP,
|
||||
MUSIC,
|
||||
SOUND,
|
||||
FONT,
|
||||
SIZE,
|
||||
};
|
||||
|
||||
static void init(const std::string& yaml_path);
|
||||
static void destroy();
|
||||
static auto get() -> List*;
|
||||
|
||||
List(const List&) = delete;
|
||||
auto operator=(const List&) -> List& = delete;
|
||||
|
||||
[[nodiscard]] auto get(const std::string& filename) const -> std::string;
|
||||
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
||||
[[nodiscard]] auto exists(const std::string& filename) const -> bool;
|
||||
[[nodiscard]] auto totalCount() const -> int { return static_cast<int>(file_list_.size()); }
|
||||
|
||||
private:
|
||||
struct Item {
|
||||
std::string path; // ruta relativa al pack (ex: "music/menu.ogg")
|
||||
Type type;
|
||||
|
||||
Item(std::string p, Type t)
|
||||
: path(std::move(p)),
|
||||
type(t) {}
|
||||
};
|
||||
|
||||
List() = default;
|
||||
~List() = default;
|
||||
|
||||
void loadFromYaml(const std::string& yaml_path);
|
||||
void loadFromString(const std::string& yaml_content);
|
||||
void addToMap(const std::string& path, Type type);
|
||||
|
||||
[[nodiscard]] static auto parseAssetType(const std::string& type_str) -> Type;
|
||||
[[nodiscard]] static auto basename(const std::string& path) -> std::string;
|
||||
|
||||
std::unordered_map<std::string, Item> file_list_;
|
||||
|
||||
static List* instance;
|
||||
};
|
||||
|
||||
} // namespace Resource
|
||||
61
source/core/resources/resource_types.hpp
Normal file
61
source/core/resources/resource_types.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations to keep this header light.
|
||||
struct JA_Music_t;
|
||||
struct JA_Sound_t;
|
||||
|
||||
void JA_DeleteMusic(JA_Music_t* music);
|
||||
void JA_DeleteSound(JA_Sound_t* sound);
|
||||
|
||||
namespace Resource {
|
||||
|
||||
struct MusicDeleter {
|
||||
void operator()(JA_Music_t* music) const noexcept {
|
||||
if (music != nullptr) {
|
||||
JA_DeleteMusic(music);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SoundDeleter {
|
||||
void operator()(JA_Sound_t* sound) const noexcept {
|
||||
if (sound != nullptr) {
|
||||
JA_DeleteSound(sound);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct MusicResource {
|
||||
std::string name;
|
||||
std::unique_ptr<JA_Music_t, MusicDeleter> music;
|
||||
};
|
||||
|
||||
struct SoundResource {
|
||||
std::string name;
|
||||
std::unique_ptr<JA_Sound_t, SoundDeleter> sound;
|
||||
};
|
||||
|
||||
// Una entrada BITMAP descodifica un GIF i emmagatzema els seus
|
||||
// 64000 bytes de píxels paletats + la paleta de 256 colors (768
|
||||
// bytes RGB). Així `getSurface(name)` i `getPalette(name)` comparteixen
|
||||
// el mateix decode.
|
||||
struct SurfaceResource {
|
||||
std::string name;
|
||||
std::vector<Uint8> pixels; // 64000 bytes (320 * 200) paletats
|
||||
std::vector<Uint8> palette; // 768 bytes (256 * R G B)
|
||||
};
|
||||
|
||||
// Per a fitxers de text generals (locale.yaml, keys.yaml, *.fnt).
|
||||
struct TextFileResource {
|
||||
std::string name;
|
||||
std::vector<uint8_t> bytes;
|
||||
};
|
||||
|
||||
} // namespace Resource
|
||||
Reference in New Issue
Block a user