Skip to content
199 changes: 161 additions & 38 deletions 32blit-pico/blit_launch.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <cstring>
#include <forward_list>

#include "pico/stdlib.h"
#include "hardware/flash.h"
Expand Down Expand Up @@ -34,6 +35,17 @@
static const uint32_t flash_end = FLASH_STORAGE_OFFSET;
#endif

struct TypeHandlerInfo {
char type[4]; // should have a null terminator, but the low byte of the offset will always be zero
uint32_t offset;
};

std::forward_list<TypeHandlerInfo> handlers;

#ifdef BUILD_LOADER
static char launch_path[256];
#endif

extern int (*do_tick)(uint32_t time);

void disable_user_code();
Expand Down Expand Up @@ -66,6 +78,8 @@
return (size - 1) / game_block_size + 1;
}

#ifdef BUILD_LOADER

#ifdef PICO_RP2350
static uint32_t find_flash_offset(uint32_t requested_size) {
uint32_t free_off = 0; // 0 is invalid as that's where the loader is
Expand Down Expand Up @@ -163,7 +177,21 @@
return ~0u;
}

// returns lowercased extension
static std::string get_file_ext(const char *path) {
// 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);

return ext;
}

#endif

static bool cleanup_duplicates(RawMetadata &meta, uint32_t new_offset) {

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem (.blit)

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoSystem

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoVision

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoVision

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoVision

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoVision

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]

Check warning on line 194 in 32blit-pico/blit_launch.cpp

View workflow job for this annotation

GitHub Actions / PicoVision

'bool cleanup_duplicates(RawMetadata&, uint32_t)' defined but not used [-Wunused-function]
bool ret = false;
for(uint32_t off = 0; off < flash_end;) {
auto size = get_installed_file_size(off);
Expand Down Expand Up @@ -211,59 +239,109 @@
return nullptr;
}

void create_type_handler_list() {
handlers.clear();

blit::api.list_installed_games([](const uint8_t *ptr, uint32_t block, uint32_t size) {
auto header = (const BlitGameHeader *)ptr;
auto metadata_ptr = ptr + header->end;

if(memcmp(metadata_ptr, "BLITMETA", 8) != 0)
return;

// skip straight to the type block
metadata_ptr += 10 + sizeof(RawMetadata);

if(memcmp(metadata_ptr, "BLITTYPE", 8) != 0)
return;

// get the type block
auto type_meta = (const RawTypeMetadata *)(metadata_ptr + 8);

// loop through filetypes
for(unsigned i = 0; i < type_meta->num_filetypes; i++) {
TypeHandlerInfo info;
memcpy(info.type, type_meta->filetypes[i], 4);
info.offset = block * game_block_size;

handlers.emplace_front(info);
}
});
}

bool launch_file(const char *path) {
#ifdef BUILD_LOADER
uint32_t flash_offset = ~0u;

// clear old path
launch_path[0] = 0;

if(strncmp(path, "flash:/", 7) == 0) // from flash
flash_offset = atoi(path + 7) * game_block_size;
else {
// from storage
auto file = open_file(path, blit::OpenMode::read);

if(!file)
return false;
auto ext = get_file_ext(path);

// read file metadata and try to find matching installed gat
RawMetadata meta;
RawTypeMetadata type_meta = {};
if(ext != "blit") {
// with handler
for(auto &handler : handlers) {
if(strncmp(ext.data(), handler.type, 4) == 0) {
flash_offset = handler.offset;
break;
}
}

if(read_file_metadata(file, meta, type_meta))
flash_offset = find_installed_blit(meta);
// set launch path
strncpy(launch_path, path, sizeof(launch_path) - 1);
} else {
// from storage
auto file = open_file(path, blit::OpenMode::read);

// flash if not found
if(flash_offset == ~0u) {
BlitWriter writer;
if(!file)
return false;

uint32_t file_offset = 0;
uint32_t len = get_file_length(file);
// read file metadata and try to find matching installed gat
RawMetadata meta;
RawTypeMetadata type_meta = {};

writer.init(len);
if(read_file_metadata(file, meta, type_meta))
flash_offset = find_installed_blit(meta);

// read in small chunks
uint8_t buf[FLASH_PAGE_SIZE];
// flash if not found
if(flash_offset == ~0u) {
BlitWriter writer;

while(file_offset < len) {
auto bytes_read = read_file(file, file_offset, FLASH_PAGE_SIZE, (char *)buf);
if(bytes_read <= 0)
break;
uint32_t file_offset = 0;
uint32_t len = get_file_length(file);

if(!writer.write(buf, bytes_read))
break;
writer.init(len);

file_offset += bytes_read;
}
// read in small chunks
uint8_t buf[FLASH_PAGE_SIZE];

close_file(file);
while(file_offset < len) {
auto bytes_read = read_file(file, file_offset, FLASH_PAGE_SIZE, (char *)buf);
if(bytes_read <= 0)
break;

// didn't write everything, fail launch
if(writer.get_remaining() > 0)
return false;
if(!writer.write(buf, bytes_read))
break;

flash_offset = writer.get_flash_offset();
file_offset += bytes_read;
}

cleanup_duplicates(meta, flash_offset);
} else
close_file(file);
close_file(file);

// didn't write everything, fail launch
if(writer.get_remaining() > 0)
return false;

flash_offset = writer.get_flash_offset();

cleanup_duplicates(meta, flash_offset);
} else
close_file(file);
}
}

auto header = (BlitGameHeader *)(FLASH_BASE + flash_offset);
Expand All @@ -276,6 +354,9 @@

requested_launch_offset = flash_offset;
return true;
#else
return false;
#endif
}

