Many things can now be configured from the NixOS module, including the search query and rating filter. There is also documentation now!
I added a builder to the flake, to build the image without entering the devshell. I also added a builder to automatically create a release archive containing BootISO, an ISO9660 driver and the UEFI Shell. There is also now a README with instructions on how to run bootiso, and a file with license notices for my dependencies.
The ramdisk is now mounted using another SimpleFileSystem. However, this comes with an external dependency on an ISO9660 driver, which must be loaded before bootiso.efi is launched. Maybe I can automate that too.
But yes, the ISO correctly loads its bootloader, and I get to the GRUB menu. Sadly, once Linux is loaded, there are errors about a missing fd0 device, which is probably one of the harder parts of this project (if it wasn’t hard enough up until now).
This is a small fix with a big effect: I forgot to initialize the DevicePath-pointer-pointer when registering the ramdisk. I now instead create a mutable reference of a pointer, which is initialized by Rust and automatically converted to a pointer by the FFI. And, lo and behold: The image loads (slowly…), and the RAM disk is mounted.
I also got QEMU to work again, the default behavior did not give the VM enough RAM. It now gets 2G, which should be enough for both the RamDisk and Arch. Just, don’t select copytoram.
I can now successfully read the file from disk into RAM!
Instead of uefi::fs, I again use the SimpleFileSystem protocol directly.
This is so I can allocate the buffer myself using allocate_pages, bypassing the heap. This unfortunately introduces another unsafe block, but that’s a small price to pay.
After that, the file also successfully reads into RAM. read contains logic to read in 1KiB blocks, to bypass bugs in some firmwares.
Next stop: Mounting the RAM Disk, using my self-made FFI wrapper.
Currently, it returns EFI_INVALID_PARAMETER, meaning that one of the parameters is NULL, for some reason.
In the last two and a half hours, I wrote my own wrapoer for the Ram Disk protocol.
The uefi crate doesn’t support this protocol yet, so I had to hack together my own support.
Now, it for some reason can’t find the LoadedImage handles… but that is something I will fix after lunch.
I started building BootISO, an EFI application to boot ISO images from GRUB/rEFInd, the EFI shell, or (on some systems and probably not really viable), the Boot Options menu.
Currently, it opens a file handle to an ISO file, which in further development will be copied to a ramdisk, which can then hopefully be used for booting. Unfortunately, neither OVMF nor my laptop seem to support Simple File System, so I’ll need to check how to load the file instead.
Up to there, there aren’t any errors, so I feel quite confident, although a debugger might end up coming in handy …
I’ve gotten used to the architecture of the UEFI crate by now, which should probably speed up development and reduce AI usage further along.
(edit: it turns out I accidentally used the LoadedImage handle to initialize SimpleFileSystem 🤦)
I laid out the first structure for the actual iOS app. It is based on a singular view, that switches between 3 states: .waiting, .scanning and .playing. In .waiting state, it just shows a big, colored button. If that button is clicked, it switches to the second state, where a QR code viewfinder appears. If a QR code is detected and correctly parsed, it switches to the third state, where I will later add the functionality to look up and play the songs.
Devlog #2: teaching a browser to render Wikipedia
last time Helix was learning to draw a cursed smiley face. this time I pointed it at Wikipedia and watched it have a breakdown.
Wikipedia’s infobox was painting directly on top of the article text. both occupied the same pixels. the infobox floated right correctly, but text didn’t know to move.
turns out Helix had two layout engines. the old single-pass painter was dead code. every edit I made to it changed nothing on screen. the real engine had float positioning but never told paragraph line boxes about the float. each line used the full container width. one line fix: inherit the parent’s float context instead of creating an empty one.
then tables. Wikipedia’s cladogram collapsed to one word per line because cells got independent widths per row. no shared columns. rewrote layoutTable with real auto layout: per-column max-content widths across all rows, colspan support, shrink-to-fit. “Kingdom: / Animalia” lines up now.
then images vanished. diagnostic log: first 3-4 images decoded fine, every subsequent one returned exactly 1964 bytes. that’s an HTTP 429 error page. Helix was spawning 50 simultaneous fetch threads (! crazy lol) per page!!!!! Wikimedia rate-limited the burst. capped at 6 concurrent fetches, added HTTP status checking, sent a real User-Agent.
images still didn’t appear because ReceiveImage never invalidated the cached layout tree. layout was built with 0x0 image boxes, the bitmap loaded later, but repaint reused the stale cache. added InvalidateLayout() on image arrival.
then JS DOM was dead. el.className = "active" updated a JS string but never touched the real node. classList.add() returned undefined. Wikipedia’s scripts set className to enable CSS rules and nothing happened. added VM hooks so property writes reflect onto the backing DOM node. classList.add/remove/toggle/contains actually work now.
Bing search was a white page. crash log: “string too long.” 115KB of inline CSS blew up during layout, silently swallowed by catch(...). then it rendered with 200-500px font-size text covering everything. Bing uses giant text inside tiny overflow:hidden containers. added overflow clipping in the paint pass and capped font sizes at 40px.
also landed: CSS custom properties with var() and inheritance, @media queries, display:grid with fr tracks and gap, srcset/data-src image fallback, background sprites, and the search bar showing your query instead of the bing URL.
Wikipedia renders with images, floated infoboxes, and aligned tables. Bing results are readable. not pretty, but usable.
I used these first two hours working out the QR codes and starting on the database. Since the base game does not have an expansion code, it made sense to start with that. I got about halfway through making a CSV containing the card ID (which is encoded in the QR code as well as written on both sides), author, title and release year of each song in tue base game. My end goal is to write an iOS app, and I am already thinking about how to distribute these databases. Do I include them in the bundle, or do I load them from a web site?
Over some time, I made a few improvements. The service now no longer runs as root, but as a dynamic user, which allocates a UID and GID at random. I had a few issues in the meantime, because this also automatically enables a lot of hardening functionality, preventing Deno from downloading its dependencies. I overrode the DENO_DIR environment variable and had systemd create a cache directory, and now it works.
Also, the access token is now no longer provided as a string, but inside an encrypted .env file. The dotenv file, however, is not loaded by Deno, but by systemd, which then passes it to the Deno process as usual.
Thanks to @jak2k for a lot of pointers to these systemd features!
It turns out the library hiccups when using a RAM buffer. I now save the files to /tmp, and create a ReadStream, and now it works! The rest of the time was spent writing a Nix flake, which both wraps the package (currently using a shell script, until I figure out how to run deno compile in the offline sandbox), and contains a NixOS module, which creates a systemd timer to run the bot as a oneshot service.
I wrote code that should work™. But for some reason, I get an HTTP error 400, and the stacktrace is of no use, since it is somewhere within the event loop. Also, some time is missing from Hackatime, since it only tracks git repositories.