Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add can_launch API #843

Merged
merged 5 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions 32blit/engine/api_private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ namespace blit {
Pen *palette = nullptr;
};

enum class CanLaunchResult {
Success = 0,
UnknownType, /// no known handler for this file
InvalidFile, /// file is not valid/doesn't exist
IncompatibleBlit, /// file is incompatible with this device
};

#pragma pack(push, 4)
struct API {
uint16_t version_major;
Expand Down Expand Up @@ -135,6 +142,9 @@ namespace blit {

// another launcher API
void (*list_installed_games)(std::function<void(const uint8_t *, uint32_t, uint32_t)> callback);
// if launch is expected to succeed on this file
// files this returns success for should be .blit files or have a registered handler (get_type_handler_metadata should return valid metadata)
CanLaunchResult (*can_launch)(const char *path);
};
#pragma pack(pop)

Expand Down
60 changes: 56 additions & 4 deletions firmware/firmware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ static bool parse_flash_metadata(uint32_t offset, GameInfo &info) {
return true;
}

static bool parse_file_metadata(FIL &fh, GameInfo &info) {
static bool parse_file_header(FIL &fh, BlitGameHeader &header, uint32_t &header_offset) {
UINT bytes_read;
uint8_t buf[10];

Expand All @@ -119,16 +119,28 @@ static bool parse_file_metadata(FIL &fh, GameInfo &info) {
uint32_t num_relocs;
f_read(&fh, (void *)&num_relocs, 4, &bytes_read);

int relocs_size = num_relocs * 4 + 8;
f_lseek(&fh, relocs_size);
header_offset = num_relocs * 4 + 8;
f_lseek(&fh, header_offset);

// read header
BlitGameHeader header;
f_read(&fh, &header, sizeof(header), &bytes_read);

if(header.magic != blit_game_magic)
return false;

return true;
}

static bool parse_file_metadata(FIL &fh, GameInfo &info) {
UINT bytes_read;
uint8_t buf[10];

// read header
BlitGameHeader header;
uint32_t relocs_size;
if(!parse_file_header(fh, header, relocs_size))
return false;

info.size = header.end - qspi_flash_address;

bool result = false;
Expand Down Expand Up @@ -556,6 +568,45 @@ static void list_installed_games(std::function<void(const uint8_t *, uint32_t, u
callback((const uint8_t *)(qspi_flash_address + game.offset), game.offset / qspi_flash_sector_size, game.size);
}

static CanLaunchResult can_launch(const char *path) {
if(strncmp(path, "flash:/", 7) == 0) {
// assume anything flashed is compatible for now
return CanLaunchResult::Success;
}

// get the extension
std::string_view sv(path);
auto last_dot = sv.find_last_of('.');
auto ext = last_dot == std::string::npos ? "" : std::string(sv.substr(last_dot + 1));
for(auto &c : ext)
c = tolower(c);

if(ext == "blit") {
BlitGameHeader header;
uint32_t header_offset;
FIL file;
FRESULT res = f_open(&file, path, FA_READ);
if(res != FR_OK)
return CanLaunchResult::InvalidFile;

if(parse_file_header(file, header, header_offset)) {
f_close(&file);
return CanLaunchResult::Success;
}

f_close(&file);
return CanLaunchResult::IncompatibleBlit;
}

// not a blit file, so we need to check for handlers
for(auto &handler : handlers) {
if(strncmp(ext.c_str(), handler.type, 4) == 0)
return CanLaunchResult::Success;
}

return CanLaunchResult::UnknownType;
}

static const uint8_t *flash_to_tmp(const std::string &filename, uint32_t &size) {
// one file at a time
// TODO: this could be improved
Expand Down Expand Up @@ -642,6 +693,7 @@ void init() {
api.tmp_file_closed = tmp_file_closed;

api.list_installed_games = list_installed_games;
api.can_launch = can_launch;

scan_flash();
flash_scanned = true;
Expand Down
69 changes: 29 additions & 40 deletions launcher/launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,86 +146,75 @@ static void load_file_list(const std::string &directory) {

game_list.clear();

auto files = list_files(directory, [](auto &file) {
auto files = list_files(directory, [&](auto &file) {
if(file.flags & FileFlags::directory)
return false;

if(file.name.length() < 6) // minimum length for single-letter game (a.blit)
return false;

if(file.name[0] == '.') // hidden file
return false;

if(file.name.find_last_of('.') == std::string::npos) // no extension
return false;
auto path = directory == "/" ? file.name : directory + "/" + file.name;
auto res = api.can_launch(path.c_str());

return true;
if(res == CanLaunchResult::UnknownType) {
// special case for images
auto last_dot = file.name.find_last_of('.');

auto ext = last_dot == std::string::npos ? "" : file.name.substr(last_dot + 1);

for(auto &c : ext)
c = tolower(c);

if(ext == "bmp" || ext == "blim")
return true;
}

return res == CanLaunchResult::Success;
});

game_list.reserve(files.size()); // worst case

for(auto &file : files) {
auto last_dot = file.name.find_last_of('.');

auto ext = file.name.substr(last_dot + 1);
auto ext = last_dot == std::string::npos ? "" : file.name.substr(last_dot + 1);

for(auto &c : ext)
c = tolower(c);

GameInfo game;
game.title = file.name.substr(0, file.name.length() - ext.length() - 1);
game.filename = directory == "/" ? file.name : directory + "/" + file.name;
game.size = file.size;

if(ext == "blit") {
GameInfo game;
game.type = GameType::game;
game.title = file.name.substr(0, file.name.length() - 5);
game.filename = directory == "/" ? file.name : directory + "/" + file.name;
game.size = file.size;

// check for metadata
BlitGameMetadata meta;
if(parse_file_metadata(game.filename, meta)) {
if(parse_file_metadata(game.filename, meta))
game.title = meta.title;
}

game_list.push_back(game);
continue;
}

if(ext == "bmp" || ext == "blim") {
GameInfo game;
} else if(ext == "bmp" || ext == "blim") {
game.type = GameType::screenshot;
game.title = file.name.substr(0, file.name.length() - ext.length() - 1);
game.filename = directory == "/" ? file.name : directory + "/" + file.name;
game.size = file.size;

// Special case check for an installed handler for these types, ie: a sprite editor
game.can_launch = api.get_type_handler_metadata && api.get_type_handler_metadata(ext.c_str());
game_list.push_back(game);
continue;
}

if(!api.get_type_handler_metadata) continue;

// check for installed handler
auto handler_meta = api.get_type_handler_metadata(ext.c_str());

if(handler_meta) {
GameInfo game;
} else {
// it's launch-able so there must be a handler
game.type = GameType::file;
game.filename = directory == "/" ? file.name : directory + "/" + file.name;
strncpy(game.ext, ext.c_str(), 5);
game.ext[4] = 0;
game.size = file.size;
game.can_launch = true;

// check for a metadata file (fall back to handler's metadata)
BlitGameMetadata meta;
auto meta_filename = game.filename + ".blmeta";
if(parse_file_metadata(meta_filename, meta))
game.title = meta.title;
else
game.title = file.name;

game_list.push_back(game);
}

game_list.push_back(game);
}

int total_items = (int)game_list.size();
Expand Down
Loading