blit::CanLaunchResult can_launch(const char *path) {
Expand All @@ -286,11 +367,7 @@
}

// 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);
auto ext = get_file_ext(path);

if(ext == "blit") {
BlitGameHeader header;
Expand All @@ -309,6 +386,14 @@
close_file(file);
return blit::CanLaunchResult::IncompatibleBlit;
}

// check handlers
for(auto &handler : handlers) {
if(strncmp(ext.data(), handler.type, 4) == 0) {
return blit::CanLaunchResult::Success;
}
}

#endif

return blit::CanLaunchResult::UnknownType;
Expand Down Expand Up @@ -419,9 +504,45 @@
restore_interrupts(status);

set_render_overlay_enabled(false);

// clear out any handler entries
auto prev = handlers.before_begin();
for(auto it = handlers.begin(); it != handlers.end();) {
if(it->offset == offset)
it = handlers.erase_after(prev);
else
prev = it++;
}
#endif
}

void *get_type_handler_metadata(const char *filetype) {
#ifdef BUILD_LOADER
for(auto &handler : handlers) {
if(strncmp(filetype, handler.type, 4) == 0) {
auto ptr = (const uint8_t *)(FLASH_BASE + handler.offset);
auto header = (const BlitGameHeader *)ptr;
auto metadata_ptr = ptr + header->end;

// this really shouldn't fail if it's in the list, but be safe
if(memcmp(metadata_ptr, "BLITMETA", 8) == 0)
return (void *)metadata_ptr;
}
}
#endif
return nullptr;
}

const char *get_launch_path() {
#ifdef BUILD_LOADER
return launch_path;
#else
return nullptr;
#endif
}

#ifdef BUILD_LOADER

// .blit file writer
void BlitWriter::init(uint32_t file_len) {
this->file_len = file_len;
Expand Down Expand Up @@ -520,3 +641,5 @@

return true;
}

#endif
6 changes: 6 additions & 0 deletions 32blit-pico/blit_launch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ static constexpr unsigned int game_block_size = 64 * 1024;

RawMetadata *get_running_game_metadata();

void create_type_handler_list();

bool launch_file(const char *path);
blit::CanLaunchResult can_launch(const char *path);
void launch_pre_init();
Expand All @@ -19,6 +21,10 @@ void list_installed_games(std::function<void(const uint8_t *, uint32_t, uint32_t

void erase_game(uint32_t offset);

void *get_type_handler_metadata(const char *filetype);

const char *get_launch_path();

class BlitWriter final {
public:
void init(uint32_t file_len);
Expand Down
12 changes: 6 additions & 6 deletions 32blit-pico/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ static uint32_t get_max_us_timer() {
return 0xFFFFFFFF; // it's a 64bit timer...
}

const char *get_launch_path() {
return nullptr;
}

static GameMetadata get_metadata() {
GameMetadata ret;

Expand Down Expand Up @@ -167,7 +163,7 @@ static const blit::APIConst blit_api_const {

::launch_file,
::erase_game,
nullptr, // get_type_handler_metadata
::get_type_handler_metadata,

::get_launch_path,

Expand Down Expand Up @@ -219,6 +215,8 @@ void disable_user_code() {
static int64_t home_hold_callback(alarm_id_t id, void *user_data) {
home_hold_alarm_id = 0;

close_open_files();

launch_pre_init();
::init(); // re-initialising the loader is effectively a reset

Expand Down Expand Up @@ -317,7 +315,9 @@ int main() {
#endif
#endif

#ifndef BUILD_LOADER
#ifdef BUILD_LOADER
create_type_handler_list();
#else
blit::set_screen_mode(ScreenMode::lores);
#endif

Expand Down
3 changes: 3 additions & 0 deletions 32blit-pico/usb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "multiplayer.hpp"
#include "overlay.hpp"

#include "fatfs_blit_api.hpp"

#include "engine/engine.hpp"
#include "executable.hpp"

Expand Down Expand Up @@ -173,6 +175,7 @@ class CDCProgCommand final : public CDCCommand {
usb_cdc_flush_write();

// reinit loader
close_open_files();
::init();

return Status::Done;
Expand Down
Loading