-
Notifications
You must be signed in to change notification settings - Fork 0
Add PICO-8 custom font compilation target #6
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // For format details, see https://aka.ms/devcontainer.json. For config options, see the | ||
| // README at: https://github.com/devcontainers/templates/tree/main/src/rust | ||
| { | ||
| "name": "Rust", | ||
| // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile | ||
| "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", | ||
|
|
||
| // Features to add to the dev container. More info: https://containers.dev/features. | ||
| // "features": {}, | ||
|
|
||
| // Configure tool-specific properties. | ||
| "customizations": { | ||
| // Configure properties specific to VS Code. | ||
| "vscode": { | ||
| "settings": {}, | ||
| "extensions": [ | ||
| "streetsidesoftware.code-spell-checker" | ||
| ] | ||
| } | ||
| }, | ||
|
|
||
| // Use 'forwardPorts' to make a list of ports inside the container available locally. | ||
| // "forwardPorts": [], | ||
|
|
||
| // Use 'postCreateCommand' to run commands after the container is created. | ||
| // "postCreateCommand": "rustc --version", | ||
| "postCreateCommand": "sudo apt update && sudo apt install -y git-lfs && git lfs install" | ||
|
|
||
| // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. | ||
| // "remoteUser": "root" | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| [package] | ||
| name = "spfc-target-p8" | ||
| version = "0.1.0" | ||
| edition.workspace = true | ||
|
|
||
| [lib] | ||
| crate-type = ["cdylib"] | ||
|
|
||
| [dependencies] | ||
| spfc-abi = { path = "../../spfc-abi" } | ||
| spf.workspace = true | ||
| bitvec = "1.0.1" | ||
| render_spf.workspace = true | ||
| anyhow.workspace = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| use std::collections::BTreeMap; | ||
|
|
||
| use crate::utilities::PixmapGlyph; | ||
|
|
||
| pub mod writer; | ||
| pub use writer::*; | ||
|
|
||
|
|
||
| #[derive(Default, Debug)] | ||
| pub struct Process { | ||
| pub family_name: String, | ||
| pub family_version: f64, | ||
| pub manufacturer: String, | ||
| pub pixmap_pairs: BTreeMap<String, PixmapGlyph>, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| use crate::{builders::Process, utilities::{last_character_index, max_height, max_width}}; | ||
| use anyhow::Result; | ||
|
|
||
| pub fn create_program_string(process: &Process) -> Result<String> { | ||
| let mut program_string = String::new(); | ||
|
|
||
| program_string += "pico-8 cartridge // http://www.pico-8.com\nversion 41\n__lua__\n"; | ||
|
|
||
| program_string += format!("-- {} v{}\n-- {}\n-- Generated by spfc\n-- https://github.com/SimplePixelFont/spfc\n\n", process.family_name, process.family_version, process.manufacturer).as_str(); | ||
|
|
||
| let font_function = format!("load_{}", process.family_name.to_ascii_lowercase().replace(" ", "_")); | ||
| program_string += format!("-- Copy the function below\n-- into your own cart\nfunction {}()\n\t-- enable custom fonts\n\tpoke(0x5f58,0x81)\n\n\t-- add font to memory\n", font_function).as_str(); | ||
|
|
||
|
|
||
| let mut buffer = vec![0; (last_character_index(&process.pixmap_pairs) as usize + 1) * 8]; | ||
| buffer[0] = max_width(&process.pixmap_pairs) + 1; //1 added for letter spacing | ||
| buffer[1] = max_width(&process.pixmap_pairs); // This will be added as a custom flag | ||
| buffer[2] = max_height(&process.pixmap_pairs) + 1; //1 added for line spacing | ||
| buffer[3] = 0; // This will be added as a custom flag offset_x | ||
| buffer[4] = 0; // This will be added as a custom flag offset_y | ||
|
|
||
| for (key, glyph) in &process.pixmap_pairs { | ||
| if let Some(p8_char) = crate::utilities::get_character_by_symbol(key) { | ||
| let char_index = p8_char.id as usize; | ||
| let bitmap_data = &glyph.bitmap; | ||
| for (i, byte) in bitmap_data.iter().enumerate() { | ||
| buffer[char_index * 8 + i] = *byte; | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+15
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fail fast on unsupported glyph symbols instead of silently dropping them. If a glyph key is not present in Suggested direction+ let unsupported: Vec<_> = process
+ .pixmap_pairs
+ .keys()
+ .filter(|key| crate::utilities::get_character_by_symbol(key).is_none())
+ .cloned()
+ .collect();
+
+ if !unsupported.is_empty() {
+ anyhow::bail!(
+ "unsupported Pico-8 glyphs: {}",
+ unsupported.join(", ")
+ );
+ }
+
let mut buffer = vec![0; (last_character_index(&process.pixmap_pairs) as usize + 1) * 8];🤖 Prompt for AI Agents |
||
|
|
||
| program_string += format!("\tpoke(0x5600,unpack(split\"{}\"))\nend\n\n", buffer.iter().map(|byte| byte.to_string()).collect::<Vec<String>>().join(",")).as_str(); | ||
|
|
||
| program_string += format!("-- Sample usage below\nfunction _init()\n\t{}()\nend\n\n", font_function).as_str(); | ||
| program_string += format!("function _draw()\n\tcls()\n\tprint(\"Hello from {}!\")\nend ", process.family_name).as_str(); | ||
|
|
||
| Ok(program_string) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| use spf::core::layout_from_data; | ||
| use spfc_abi::{BackendInfo, CURRENT_ABI_VERSION, CompileOptions, CompileResult, PluginOption}; | ||
|
|
||
| mod builders; | ||
| use builders::*; | ||
| mod utilities; | ||
| use utilities::*; | ||
|
|
||
| #[spfc_abi::export] | ||
| fn get_backend_info() -> BackendInfo { | ||
| BackendInfo { | ||
| name: "P8 PICO8 Backend", | ||
| version: 1, | ||
| abi_version: CURRENT_ABI_VERSION, | ||
| } | ||
| } | ||
|
|
||
| #[spfc_abi::export] | ||
| fn get_plugin_options() -> Vec<PluginOption> { | ||
| vec![] | ||
| } | ||
|
|
||
| #[spfc_abi::export] | ||
| fn compile(options: CompileOptions) -> CompileResult { | ||
| let data = std::fs::read(&options.input).unwrap(); | ||
| let layout = layout_from_data(&data).unwrap(); | ||
| let font_table = layout.font_tables.first().unwrap(); | ||
| let font = font_table.fonts.first().unwrap(); | ||
|
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid implicit “first font” selection. Line 27 and Line 28 silently pick the first table/font. For inputs containing multiple fonts, this can compile the wrong asset with no signal. Please validate the expected cardinality (or add explicit selection via plugin options) and fail clearly when ambiguous. 🤖 Prompt for AI Agents |
||
|
|
||
| let mut process = Process::default(); | ||
| process.family_name = font.name.clone(); | ||
| process.family_version = font.version as f64; | ||
| process.manufacturer = font.author.clone(); | ||
| process.pixmap_pairs = create_pixmap_pairs(&layout); | ||
|
|
||
| let font_data = create_program_string(&process).unwrap(); | ||
| std::fs::write(&options.output, &font_data).unwrap(); | ||
|
Comment on lines
+25
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove panic paths from the exported Line 25–Line 37 use 🤖 Prompt for AI Agents |
||
|
|
||
| println!( | ||
| "Finished writing {} bytes to {}", | ||
| font_data.len(), | ||
| options.output | ||
| ); | ||
|
|
||
| CompileResult::Success | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sanitize font metadata before embedding it into Lua.
family_nameandmanufacturerare spliced into a comment, a function name, and a quoted string verbatim. Quotes, newlines, punctuation, or a leading digit can generate invalid Lua, and crafted metadata can inject extra code into the emitted cart.Also applies to: 34-35
🤖 Prompt for AI Agents