<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Get Info: #hack</title>
    <description>Posts tagged “hack” — Blog of independent game and app developer Matt Sephton. Featuring vintage Macintosh, game development, digital artwork, Japanese esoterica, video game reviews, hacks and tips, and much more.</description>
    <link>https://blog.gingerbeardman.com/tag/hack/</link>
    <atom:link href="https://blog.gingerbeardman.com/tag/hack/index.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 01 Jul 2026 16:09:47 +0000</pubDate>
    <lastBuildDate>Wed, 01 Jul 2026 16:09:47 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>

    
      
        <item>
          <title>New (Old) 3D Golf: porting PC-9801 &amp; Virtual Boy to Mega Drive</title>
          <description>&lt;p&gt;The Japanese Mega Drive ports of T&amp;amp;E SOFT’s &lt;a href=&quot;/2024/11/09/new-3d-golf-simulation-video-game-series/&quot;&gt;New 3D Golf Simulation&lt;/a&gt; series are my favourite golf games, and recently I’ve been living inside their ROMs.&lt;/p&gt;

&lt;p&gt;As with all the craziest ideas, it began with a “I wonder if I could”… In the early hours of one April morning I managed to pull a single course out of the game—its terrain and flyby data—and reimplement it in a viewer of my own, written in Three.js. Over the following week or so of continued reverse engineering, that viewer quietly grew into something resembling a 3D golf game running in the browser. Finding the data had some big clues: we know that there are 18 holes, the distances of each hole and their sequence order, and I’d read the courses were made of ~256 points, so adding all these heuristics together meant it was much easier to find the data than finding a needle in a haystack.&lt;/p&gt;

&lt;p&gt;Understanding the data that well meant I could go the other way, too—&lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3mkgnbdzljc2o&quot;&gt;back into the original Mega Drive games&lt;/a&gt; themselves. First I added a terrain modifier. To test it I &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3mkkxeaebm22c&quot;&gt;flattened the entire course like a pancake&lt;/a&gt; to confirm my understanding was correct, and then cranked it up to 11 into a sort of &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3mkpwexii4c2t&quot;&gt;“Hyperactive Terrain Mode”&lt;/a&gt; that warps the fairways into something wild. Both worked well.&lt;/p&gt;

&lt;p&gt;An early attempt changed its mind on every run; turned out I was seeding it from an uninitialised memory location. 🤦 With no debugger console to hand, I’d been hunting bugs like this the crude way—scribbling values into the cartridge’s SRAM (its battery-backed save memory) and reading them back out, a poor man’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf&lt;/code&gt;. So it wasn’t exactly straightforward.&lt;/p&gt;

&lt;p&gt;Once that was sorted, I gave the 32-year-old game some &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3mkt6k57nlc2e&quot;&gt;brand new, custom user interface&lt;/a&gt; to match.&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;HHbEVRtbw7Q&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;p&gt;Next I wondered if the course data was the same across all of the four Mega Drive games, could it be the same across the games on other platforms? The answer is &lt;strong&gt;yes&lt;/strong&gt;: &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3ml2k552qis2f&quot;&gt;the same course data format&lt;/a&gt; turns out to be used right across the series, from the original PC-9801 games (and almost certainly X68000 and FM Towns) through to the Mega Drive and even the Virtual Boy. If my (little-endian) maths is correct that’s a total of 7 unique courses, all sharing one format. There’s some reformatting that needs to be done, but the data structure is the same. And since I could already read the courses, I could write them too—patching the games to pick a course at random, or to load one that was never available on the Mega Drive in the first place. PC-9801 to Mega Drive required sorting the polygons to match how they were expected to be stored.&lt;/p&gt;

&lt;p&gt;But I guess T&amp;amp;E SOFT used the same POLYSYS-CAD software to design all the courses over several years? I love how such a tool could have that sort of longevity.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/new-old-golf-polysyscad.jpg&quot; alt=&quot;IMG&quot; title=&quot;ポリシスCAD (POLYSYS-CAD) PC software used to design hole topology mesh of only ~256 points&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;That last part is the really fun bit. (Can this even &lt;em&gt;be&lt;/em&gt; more fun?)&lt;/p&gt;

&lt;p&gt;Here are three courses running on the Mega Drive for the first time:&lt;/p&gt;

&lt;h2 id=&quot;te-selection&quot;&gt;T&amp;amp;E Selection&lt;/h2&gt;

&lt;p&gt;Extracted from the &lt;a href=&quot;https://www.mobygames.com/game/102547/new-3d-golf-simulation-te-selection/&quot;&gt;NEC PC-9801 add-on course disk&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This course is somewhat unique as it has messages spelled using coloured topology:&lt;br /&gt;
the 1st has “GO!” by the tee position; the 18th has “T&amp;amp;E” just beyond the final green&lt;/p&gt;
&lt;/blockquote&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;duXwfq-F-CA&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;eight-lakes-gc&quot;&gt;Eight Lakes G.C.&lt;/h2&gt;

&lt;p&gt;Also extracted from &lt;a href=&quot;https://www.mobygames.com/game/71396/new-3d-golf-simulation-eight-lakes-gc/&quot;&gt;NEC PC-9801 add-on course disk&lt;/a&gt;:&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;J0PliXErDNU&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;blockquote&gt;
  &lt;p&gt;A fact perhaps only I care about: &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3mmmt2mkrzc2z&quot;&gt;during development, prior to Feb 1990, it was &lt;em&gt;Seven Lakes G.C.&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/new-old-golf-seven-lakes.webp&quot; alt=&quot;Seven Lakes G.C.&quot; title=&quot;Seven Lakes G.C., as seen in Comptiq Vol. 63 &amp;amp; Oh! PC Issue 117&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;papillon-cc&quot;&gt;Papillon C.C.&lt;/h2&gt;

&lt;p&gt;Extracted from the Nintendo Virtual Boy game &lt;a href=&quot;https://www.mobygames.com/game/15306/golf/&quot;&gt;T&amp;amp;E Virtual Golf&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s called Papillon—the French word for butterfly—because the course holes were laid out in the shape of a butterfly. Which was surely a nod to the shape of the Virtual Boy controller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;8Hpnm4w4EDU&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;p&gt;That last one needed a little extra work. T&amp;amp;E Golf on Virtual Boy doesn’t have a hole flyby, so I had to generate the camera path myself: a bezier curve from tee to pin, nudged towards the centre point of the visible course as it appears on the mini-map. The flyby path in this video was about half way to my final solution.&lt;/p&gt;

&lt;p&gt;Playing these courses on Mega Drive is truly special and the effort was very much worthwhile. 🥰&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;a-few-things-i-learned-along-the-way&quot;&gt;A few things I learned along the way&lt;/h2&gt;

&lt;p&gt;Living inside the disassembly for weeks, I kept tripping over the little decisions T&amp;amp;E SOFT made all those years ago. Some are clever, some are quietly bonkers, and all of them made me grin:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The hole is three times too big.&lt;/strong&gt; The cup grabs any ball within ~6.7 inches—triple a real hole’s radius—so balls drop from further out than they look. A fudge for the 320×224 screen, where ball and cup were both sub-pixel.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The polygons pre-sort themselves.&lt;/strong&gt; No depth buffer on the Mega Drive, so the draw order is baked into the course data, back-to-front (the painter’s algorithm). The giveaway: it doesn’t match the original PC-9801 CAD order.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Backspin can reverse a putt.&lt;/strong&gt; Spin isn’t cosmetic: it’s fed back into the roll and can make the ball check up and trickle backwards. Real ballistic physics in a 1993 cartridge. Love it!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Water isn’t a hazard—just very sticky.&lt;/strong&gt; There’s no “in the water” state; water polygons carry friction so high it kills the ball in one frame. The penalty falls out of the ordinary maths.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Wind is a real force, not an aim fudge.&lt;/strong&gt; It becomes a horizontal acceleration applied every frame of flight, exactly like gravity.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Augusta’s wind never actually changes.&lt;/strong&gt; The direction is never written—only strength varies. The arrow only seems to swing because it’s drawn relative to the camera.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Bunkers plug, cart paths kick.&lt;/strong&gt; Every surface has its own bounce coefficient. The fairway hands back a healthy ~40% of the ball’s speed; a bunker returns only ~10%, so the ball plugs where it lands; a cart path or rock fires it back at ~75% for that horrible hard skip.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Your lie quietly rolls the dice.&lt;/strong&gt; On every stroke the game picks a random number from a per-(lie, club) range and folds it into your swing power. A clean fairway lie uses a narrow range; a bad lie widens it—so the rough genuinely makes your shots less predictable. The ranges live in a 17×17 table, one entry per lie-and-club combination.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;four-volumes-one-evolving-engine&quot;&gt;Four volumes, one evolving engine&lt;/h2&gt;

&lt;p&gt;It’s tempting to treat the four Mega Drive games as a single engine with interchangeable courses. They’re not, and the very first line of the cross-volume notes I kept is a warning to myself: ⚠️ &lt;em&gt;never assume all four ROMs share code or data layouts.&lt;/em&gt; T&amp;amp;E SOFT kept tinkering release to release, and you only catch it by dumping the same region in all four disassemblies and diffing.&lt;/p&gt;

&lt;p&gt;The ROM headers number them &lt;em&gt;New 3D Golf Simulation&lt;/em&gt; Vol.1–4, and each header also carries a build date stamped in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY.MMM&lt;/code&gt; form. Here’s the curiosity: the volume numbers track the &lt;strong&gt;build&lt;/strong&gt; dates, not the retail release dates. Vol.2 &lt;em&gt;Devil’s Course&lt;/em&gt; was finished a month before Vol.3 &lt;em&gt;Augusta&lt;/em&gt;—but reached the shops a month after it:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Vol&lt;/th&gt;
      &lt;th&gt;Title&lt;/th&gt;
      &lt;th&gt;Japanese&lt;/th&gt;
      &lt;th&gt;ROM build&lt;/th&gt;
      &lt;th&gt;Retail release&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;Pebble Beach no Hatou&lt;/td&gt;
      &lt;td&gt;ペブルビーチの波濤&lt;/td&gt;
      &lt;td&gt;1993-07&lt;/td&gt;
      &lt;td&gt;1993-10-29&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;Devil’s Course&lt;/td&gt;
      &lt;td&gt;デビルズコース&lt;/td&gt;
      &lt;td&gt;1993-08&lt;/td&gt;
      &lt;td&gt;1994-01-28&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;Harukanaru Augusta&lt;/td&gt;
      &lt;td&gt;遙かなるオーガスタ&lt;/td&gt;
      &lt;td&gt;1993-09&lt;/td&gt;
      &lt;td&gt;1993-12-17&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;Waialae no Kiseki&lt;/td&gt;
      &lt;td&gt;ワイアラエの奇蹟&lt;/td&gt;
      &lt;td&gt;1993-09&lt;/td&gt;
      &lt;td&gt;1994-02-25&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A couple of header quirks fell out of this. Pebble’s stamp reads &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1993.JLY&lt;/code&gt;—Sega’s own oddball abbreviation for July. And while three of the carts credit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SEGA&lt;/code&gt;, &lt;em&gt;Augusta&lt;/em&gt; credits T&amp;amp;E Soft’s Sega licensee code &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T-114&lt;/code&gt; instead—a clue that it alone was self-published by T&amp;amp;E SOFT rather than by Sega. The boxes agree: Augusta’s isn’t Sega-branded either.&lt;/p&gt;

&lt;p&gt;Two places they genuinely diverge, each confirmed by dumping the same region in all four:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Colour isn’t a plain palette lookup—and the recipe is per-game.&lt;/strong&gt; A surface byte runs through a little chain of lookup tables before it becomes a pen colour, and those tables aren’t shared: Pebble grades several surfaces differently and even reorders two entries, while Devil’s Course carries its own darker, redder palette. Waialae, charmingly, reuses a single palette three times where its siblings have three distinct ones.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;A decoder bug only Pebble could trigger.&lt;/strong&gt; In the polygon stream, vertex indices are single bytes, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xFF&lt;/code&gt; acting as an escape prefix—the byte after it encodes a higher index (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xE0 + xx&lt;/code&gt;), so a hole can point past the ~254 vertices a lone byte can name. My extractor mishandled that escaped range, but only Pebble’s holes are dense enough to actually &lt;em&gt;use&lt;/em&gt; it—so the bug sailed through the other three games and only fell over when I reached Pebble. Same encoding in every cart; one course’s data was all it took to expose the flaw in my reader.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also the US release, &lt;em&gt;Pebble Beach Golf Links&lt;/em&gt; (header stamped 1993-11, likely on shelves 1994-04): the same course data on a larger ROM, with English strings present where the Japanese Vol.1 zeroed them. That parallel made a useful “Rosetta Stone” for decoding menus and text.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;inside-waialae&quot;&gt;Inside Waialae&lt;/h2&gt;

&lt;p&gt;Waialae was my primary reference—1,572,864 bytes, header &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NEW 3D GOLF SIMULATION Vol.4 Waialae C.C.&lt;/code&gt;, serial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GM G-5529&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each hole is reached through four ROM pointers, one per data block, and they’re wildly different sizes. Block 0 is the vertex list—244 XYZ points, the ~256-point mesh, about 1.5 KB. Block 1 is the bulk of it: sixteen view-order streams (one draw order per camera angle) that bake in the back-to-front sorting—around 5.5 KB, bigger than the geometry it orders. Block 2 holds the mesh and sprites themselves (230 polygons plus 54 sprites), ~1.8 KB. Block 3 is just the flyby keyframes, a slim ~0.7 KB. For Waialae’s first hole that comes to about 9.2 KB, split like this:&lt;/p&gt;

&lt;svg viewBox=&quot;0 0 740 94&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;holeDesc&quot; style=&quot;display:block;margin:0 auto;width:100%;max-width:740px;height:auto;font-family:-apple-system,BlinkMacSystemFont,&apos;Segoe UI&apos;,Helvetica,Arial,sans-serif&quot;&gt;
  &lt;desc id=&quot;holeDesc&quot;&gt;One bar representing a hole&apos;s data for Waialae hole 1, split into four segments by size: Block 0 vertex list 1,466 bytes; Block 1 view-order streams 5,490 bytes; Block 2 mesh and sprites 1,758 bytes; Block 3 flyby keyframes 666 bytes.&lt;/desc&gt;
  &lt;rect x=&quot;12&quot; y=&quot;8&quot; width=&quot;112&quot; height=&quot;78&quot; fill=&quot;#c5e0b4&quot; stroke=&quot;#2f5e22&quot; /&gt;
  &lt;rect x=&quot;124&quot; y=&quot;8&quot; width=&quot;419&quot; height=&quot;78&quot; fill=&quot;#538135&quot; stroke=&quot;#2f5e22&quot; /&gt;
  &lt;rect x=&quot;543&quot; y=&quot;8&quot; width=&quot;134&quot; height=&quot;78&quot; fill=&quot;#70ad47&quot; stroke=&quot;#2f5e22&quot; /&gt;
  &lt;rect x=&quot;677&quot; y=&quot;8&quot; width=&quot;51&quot; height=&quot;78&quot; fill=&quot;#a9d18e&quot; stroke=&quot;#2f5e22&quot; /&gt;
  &lt;text x=&quot;22&quot; y=&quot;28&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#1f3b14&quot;&gt;Block 0&lt;/text&gt;
  &lt;text x=&quot;22&quot; y=&quot;46&quot; font-size=&quot;11&quot; fill=&quot;#33521f&quot;&gt;Vertex list&lt;/text&gt;
  &lt;text x=&quot;22&quot; y=&quot;64&quot; font-size=&quot;11&quot; fill=&quot;#33521f&quot;&gt;1,466 B&lt;/text&gt;
  &lt;text x=&quot;134&quot; y=&quot;28&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#ffffff&quot;&gt;Block 1 · View-order streams&lt;/text&gt;
  &lt;text x=&quot;134&quot; y=&quot;46&quot; font-size=&quot;11&quot; fill=&quot;#e7f2dd&quot;&gt;one draw order per camera angle (×16)&lt;/text&gt;
  &lt;text x=&quot;134&quot; y=&quot;64&quot; font-size=&quot;11&quot; fill=&quot;#e7f2dd&quot;&gt;5,490 B&lt;/text&gt;
  &lt;text x=&quot;553&quot; y=&quot;28&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#14300a&quot;&gt;Block 2&lt;/text&gt;
  &lt;text x=&quot;553&quot; y=&quot;46&quot; font-size=&quot;11&quot; fill=&quot;#14300a&quot;&gt;Mesh + sprites&lt;/text&gt;
  &lt;text x=&quot;553&quot; y=&quot;64&quot; font-size=&quot;11&quot; fill=&quot;#14300a&quot;&gt;1,758 B&lt;/text&gt;
  &lt;text x=&quot;683&quot; y=&quot;28&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#1f3b14&quot;&gt;Block 3&lt;/text&gt;
  &lt;text x=&quot;683&quot; y=&quot;46&quot; font-size=&quot;11&quot; fill=&quot;#1f3b14&quot;&gt;Flyby&lt;/text&gt;
  &lt;text x=&quot;683&quot; y=&quot;64&quot; font-size=&quot;11&quot; fill=&quot;#1f3b14&quot;&gt;666 B&lt;/text&gt;
&lt;/svg&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;A couple more structural quirks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;A spatial grid, decades early.&lt;/strong&gt; Immediately after the vertex pool sits a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count × 16&lt;/code&gt; word offsets into the face section—a two-level spatial grid (cell → faces) so the engine can look up the relevant polygons from the ball’s (x, z) without walking the whole hole.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Why the SRAM debugging hurt.&lt;/strong&gt; Waialae’s battery-backed save RAM is odd-lane only, from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$200001&lt;/code&gt;. Byte writes have to land on odd Mega Drive addresses; even-address writes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$200000&lt;/code&gt; simply disappear. That’s the real reason scribbling values into SRAM as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf&lt;/code&gt; substitute was so finicky—half my early writes were going into the void. (BlastEm helpfully flushes SRAM to disk on quit, so I could read it back from the host.)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;variable-zoom&quot;&gt;Variable zoom&lt;/h2&gt;

&lt;p&gt;The shared course format is what let me move holes between platforms, but each machine scales the world differently. The proven case: Waialae hole 1 from the PC-9801 drops into the Mega Drive after a fixed &lt;strong&gt;1.6× rescale on X and Z&lt;/strong&gt; (Y untouched), plus a &lt;strong&gt;little-endian → big-endian flip&lt;/strong&gt; on the flyby path records.&lt;/p&gt;

&lt;p&gt;Lining those transplanted polygons up against the stock Mega Drive ones is also what &lt;em&gt;proved&lt;/em&gt; the rendering trick I mentioned earlier: the Mega Drive packs faces in descending max-Z order—back to front, the painter’s algorithm—and the original PC-9801 face id survives the journey as the Mega Drive’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attr1&lt;/code&gt; byte.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;two-deeper-cuts&quot;&gt;Two deeper cuts&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The flyby camera, decoded by statistics.&lt;/strong&gt; Each flyby keyframe carries two mystery bytes. With no documentation, I histogrammed 4,723 of them across every hole and the shape gave it away: one byte is an 8-bit angle (256 units = 360°) for yaw, the other a signed pitch clamped to about ±40, positive meaning the camera looks down. Educated guessing, with visuals.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The Virtual Boy world is built at a different scale.&lt;/strong&gt; The Virtual Boy stores its courses at 32 raw units per yard, where the Mega Drive works in 17—so Papillon has to be shrunk by exactly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;17/32&lt;/code&gt; (0.53) to sit correctly on the Mega Drive, otherwise every club hits too short for the hole. (My first attempt used the wrong unit and reported hole 1 as 321 yards instead of its true 360.) It’s the same idea as the 1.6× I needed coming the other way from the PC-9801—one shared format, but every machine measures its yards differently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing ran on rizin and vasmm68k with BlastEm for execution—though frame-time profiling had to move to Genesis Plus GX, because BlastEm freezes the VDP’s HV counter during the long rendering routines I was trying to measure.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-the-old-magazines-turned-up&quot;&gt;What the old magazines turned up&lt;/h2&gt;

&lt;p&gt;Reverse engineering only tells you &lt;em&gt;what&lt;/em&gt; the games do; for the &lt;em&gt;why&lt;/em&gt;, I went digging through a stack of Japanese computer magazines from the era, OCRing the scans to pull out the text. A 1989 developer interview about &lt;em&gt;Harukanaru Augusta&lt;/em&gt; (遙かなるオーガスタ)—the PC-9801 original that kicked off the series—turned out to be a goldmine:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The 3D engine came first.&lt;/strong&gt; T&amp;amp;E’s POLYSYS pre-dated the golf games by a couple of years, already appearing—only in the 3D intro logos, as far as I can tell—in &lt;em&gt;DAIVA STORY 7: Light of Kali Yuga&lt;/em&gt; and &lt;em&gt;Psy-O-Blade&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Trees were nearly real 3D.&lt;/strong&gt; They tried modelling trees as polygons, leaves and all—but one tree took as long to draw as a whole screen. So scaled sprites were used instead.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The first game had no hills.&lt;/strong&gt; T&amp;amp;E’s &lt;em&gt;3-D Golf Simulation&lt;/em&gt;, written in BASIC six years earlier, had no terrain undulation at all—and on the Sharp X1, 18 holes took &lt;em&gt;half a day&lt;/em&gt; to play through.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;POLYSYS was meant to be general-purpose:&lt;/strong&gt; swap the data and it renders anything. T&amp;amp;E planned an RPG and a shooter on it and intended to license it to other software houses.&lt;/li&gt;
  &lt;li&gt;One programmer, mostly: &lt;strong&gt;Eiji Kato&lt;/strong&gt; (加藤英治).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the &lt;em&gt;Augusta&lt;/em&gt; course itself came with a wonderful backstory:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;They licensed the real thing.&lt;/strong&gt; An official contract with Augusta National, working from the club’s blueprints. Staff visited, didn’t play, but “rubbed their cheeks on the grass.”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Augusta sent back ~60 corrections:&lt;/strong&gt; eg. pine trees too short and too spread out, flowers too pink, bunker sand the wrong colour.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No do-overs, by design.&lt;/strong&gt; You could save mid-round, but loading erased the save data—so no replaying holes to pad your score.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Best of all:&lt;/strong&gt; the dev build’s four caddies were all women. Augusta’s are all men, so the final game swapped them. The ladies returned in the expansion courses and Mega Drive games.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The T&amp;amp;E Selection caddies are real people:&lt;/strong&gt; four women who worked at &lt;strong&gt;Brother Industries&lt;/strong&gt;—whose &lt;strong&gt;TAKERU&lt;/strong&gt; software vending machines sold these add-on course disks.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What Next?&lt;/h2&gt;

&lt;p&gt;There’s an extra bit of hacking I’m working on but am unsure if it will lead to anything, but if it does it will need a post all of its own. Hold your thumbs. Fingers crossed. 🤞&lt;/p&gt;

&lt;p&gt;It would be possible to release a small script which given both original games would do the extraction and patching, but for now I don’t feel comfortable doing that. I still need to figure out the correct tree mapping for each game, decide which of the four Mega Drive games is most suited to each of the three new courses, add new title screens and a few more bits of detail work.&lt;/p&gt;

&lt;p&gt;I’d love to &lt;a href=&quot;https://bsky.app/profile/gingerbeardman.com/post/3mnhbioqr4s2f&quot;&gt;see these ported courses released officially&lt;/a&gt; some day—the series IP is now owned and managed by D4 Enterprise—so if you know anybody there please hook us up! If you are an employee of D4 Enterprise then please check my request to license the IP. 🙏&lt;/p&gt;

&lt;p&gt;There are more period games in the series that I’d like to take a look at to see if they use the same data format, or modify it in any specific way. SNES and 3DO seem to be the most interesting. 🧐&lt;/p&gt;

&lt;p&gt;But for now it’s just me, a pile of disassembly files, rizin and vasmm68k, the BlastEm emulator, and a soft spot for blue skies and FM synth — still trying to get the ball in the hole. ⛳️🏌️‍♂️&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 19 Jun 2026 16:26:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/06/19/new-old-3d-golf-porting-pc-9801-and-virtual-boy-to-mega-drive/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/06/19/new-old-3d-golf-porting-pc-9801-and-virtual-boy-to-mega-drive/</guid>
        </item>
      
    
      
        <item>
          <title>Passing Cloudflare Turnstile using two fingers</title>
          <description>&lt;p&gt;Here’s a quick tip for passing the infamous Cloudflare Turnstile captcha checkbox pages:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tab&lt;/code&gt; key, to focus the checkbox (I use my index finger)&lt;/li&gt;
  &lt;li&gt;press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Space&lt;/code&gt; bar, to select the checkbox (I use my thumb)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;…no mouse movement or clicking needed!&lt;/p&gt;

&lt;p&gt;✌️&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/cloudflare-turnstile-two-fingers.png&quot; alt=&quot;Cloudflare Turnstile checkbox&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 15 Jun 2026 17:33:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/06/15/passing-cloudflare-turnstile-using-two-fingers/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/06/15/passing-cloudflare-turnstile-using-two-fingers/</guid>
        </item>
      
    
      
        <item>
          <title>Controlling local web servers using xbar</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/localmost/&quot;&gt;gingerbeardman.com/apps/localmost/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes I want to run local web servers for projects I’m working on. Usually more than one at a time, or at least over a short space of time.&lt;/p&gt;

&lt;p&gt;So I thought it would be cool to have a controller for those local servers in my menu bar. Sounded like the perfect job for a little scripting and &lt;a href=&quot;https://github.com/matryer/xbar&quot;&gt;xbar&lt;/a&gt;, which is a great way to prove a menubar app idea quickly.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;The plugin allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;toggle servers on and off&lt;/li&gt;
  &lt;li&gt;open in browser&lt;/li&gt;
  &lt;li&gt;view ports&lt;/li&gt;
  &lt;li&gt;view paths&lt;/li&gt;
  &lt;li&gt;view log sizes&lt;/li&gt;
  &lt;li&gt;clear logs&lt;/li&gt;
  &lt;li&gt;edit config&lt;/li&gt;
  &lt;li&gt;and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-localhost-dark.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;example-config&quot;&gt;Example config&lt;/h2&gt;

&lt;p&gt;In the config file we set the starting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_PORT&lt;/code&gt;, followed by one or more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_DIR&lt;/code&gt; for as many projects as you might want servers. We can temporarily comment out those server lines to prevent projects from appearing in the menu.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# .xbar_httpd_config&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;8000
&lt;span class=&quot;c&quot;&gt;#SERVER_DIR=~/Projects/starchasers/&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/serenity/
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/point-cloud/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;source-code&quot;&gt;Source code&lt;/h2&gt;

&lt;p&gt;Python source code is available in the following gist:&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 12 Jan 2026 20:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</guid>
        </item>
      
    
      
        <item>
          <title>Remove Comments Extension for Nova editor</title>
          <description>&lt;p&gt;I’ve released a new extension for the Nova editor.&lt;/p&gt;

&lt;p&gt;It’s called &lt;em&gt;Remove Comments&lt;/em&gt; and it …removes comments from the current line, or selected lines, in your code. That’s it!&lt;/p&gt;

&lt;p&gt;Oh, I’ve tried to support as many comment/syntax formats as I can think of. Sadly it’s not possible to get the current comment formatting from the Nova API, so I had to roll my own logic and heuristics.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.remove-comments/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.remove-comments/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript (C-style comments):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This comment will be removed&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This trailing comment will be removed&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* This block comment will be removed */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Python (hash-style comments):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# This comment will be removed
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This trailing comment will be removed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;HTML:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This comment will be removed --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This trailing comment will be removed --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 08 Dec 2025 15:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/12/08/remove-comments-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/12/08/remove-comments-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Three.js Completions Extension for Nova editor</title>
          <description>&lt;p&gt;I’ve released an extension to add &lt;a href=&quot;https://threejs.org&quot;&gt;Three.js&lt;/a&gt; completions support to the Nova editor.&lt;/p&gt;

&lt;p&gt;The coolest thing about this extension is that most of the code is automatically generated straight from the Three.js TypeScript files. This means there’s the potential for less errors in the data, and I can easily update the extension whenever a new release of Three.js comes out just by running a single command. Why work &lt;em&gt;harder&lt;/em&gt; when you can work &lt;em&gt;cleverer&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.ThreeJS/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.ThreeJS/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might be quick to deduce that—yes—I’m making a 3D game. 😘&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 04 Oct 2025 15:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/10/04/three-js-completions-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/10/04/three-js-completions-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Updates to Extensions for Nova editor</title>
          <description>&lt;p&gt;In late 2024, I spent some time improving my tools by &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;building a set of extensions for Nova editor&lt;/a&gt; to streamline some time-consuming tasks I encounter during blogging and game development.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-extensions.png&quot; alt=&quot;IMG&quot; title=&quot;My current list of extensions for Nova editor&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This week, I released major updates to many of them with enhancements and new features:&lt;/p&gt;

&lt;h2 id=&quot;bookmarks&quot;&gt;Bookmarks&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Adds sorting, the ability to bookmark folders, marking of missing items, and other improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;markdown-file-linker&quot;&gt;Markdown File Linker&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Results in the choice palette are now sorted by default using dates found in filenames.&lt;/li&gt;
  &lt;li&gt;Along with other filtering improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;unwrap-paragraph&quot;&gt;Unwrap Paragraph&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can now unwrap the text surrounding cursor, which removes some friction.&lt;/li&gt;
  &lt;li&gt;In selected text multiple paragraphs will be unwrapped individually.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;word-counter&quot;&gt;Word Counter&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Watched words can be managed through the sidebar, added from selected text, or via typing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;yaml-tag-picker&quot;&gt;YAML Tag Picker&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can quickly insert or update the creation/modified ISO date in your front matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Plus!&lt;/em&gt; I also released a brand new extension:&lt;/p&gt;

&lt;h2 id=&quot;csv-to-md-table&quot;&gt;CSV to MD table&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.csv2md/&quot;&gt;Version 1.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Convert tabular data between CSV/TSV and Markdown formats.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 30 Aug 2025 15:03:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/08/30/updates-to-my-extensions-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/08/30/updates-to-my-extensions-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>iOS Low Data Mode reveals true app update sizes</title>
          <description>&lt;p&gt;An interesting side-effect of iOS Low Data Mode is that it shows you the exact size of an app update for your device, often a tiny fraction of the listed app size. This is because (delta) updates contain only the files that have changed. Even a first-time install is smaller due to app slicing/thinning containing only what is needed for that device.&lt;/p&gt;

&lt;p&gt;The larger size is the universal app size containing assets for all devices. It’s simpler and less confusing for everybody to see the same size.&lt;/p&gt;

&lt;p&gt;There are more screenshots at the comment links.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/low-data-mode.png&quot; alt=&quot;IMG&quot; title=&quot;eBay 6.220.0 update size for an iPhone XS running iOS 17&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 06 Aug 2025 19:43:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/08/06/ios-low-data-mode-reveals-true-app-update-sizes/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/08/06/ios-low-data-mode-reveals-true-app-update-sizes/</guid>
        </item>
      
    
      
        <item>
          <title>Amazfit activity tracker and watch face asset generation</title>
          <description>&lt;p&gt;I recently wanted to track my heart rate and sleeping patterns, but do not want an Apple Watch. So I ended up buying a cheap &lt;a href=&quot;https://uk.amazfit.com/products/amazfit-band-7&quot;&gt;Amazfit Band 7&lt;/a&gt; from &lt;a href=&quot;https://www.decathlon.co.uk/p/activity-tracker-band-7/_/R-p-349533?mc=8828106&quot;&gt;Decathlon&lt;/a&gt; sports store. The one I bought was reduced because of a damaged box—I love a good bargain!&lt;/p&gt;

&lt;p&gt;It’s a cool little thing and goes for an unbelievable 18 days on a single charge with default settings. Since I bought it I’ve had to charge it once! Even more amazing is that this little thing runs apps and watch faces powered by JavaScript, has an OLED screen, senses heart rate, step count, blood oxygen, sleep patterns, and lots more features I’ll never use. And it still gets multiple weeks on a single charge! The app it comes with is fairly low-nag if you don’t want to buy into the ecosystem.&lt;/p&gt;

&lt;p&gt;Anyway, I found some &lt;a href=&quot;https://amazfitwatchfaces.com/amazfit-band/view/58&quot;&gt;nice watch faces&lt;/a&gt; but if anything they were all a little bit busy for me. Ideally I’d make my own but it seemed like a lot of work—I would need to create images for every digit at every size as well as any other things I want on the face—so I put it on the rainy day list.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;ok-computer&quot;&gt;OK, Computer&lt;/h2&gt;

&lt;p&gt;Today it isn’t raining but I do have some time on my hands, which is the next best thing. I didn’t want to spend time in Figma exporting images over and over again. I get cold shivers thinking about slicing images in &lt;a href=&quot;https://en.wikipedia.org/wiki/Macromedia_Fireworks&quot;&gt;Fireworks&lt;/a&gt;. So I wondered if I could create a tool that would make light work of generating the images I need from a font of my choosing.&lt;/p&gt;

&lt;p&gt;It was pretty straightforward and I ended up with what you see below, where you can customise: font, size, color, alignment, filename prefix, and required characters/phrases/symbols. And then you can download all the images in one click! &lt;em&gt;NICE.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I do my best to adhere to the specs required by the most common watch face building tools, and in almost all cases I’ve been able to use the images exactly as they come out of the tool. There is some extraneous padding, which means you have to use quite large negative spacing (equivalent to kerning) but it all works well in practice.&lt;/p&gt;

&lt;p&gt;There’s nothing that ties the assets being generated to the Amazfit Band 7, so feel free to use the tool for other purposes if you want to just generate some digits, text, or symbols as images.&lt;/p&gt;

&lt;p&gt;The web app tool is at &lt;a href=&quot;https://www.gingerbeardman.com/amazfit/&quot;&gt;gingerbeardman.com/amazfit/&lt;/a&gt; if you want to try it out.&lt;/p&gt;

&lt;p class=&quot;screen&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/amazfit-web-app.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;heres-one-i-made-earlier&quot;&gt;Here’s one I made earlier&lt;/h2&gt;

&lt;p&gt;This is the first face I created using the tool: DIN. I’m generating images of numbers, and a few other things, in a few different sizes and two colours. The darker text might look too dark on your display, but on the OLED display of the device it’s plenty bright enough for secondary information. I used a &lt;a href=&quot;https://amazfitwatchfaces.com/forum/viewtopic.php?t=2743&quot;&gt;Windows tool&lt;/a&gt; to lay things out and generate the package, and &lt;a href=&quot;https://amazfitwatchfaces.com/forum/viewtopic.php?t=3873&quot;&gt;installed it locally&lt;/a&gt; in developer mode on my device.&lt;/p&gt;

&lt;p&gt;You can install &lt;a href=&quot;https://amazfitwatchfaces.com/amazfit-band/view/353&quot;&gt;the watch face&lt;/a&gt; using the &lt;a href=&quot;https://amazfitwatchfaces.com/awapp&quot;&gt;AmazFaces app&lt;/a&gt; on iOS or Android. It’s also available on the official watch face store in the &lt;a href=&quot;https://apps.apple.com/gb/app/zepp/id1127269366&quot;&gt;Zepp app&lt;/a&gt;.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/amazfit-band-7-din.png&quot; alt=&quot;IMG&quot; title=&quot;DIN looks super on OLED!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The typeface is &lt;a href=&quot;https://github.com/amcchord/datto-d-din&quot;&gt;D-DIN Condensed Bold&lt;/a&gt;—a cool cut of &lt;a href=&quot;https://en.wikipedia.org/wiki/DIN_typeface&quot;&gt;DIN&lt;/a&gt; and it’s free and released under &lt;a href=&quot;https://en.wikipedia.org/wiki/SIL_Open_Font_License&quot;&gt;SIL Open Font License&lt;/a&gt;. Thanks very much to Datto and to the type designer, &lt;a href=&quot;https://luc.devroye.org/showcase-charlesnix/&quot;&gt;Charles Nix&lt;/a&gt; at &lt;a href=&quot;https://www.monotype.com/studio/charles-nix&quot;&gt;Monotype&lt;/a&gt;!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 11 Apr 2025 22:05:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/04/11/amazfit-activity-tracker-and-watch-face-asset-generation/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/04/11/amazfit-activity-tracker-and-watch-face-asset-generation/</guid>
        </item>
      
    
      
        <item>
          <title>Automating the cleaning of macOS-specific files on Eject</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/driveaway/&quot;&gt;gingerbeardman.com/apps/driveaway/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dot underscore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;._&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.DS_Store&lt;/code&gt; files are macOS-specific metadata cruft generated for foreign filesystems (like FAT32 or exFAT) that are not usually needed for disks that are mainly used on other platforms. Digital cameras, music players, e-book readers, and handheld gaming devices can get confused when they encounter these odd files during file system parsing and directory listing. The problem is compounded if the devices naïvely process files by looking only at the file extension as they will then see the dot underscore version of a file as a duplicate and try to preview/play/open it.&lt;/p&gt;

&lt;p&gt;For years I’ve used an app called &lt;a href=&quot;https://web.archive.org/web/20250208072547/https://macpaw.com/cleanmymac&quot;&gt;CleanMyDrive&lt;/a&gt; to remove such files, but &lt;a href=&quot;https://macpaw.com/news/cleanmydrive-no-longer-developed&quot;&gt;it was discontinued in October 2023&lt;/a&gt;. I continued to use it until it recently stopped working completely …so I needed to find an alternative solution. There are some apps on the Mac App Store that look like they’ll do the trick, but I don’t really want to spend the time buying and trialling multiple apps to find one that fits my usage habits. I can make one!&lt;/p&gt;

&lt;p&gt;I already use an app called &lt;a href=&quot;https://xbarapp.com&quot;&gt;xbar&lt;/a&gt; for keeping track of my GitHub issues, itch.io sales, network/server status, and more. So I decided to flex my shell script muscles and put together an xbar script to do it.&lt;/p&gt;

&lt;p&gt;The script adds a menu bar item that allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Eject (click)&lt;/li&gt;
  &lt;li&gt;Unmount (option-click)&lt;/li&gt;
  &lt;li&gt;Eject All (without cleaning, useful when you want to disconnect all drives from your computer)&lt;/li&gt;
  &lt;li&gt;All with a handy notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! Straight to the point, no frills, functional software.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-volumes.png&quot; alt=&quot;IMG&quot; title=&quot; &quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/610f22180117ad20465d7c529cc5faa0.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 08 Feb 2025 20:45:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/02/08/automating-the-cleaning-of-macos-specific-files-on-eject/</guid>
        </item>
      
    
      
        <item>
          <title>Extensions for Nova editor</title>
          <description>&lt;p&gt;I’m a big believer in solving problems yourself if it’s possible rather than waiting for app updates that might never arrive. Making extensions for the &lt;a href=&quot;https://nova.app&quot;&gt;Nova editor&lt;/a&gt; that I do most of my programming and blogging in is so much fun! So, here are some of my own creation:&lt;/p&gt;

&lt;p&gt;View all: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;yaml-tag-picker&quot;&gt;YAML Tag Picker&lt;/h2&gt;

&lt;p&gt;Allows you to easily select tags for the front matter in your blog posts. It scans your existing posts for tags and presents them in a Choice Palette, making it easy to maintain consistent tagging across your blog. Nice!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yaml-tag-picker.png&quot; alt=&quot;IMG&quot; title=&quot;Searching existing tags for the word “play”&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;markdown-file-linker&quot;&gt;Markdown File Linker&lt;/h2&gt;

&lt;p&gt;Allows you to insert links to local files as Markdown, perfect for linking between articles in your Jekyll blog! Also includes the ability to wrap the current text in a link, and if there’s a URL on the clipboard that’ll be used as the link destination.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You choose a local file using the file selector, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users/matt/Projects/blog/_posts/2023/2023-11-21-yoyozo-how-i-made-a-playdate-game-in-39kb.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it will be inserted as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve selected some text before invoking the extension, you’ll get:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[YOYOZO](/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an image you might end up with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;![IMG](∕images/posts/yoyozo-teaser.gif)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;reindent-o-matic&quot;&gt;Reindent-o-matic&lt;/h2&gt;

&lt;p&gt;Allows you to apply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.editorconfig&lt;/code&gt; indent rules to the current file, or all files matching specific extensions. Important: changes are applied but not saved, giving you the opportunity to review.&lt;/p&gt;

&lt;p&gt;Useful when reusing code from one project that used space indentation to a new one that uses tab indentation. Ideally Nova should do this automatically, and sometimes it does, but not every time for reasons I can’t figure out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Reindent-o-matic/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Reindent-o-matic/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;toggle-scroll-bars&quot;&gt;Toggle Scroll Bars&lt;/h2&gt;

&lt;p&gt;Allows you to see the scrollbar at all times. This is useful because the scrollbar also contains source control change markers. With scrollbars always visible you can more easily locate changes across the entire length of your document.&lt;/p&gt;

&lt;p&gt;This has already saved me so much time and effort and I’ve only been using the editor this way for a week. So I thought I’d create an extension to make it easier for other people to enjoy this usability improvement.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.scrollbars/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.scrollbars/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scroll Bars on/off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/scrollbars-on-minimap-off.png&quot; alt=&quot;on-off&quot; /&gt; &lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/scrollbars-off-minimap-off.png&quot; alt=&quot;off-off&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;unwrap-paragraph&quot;&gt;Unwrap Paragraph&lt;/h2&gt;

&lt;p&gt;OK, I got carried away. This one adds the ability to merge multiple lines of selected text into one.&lt;/p&gt;

&lt;p&gt;This is useful for combining multiple lines of data into one, or reflowing a paragraph of text that has had manual line breaks applied such as something pasted from an old email. It also colalesces areas of white space into a single space. So, it’s not just “Join Lines” but something more specific.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;TextMate&lt;/em&gt; editor this command is called Unwrap Paragraph, so I’ve kept the same name.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;Text, before:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	What 
exactly does  
this   extension	
do with the
text?  🤔
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Text, after:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;What exactly does this extension do with the text? 🤔
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source code, before:&lt;/p&gt;
&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x202&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x451&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x505&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x088&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source code, after:&lt;/p&gt;
&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x202&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x451&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x505&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x088&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;filter-through-command&quot;&gt;Filter Through Command&lt;/h2&gt;

&lt;p&gt;Run terminal commands on selected text. When you need to use a terminal command but you just want the results. The possibilities are endless as command line is your oyster! Comes with a library of useful commands including sort, base64 decode/encode, extract URLs, remove blank lines, count loc, remove HTML tags.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FilterThroughCommand/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FilterThroughCommand/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type your own command&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-filter-through-custom-command.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick from a library of commands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-filter-through-command.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;bookmarks&quot;&gt;Bookmarks&lt;/h2&gt;

&lt;p&gt;Bookmark files in the Sidebar for easier management. Ideal if the Files Sidebar is too much, or you want a simpler view of the files you are working on.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;tag-sync&quot;&gt;Tag Sync&lt;/h2&gt;

&lt;p&gt;Automatic synchronisation of closing tag when editing opening tag. Improve your coding efficiency by ensuring tag pairs stay synchronized while editing markup languages!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.tagsync/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.tagsync/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;openscad&quot;&gt;OpenSCAD&lt;/h2&gt;

&lt;p&gt;Adds language support for &lt;a href=&quot;https://openscad.org&quot;&gt;OpenSCAD&lt;/a&gt;: &lt;em&gt;The Programmers Solid 3D CAD Modeller&lt;/em&gt;, including both syntaxes (with highlighting) and completions (with alternate forms).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.openscad/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.openscad/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;macro&quot;&gt;Macro&lt;/h2&gt;

&lt;p&gt;Text editing recording and playback system. This one &lt;a href=&quot;/2024/10/24/macro-extension-for-nova-editor/&quot;&gt;has its own blog post&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;word-counter&quot;&gt;Word Counter&lt;/h2&gt;

&lt;p&gt;Count multiple words and have their tally displayed in the sidebar. This one also &lt;a href=&quot;/2024/10/27/word-counter-extension-for-nova-editor/&quot;&gt;has its own blog post&lt;/a&gt;.&lt;/p&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 17 Oct 2024 20:47:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/17/extensions-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/17/extensions-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>A haze of inspiration</title>
          <description>&lt;p&gt;A long time ago in a galaxy far, far away I had to give an impromptu presentation about something I didn’t know much about. The goal of the task was to see how well we could ad-lib a presentation under pressure. Everybody in the room had to think of a topic whilst waiting for our turn. The person next to me couldn’t think of anything and asked me what I had thought of, I said “the off-side rule” (it’s a football/soccer thing, don’t worry about it). Then that person was called before me and proceeded to give a presentation about… the off-side rule. I was mortified, and when my turn came I was still so shell shocked that I can’t remember what other topic I picked was, or even how the rest of the day went. Afterwards I chatted with the person—at the time we were good friends and continue to be to this day—who told me when their name was called “a haze of inspiration” came over them and the only thing they could think about was the topic I’d just mentioned. And so it was.&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/haze-of-inspiration.webp&quot; alt=&quot;WEBP&quot; title=&quot;“boat sailing in body of water”, photograph by Joel Bengs (courtesy of &amp;lt;a href=&amp;quot;https://unsplash.com/photos/boat-sailing-in-body-of-water-arYiUpN5tZk&amp;quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;)&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;from-whence-it-came&quot;&gt;From whence it came&lt;/h2&gt;

&lt;p&gt;Just as a haze obscures clear vision, the creative process can blur the lines between our own ideas and those we’ve encountered. We can be inspired by everything around us, in an unconscious way. I’d venture to say that this is the most common form of inspiration, you’re just existing and soaking up as much as you can. In some ways it is automatic and unavoidable. As a result, we share a lot of common experiences and so this sort of natural inspiration can lead to the Zeitgeist, with similar ideas emerging simultaneously in different places. A sort of opposite to this is the concept of incremental innovation, where small changes happen to existing ideas that are occasionally enough for it to feel new. And then there are the types of new ideas that can’t be easily found elsewhere, that are provably innovative and totally new. By definition the haze of inspiration is a very grey space, difficult to navigate, and open to interpretation. There’s no absolute right or wrong, unless you are in a position to flex legal muscles. Below is my own interpretation of how I see things, it’s expected that you won’t agree with all of it.&lt;/p&gt;

&lt;h2 id=&quot;navigating-the-haze&quot;&gt;Navigating the haze&lt;/h2&gt;

&lt;p&gt;I often pause and examine my own creative process. This serves a number of purposes: it helps me notice opportunities for improvement that might otherwise be missed along the way. If my head is down, I might not notice that I can save time by refining my workflow, or gain new understanding by looking at a problem from a different angle or through a different lens.&lt;/p&gt;

&lt;p&gt;Tracking or keeping a record of things you find inspiring is very useful. Some might use Pinterest, bookmark managers, scrap books, print outs, notebooks, folders of saved files. Whatever works for you, really.&lt;/p&gt;

&lt;p&gt;At this point I feel it’s worth mentioning the challenge of distinguishing between inspiration and imitation. We can all be inspired by something, but how we choose to act on the inspiration can be a challenge. How much you take away from the inspiration might mean you cross the line into imitation. The difference between the two is open to interpretation, and legally it’s a very grey area, so it requires understanding of our own moral compass. More on that later. But for me the difference can be summarised by how comfortable I am in the knowledge that I put enough of myself into the idea, then I’m inspired. If I don’t put enough of myself into the idea, then I would call it imitation.&lt;/p&gt;

&lt;h2 id=&quot;original-thinking&quot;&gt;Original thinking&lt;/h2&gt;

&lt;p&gt;Thinking can be difficult, and coming up with original thoughts is even more difficult. One could ask if it’s even possible at all to have an original thought? I think it is, though it’s often said that “&lt;a href=&quot;https://www.youtube.com/watch%3Fv%3DX9RYuvPCQUA&quot;&gt;everything is a remix&lt;/a&gt;”. My personal feeling is that an original thoughts can only come when you take yourself out of the equation. Famous artists used drugs, alcohol, and more. Karl Wallinger of the band World Party (check out the album “Goodbye Jumbo”) famously “&lt;a href=&quot;https://podcasts.apple.com/gb/podcast/darko-audio-podcast/id1368388920?i=1000514052892&quot;&gt;never worked straight&lt;/a&gt;” (~25:30), he would always smoke a joint with the idea being “to get yourself out the fucking way”. John Lennon, too, though perhaps with harder stuff.&lt;/p&gt;

&lt;p&gt;On the other hand Quincy Jones used a technique where he would get himself into what he called “&lt;a href=&quot;https://www.clashmusic.com/features/in-conversation-questlove/&quot;&gt;the alpha state&lt;/a&gt;”, a kind of liminal space between being asleep and awake, where he would routinely have all of his best ideas. Miles Davis used &lt;a href=&quot;https://coppice-gate.com/film/402/miles-davis-the-first-improvised-music-film-soundtrack&quot;&gt;improvisation&lt;/a&gt;. Salvador Dalí used a technique he called “&lt;a href=&quot;https://mma.pages.tufts.edu/fah188/clifford/Subsections/Paranoid%20Critical/paranoidcriticalmethod.html&quot;&gt;paranoiac-critical method&lt;/a&gt;” to access his subconscious through fear. David Lynch uses &lt;a href=&quot;https://www.vice.com/en/article/david-lynch-wants-you-to-meditate-maybe-make-a-lamp-during-self-isolation/&quot;&gt;meditation&lt;/a&gt;. Brian Eno used a deck of cards called “&lt;a href=&quot;https://en.wikipedia.org/wiki/Oblique_Strategies&quot;&gt;Oblique Strategies&lt;/a&gt;” to add constraints to the creative process. David Bowie used a “&lt;a href=&quot;https://en.wikipedia.org/wiki/Cut-up_technique&quot;&gt;cut-up technique&lt;/a&gt;” (also called découpé), adapted from one used by William S. Burroughs but with much older origins, where existing works were cut up and rearranged into new works. My current favourite pop star, Lo Lauren, &lt;a href=&quot;https://www.youtube.com/watch?v=infNIRbESVE&amp;amp;list=PLCu6QvIFB9Chc-pTB0tWnUv67ufwVz8zO&amp;amp;pp=iAQB&quot;&gt;creates songs in 10 minutes&lt;/a&gt; over a found beat using three suggested words to focus the topic of the lyrics. It’s encouraging to realise that there is more than one method and that you can most likely find one that works for you.&lt;/p&gt;

&lt;p&gt;My own method is to recede into the haze itself, into darkness. The simple act of laying down in a quiet room, and closing my eyes for a few minutes is usually enough for me to navigate through the haze and come out with an original idea, solution to a problem, name of a new product, or whatever.&lt;/p&gt;

&lt;h2 id=&quot;research&quot;&gt;Research&lt;/h2&gt;

&lt;p&gt;For me, outside of the actual act of creation, this is most fun part of the creative endeavour. The world is literally your oyster. Given that 99% of stuff happened in the past, I prefer to consult historic sources. Old magazines, books, interviews, and of course the video games and music I grew up with are now old enough to qualify. Ha! The most important thing here is to look outside your field of view. It’s easy to be inspired by the things right in front of you, but more difficult—and more rewarding—to be inspired by things further afield. Reach deeper, go further, get outside your comfort zone, take the road less travelled.&lt;/p&gt;

&lt;p&gt;Once inspired, or locked on to an idea, it’s important to do some due diligence to check that it’s unique, or not protected by any laws or copyright, or even just to check that a name is free enough to be used. Domain names can be taken, hashtags can be already used. My funniest example is when I was originally calling my hit game &lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;YOYOZO&lt;/a&gt; by another name: YOYOZORA, which is a combination of the words YOYO (the toy) and YOZORA (the Japanese word for night sky). I quickly changed plans when a hashtag search revealed somebody posting dick pics.&lt;/p&gt;

&lt;p&gt;It’s worth noting that some creators intentionally avoid external influences during their creative process, believing this leads to more original work. Nick Cave, for instance, &lt;a href=&quot;https://the-talks.com/interview/nick-cave/&quot;&gt;avoids listening to music while writing&lt;/a&gt; to prevent unconscious imitation. Filmmaker Jim Jarmusch tries not to watch other films, or &lt;a href=&quot;https://cinemontage.org/stranger-than-paradox-jim-jarmusch/&quot;&gt;even the scenes he has already shot&lt;/a&gt;, while working on a project to maintain his unique vision. Even in tech, Steve Jobs was known for his “&lt;a href=&quot;http://www.stephengobeli.com/analysis/not-invented-here/&quot;&gt;not invented here&lt;/a&gt;” syndrome, often preferring to develop ideas from scratch. This approach isn’t about ignoring the world entirely, but about creating a space where your own ideas can flourish without immediate external influence. It’s a delicate balance – you want to be informed, but not overly swayed by what’s already out there.&lt;/p&gt;

&lt;h2 id=&quot;attribution&quot;&gt;Attribution&lt;/h2&gt;

&lt;p&gt;By now you’ll know in your heart if what you’re dealing with is imitation or inspiration. What next? Methods of attribution might include: direct credit, “inspired by”, footnotes, a mention, etc. Or if the inspiration is barely visible at all there might be no attribution needed. There’s that moral compass again.&lt;/p&gt;

&lt;p&gt;Attribution can add value to your work, as it shows not only are you compassionate and considerate, but that you value the creativity of other people. In some fields, like music and visual arts it’s perhaps more difficult to do some forms of attribution. You can’t put a list of credits on a painting or in an audio recording, but you can put them in supporting material. There’s always a way.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I won’t spend much time on the topic of “AI”, or to be more precise “generative tools”, but it’s worth mentioning them briefly at this point as they lack any method of attribution which of course is a problem. How big a problem depends on how much originality is in the result and that is a very difficult thing to measure, though I would say not impossible at least regarding the series of prompts that led to the output. Anyway, the results from the use of such tools is also open to interpretation and perhaps this is even more of a grey area than the haze of inspiration itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;communication&quot;&gt;Communication&lt;/h2&gt;

&lt;p&gt;So how do you figure out how best to attribute? By reaching out and having a conversation. But that’s easier said than done, I think.&lt;/p&gt;

&lt;p&gt;I’ve never gone so far as to write a formal letter or anything like that, I’ve been more casual about it. I’ve written to game developers whose games I wanted to port. Having something for them to see is always good—a prototype—and explaining the expectations and reach of the agreement will make everybody feel comfortable. There will surely be some anxiety and reticence for an idea to be taken by somebody else. In my experience I try to frame it in as positive a way as possible. Answer the questions they might have before they even have a chance to think them.&lt;/p&gt;

&lt;p&gt;Timing is always key. I always make such an approach as soon as possible. With my game &lt;a href=&quot;/2023/06/26/ball-und-panzer-golf-making-a-playdate-game-in-a-week/&quot;&gt;Fore! Track&lt;/a&gt;, I contacted the developer whose game I was inspired by as soon as I had a prototype up and running. At that point, if they’d have objected to the idea I wouldn’t have lost much time. If I’d have contacted them with a finished game, it might not have made much difference to their decision making process, but I would have had more invested in it and more to lose. Contacting somebody with only an idea for me feels like the wrong way to go about it, as &lt;a href=&quot;https://sive.rs/multiply&quot;&gt;ideas are relatively easy and execution is everything&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With such discussions it’s important to have open conversations, which will can lead to mutual understanding and respect. Leave your ego at the door and bring truthfulness, compassion, open-mindedness and you will be rewarded. Misunderstandings will inevitably happen through the course of the conversation, but by being open both parties can rest assured that there will only be movement towards resolution rather than escalation.&lt;/p&gt;

&lt;p&gt;By far the best outcome of such open discussions is the fact that two heads are better than one so the solution is almost always better than the original idea or approach. This means it’s in your own interests to have these sorts of conversations as often as possible. They might lead to better ideas, collaboration, partnerships, refinement, suggestions you would never have thought of, about turns, and of course rejection.&lt;/p&gt;

&lt;p&gt;Rejections are always hard to take, but I’m a firm believer that with time, and enough water under the bridge, that newer and stronger ideas will emerge. For me that can take weeks, months, even years. But every time it happens I think “the Universe provides” and smile.&lt;/p&gt;

&lt;h2 id=&quot;ethical-considerations&quot;&gt;Ethical considerations&lt;/h2&gt;

&lt;p&gt;So, about that moral compass I keep mentioning. Ideas are more than a feeling, especially when executed and turned into something that can be seen, heard, played. They turn from intangible to tangible, and this process is guided by your moral compass.&lt;/p&gt;

&lt;p&gt;When it comes to that “haze of inspiration”, intellectual integrity is the needle on your moral compass. It helps you navigate through the fog without stepping on anyone’s toes. It’s about asking yourself not whether you could use an idea, but whether you should. It’s about being upfront about what inspired you, maybe asking for a thumbs-up if you’re borrowing heavily from someone else’s work, and generally just being a decent human in the creative playground. By sticking to these principles, you’re not just keeping your own nose clean. You’re helping to create an environment where ideas can bounce around freely, where people aren’t afraid to share their cool thoughts, and where everyone gets their due credit. It’s like keeping the idea ecosystem healthy, you know?&lt;/p&gt;

&lt;p&gt;Intellectual integrity is really just about not being a jerk with other people’s ideas. It’s about giving credit where it’s due, trying to get your facts straight, and not twisting things to fit your narrative. It’s also about owning your work, warts and all. If you messed up or your info isn’t 100% solid, just say so. Nobody’s perfect, right? And here’s an important addition: be open to other viewpoints. Just because someone disagrees with you doesn’t mean they’re wrong (even if you really, really think they are).&lt;/p&gt;

&lt;p&gt;All ideas build on previous work. I subscribe to the idea that everything is a remix, and I also know that being cool, kind, and considerate costs nothing. Being uncool, unkind, or inconsiderate can cost you more energy in the long run. Time teaches that particular lesson, so consider it being mentioned here a free power-up. We are lucky enough to be able to stand on the shoulders of giants, so it’s important to not make a mess whilst we’re up there.&lt;/p&gt;

&lt;h2 id=&quot;personal-growth&quot;&gt;Personal growth&lt;/h2&gt;

&lt;p&gt;Growing is hard and with it come a lot of pains. Road blocks, wrong turns, bad luck, but hopefully it will trend towards progress.&lt;/p&gt;

&lt;p&gt;One key aspect of growth is synthesis. Plants turn light energy into chemical energy through photosynthesis. The output is vastly different to the input. I think this is a useful lens through which to look at techniques for turning influences into something truly novel. You can look at the &lt;a href=&quot;/2023/04/10/where-can-i-see-hokusai-great-wave-today/&quot;&gt;The Great Wave&lt;/a&gt; and print your own version of it if the goal is to imitate it as closely as possible (as &lt;a href=&quot;https://www.youtube.com/playlist?list=PLK-Wicsj5rAasS2g7e-Z9eFUdG6I7ZqED&quot;&gt;David Bull&lt;/a&gt; has done), or you might choose to draw it instead and replace the crests of the wave with bunny rabbits (as &lt;a href=&quot;https://shop.kozyndan.com/products/uprisings-poster&quot;&gt;kozyndan&lt;/a&gt; did), or you might sculpt it from digital clay and add some mahjong tiles to it (like &lt;a href=&quot;/2023/04/13/sparrow-solitaire-for-playdate/&quot;&gt;vxcl did for my game Sparrow Solitaire&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I like to think of this synthesis as a road—you can turn off at any point but the further you go the more wonderful the sights will be. I’d like to make a short detour to mention constraints and how they can foster creativity. Whilst the road might be long, it’s not sprawling but a single direction. Navigating a complex road system would be much more difficult, so I encourage you to impose additional constraints if there don’t seem to be enough, or if the way forward isn’t clear.&lt;/p&gt;

&lt;p&gt;Any period of not creating that you end up in is just as important as a period of intense creativity that you might rather be in. My favourite quote about this (by BT, the musician; I’m paraphrasing) is that creativity comes in waves, sometimes you’re in the doldrums and not much is happening but there is no doubt that the next wave will arrive at some point, so you just have to be ready to jump on and ride it when it does!&lt;/p&gt;

&lt;p&gt;It’s a fine balance between being influenced and being derivative. Sadly your moral compass is not fitted with a warning alarm of any sort, so you’ll have to rely on your heart, head, and those of others to give you guidance here. Having respect for others’ ideas can be considered a constraint in and of itself and can actually push you to be more innovative. Being derivative is a trap to be avoided, not only would you not gain as much personally from the endeavour but you might also inadvertently dilute the idea, brand, vibe of the originator. You should instead consider the person who has inspired you as a mentor, supporter, team mate, power-up, voice of reason, or even shoulder to cry on. The important take-away is that they are there to help because, hey, they were there first.&lt;/p&gt;

&lt;h2 id=&quot;embracing-the-future&quot;&gt;Embracing the future&lt;/h2&gt;

&lt;p&gt;With enough searching, or metaphorical travelling, you’ll be able to find your own voice, style, brand, or “vibe” as I prefer to call it. I’m a bit of a hippie at heart. The ultimate goal is to find that rug that really ties the room together. Then cherish it, feed it, sculpt it, even defend it if the need arises. You’ll have the ideas, make a &lt;a href=&quot;https://allaboutstevejobs.com/verbatim/interviews/playboy_1985&quot;&gt;dent in the universe&lt;/a&gt;, and develop a vibe that other people will be influenced by, and hopefully they will take the right path and be inspired by it rather than choose to imitate it. Perhaps that’s the ultimate goal? It’s your turn to set a good example for those that come after you.&lt;/p&gt;

&lt;hr /&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 16/9;&quot; videoid=&quot;j29Vjxi_oAg&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;center&gt;Excerpt from &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Big_Lebowski&quot;&gt;The Big Lebowski&lt;/a&gt; (Ethan &amp;amp; Joel Coen, 1998)&lt;/center&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/60965426-the-creative-act&quot;&gt;The Creative Act: A Way of Being&lt;/a&gt; (Rick Rubin, 2023)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/13099738-steal-like-an-artist&quot;&gt;Steal Like An Artist&lt;/a&gt; (Austin Kleon, 2021)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/10770576-the-ecstasy-of-influence&quot;&gt;The Ecstasy of Influence&lt;/a&gt; (Jonathan Lethem, 2011)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sive.rs/multiply&quot;&gt;Ideas Are Just a Multiplier of Execution&lt;/a&gt; (Derek Sivers, 2005)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/8083765-think-like-da-vinci&quot;&gt;Think Like Da Vinci&lt;/a&gt; (Michael J. Gelb, 1998)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/18144590-the-alchemist&quot;&gt;The Alchemist&lt;/a&gt; (Paulo Coelho, 1988)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/20425787-oblique-strategies&quot;&gt;Oblique Strategies&lt;/a&gt; (Brian Eno &amp;amp; Peter Schmidt, 1975–2001)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/780304.Design_Methods&quot;&gt;Design Methods&lt;/a&gt; (John Chris Jones, 1970–1992)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hermanmiller.com/stories/why-magazine/design-q-and-a-charles-and-ray-eames/&quot;&gt;Design Q &amp;amp; A&lt;/a&gt; (Charles &amp;amp; Ray Eames, 1969–1972)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/157993.The_Little_Prince&quot;&gt;The Little Prince&lt;/a&gt; (Antoine de Saint-Exupéry, 1943)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;Thanks to vivarado for feedback on this piece ahead of publication.&lt;br /&gt;
And to Nick, Charlie, Jan and Neil for feedback that led to later revisions.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 28 Sep 2024 17:46:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/09/28/a-haze-of-inspiration/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/09/28/a-haze-of-inspiration/</guid>
        </item>
      
    
      
        <item>
          <title>Search Moby Games using Alfred app</title>
          <description>&lt;p&gt;Earlier this year I made a workflow for &lt;a href=&quot;https://alfred.app&quot;&gt;Alfred app&lt;/a&gt; to allow easy searching of &lt;a href=&quot;https://www.mobygames.com&quot;&gt;Moby Games&lt;/a&gt;. Earlier this week it was released on &lt;a href=&quot;https://alfred.app/workflows/gingerbeardman/moby-games/&quot;&gt;Alfred Gallery&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/alfred-moby-games.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://alfred.app/workflows/gingerbeardman/moby-games/&quot;&gt;alfred.app/workflows/gingerbeardman/moby-games/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow &lt;a href=&quot;https://www.mobygames.com/info/api/&quot;&gt;requires an API key that you can get instantly&lt;/a&gt; via your Moby Games profile page.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’d like access to the free API (not-for-profit use), create a MobyGames account and then visit your profile page. Click the ‘API’ link under your user name to sign up for an API key. If you wish to use the API key with an existing game launcher or plugin please specify which launcher and plugin you will be using.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enter “Alfred app Moby Games workflow”. Then paste your API into the Workflow configuration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;open-source&quot;&gt;Open Source&lt;/h2&gt;
&lt;p&gt;Suggestions and PRs are welcome at: &lt;a href=&quot;https://github.com/gingerbeardman/alfred-moby-games/&quot;&gt;github.com/gingerbeardman/alfred-moby-games/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 04 Aug 2024 12:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/08/04/search-moby-games-using-alfred-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/08/04/search-moby-games-using-alfred-app/</guid>
        </item>
      
    
      
        <item>
          <title>Taking command of the Context Menu in macOS</title>
          <description>&lt;p&gt;Yesterday on Twitter the inimitable &lt;a href=&quot;https://twitter.com/mortenjust/status/1817991110544744764&quot;&gt;Morten Just posted a preview of a tool he’s created&lt;/a&gt; that wrap ffmpeg to allow movies, such screen recordings but pretty much anything, to be re-encoded to a smaller filesize.&lt;/p&gt;

&lt;p&gt;I responded with a trick I use to do the same on “right-click” context menu using a macOS app called ContextMenu, and others said it was possible to do it using Automator (with some caveats). In this blog post I’ll compare the two.&lt;/p&gt;

&lt;p&gt;But first… let’s talk about how we will make this work.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;processing-files-through-shell-scripts&quot;&gt;Processing files through shell scripts&lt;/h1&gt;

&lt;p&gt;We’ll be making use of command line tools to do the heavy lifting, but don’t worry I’ll show that this can be as simple as a single-line command to process a single file, or a shell scripts of a few lines to process multiple files.&lt;/p&gt;

&lt;h3 id=&quot;single-files&quot;&gt;Single files&lt;/h3&gt;

&lt;p&gt;The one-line command to convert a video or audio file can be as simple as the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are some assumptions here:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; has been installed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;my Mac is Apple silicon, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; installs commands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/homebrew/bin/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;(older Intel Macs will have them installed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;the script is called with parameters passed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;argv&lt;/code&gt; so the file is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$@&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;the new file will have the same name as the original, but with a new .mp4 file extension&lt;/li&gt;
  &lt;li&gt;it only works on single files (read on)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; add an extra parameter to the command line to use hardware encoding (hevc_videotoolbox) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c:v hevc_videotoolbox&lt;/code&gt;, which would be something like twice as fast, but this will not reduce the file size. So we stick with ffmpeg default settings.&lt;/p&gt;

&lt;p&gt;You can take the exact same approach with any destination format as it is decided by the file extension. Cool. You could convert any of ffmpeg’s supported file types to any other, such as WAV, OGG, MKV, etc.&lt;/p&gt;

&lt;p&gt;And as we’ll see later we can take this simple command and change it to use other command line tools to compress GIFs, with or without upscaling, and many other timesaving tasks. Powerful stuff!&lt;/p&gt;

&lt;h3 id=&quot;multiple-files&quot;&gt;Multiple files&lt;/h3&gt;

&lt;p&gt;To extend this approach for multiple files you can use the following for loop:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    /opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;multiple-files-in-parallel&quot;&gt;Multiple files in parallel&lt;/h3&gt;

&lt;p&gt;And to process those multiple files in parallel (be careful not to overwhelm your computer!) we can background each execution using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; symbol.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    /opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt; &amp;amp;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;automator&quot;&gt;Automator&lt;/h2&gt;

&lt;p&gt;This has been a built-in macOS app since 2005. Setup is relatively straightforward:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a new &lt;em&gt;Quick Action&lt;/em&gt; workflow&lt;/li&gt;
  &lt;li&gt;Workflow receives current: &lt;em&gt;movie files&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;in: &lt;em&gt;Finder&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Add a new action of type action &lt;em&gt;Run Shell Script&lt;/em&gt; (search for it in the sidebar)&lt;/li&gt;
  &lt;li&gt;Pass input: &lt;em&gt;as arguments&lt;/em&gt; (this will give us a template command)&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Replace the echo line with your command&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Save&lt;/em&gt;, name it “Duplicate as MP4”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-automator-setup.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The menu item is now available on the context menu, inside the &lt;em&gt;Quick Actions&lt;/em&gt; submenu. After repeated use I find this submenu too annoying, but you may fare better.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-automator-finder.png&quot; alt=&quot;PNG&quot; title=&quot;Automator Quick Action in Finder Context Menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, pretty easy! But, there are some caveats or limitations that may, or may not, annoy you. Perhaps we don’t want the menu item to appear for such a broad range of files (all “movie files”), or maybe we want it to appear for multiple types of files (both “audio files” and “movie files”). Sadly this is not so easy with Automator.&lt;/p&gt;

&lt;p&gt;Automator stores these files at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Services/&lt;/code&gt; and they will migrate to a new Mac.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;contextmenu&quot;&gt;ContextMenu&lt;/h2&gt;

&lt;p&gt;Many years ago I found &lt;a href=&quot;https://apps.apple.com/us/app/context-menu/id1236813619?mt=12&quot;&gt;ContextMenu&lt;/a&gt; ($4.99) which solves all of the issues I have with the Automator approach. There’s also a free version, &lt;a href=&quot;https://apps.apple.com/gb/app/context-menu-lite/id1261373706?mt=12&quot;&gt;ContextMenu Lite&lt;/a&gt;, that supports up to 3 actions to give you a taste of the good stuff.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Submenu is optional&lt;/li&gt;
  &lt;li&gt;Apply to multiple types (files or directories; can be as granular as file extension)&lt;/li&gt;
  &lt;li&gt;Show output (sometimes you want to see the results of the command)&lt;/li&gt;
  &lt;li&gt;Confirmation before running (if it’s a potentially dangerous operation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-context-menu-setup.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The menu item displays in Finder as follows:&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-context-menu-finder.png&quot; alt=&quot;PNG&quot; title=&quot;ContextMenu Action in Finder Context Menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ContextMenu stores these files in its own group folder, right click an action and choose &lt;em&gt;Show in Finder&lt;/em&gt;, but you can specify your own folder in a location of your choice. Both will migrate to a new Mac, but the later might give you more control.&lt;/p&gt;

&lt;p&gt;Edit: we can limit actions to running on files with specific extensions or, since update 1.4.4 &lt;a href=&quot;https://gist.github.com/RhetTbull/7221ef3cfd9d746f34b2550d4419a8c2&quot;&gt;specific UTI types&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;compressing-a-gif&quot;&gt;Compressing a GIF&lt;/h2&gt;

&lt;p&gt;We can use the &lt;a href=&quot;https://www.lcdf.org/gifsicle/&quot;&gt;gifsicle&lt;/a&gt; command line tool to optimise (compress) an Animated GIF. The a parameters I use here are explained in the &lt;a href=&quot;https://www.lcdf.org/gifsicle/man.html&quot;&gt;gifsicle man page&lt;/a&gt;, and I arrived at them with &lt;a href=&quot;/2016/06/16/post-processing-animated-gifs/&quot;&gt;years of experimentation&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/gifsicle &lt;span class=&quot;nt&quot;&gt;-O1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Okeep-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--careful&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.gif&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.o.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can also do the same thing whilst scaling it up by a factor of 2:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/gifsicle &lt;span class=&quot;nt&quot;&gt;-O1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Okeep-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--careful&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scale&lt;/span&gt; 2 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.gif&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.o.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;repository-of-contextmenu-actions&quot;&gt;Repository of ContextMenu Actions&lt;/h2&gt;

&lt;p&gt;Here’s my repo of shared actions. Inside each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cmaction&lt;/code&gt; file is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.sh&lt;/code&gt; file which contains the command that you can use in Automator if you’d like.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gingerbeardman/contextmenu-actions&quot;&gt;github.com/gingerbeardman/contextmenu-actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These include many useful commands such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Convert BIN+CUE to ISO&lt;/li&gt;
  &lt;li&gt;Change sample rate of audio&lt;/li&gt;
  &lt;li&gt;Convert image to 1-bit&lt;/li&gt;
  &lt;li&gt;Convert image format&lt;/li&gt;
  &lt;li&gt;Create Clean ZIP&lt;/li&gt;
  &lt;li&gt;Duplicate audio as alternative format&lt;/li&gt;
  &lt;li&gt;Duplicate image as alternative format&lt;/li&gt;
  &lt;li&gt;Duplicate movie as alternative format&lt;/li&gt;
  &lt;li&gt;File information&lt;/li&gt;
  &lt;li&gt;Generate images for web&lt;/li&gt;
  &lt;li&gt;Make script executable&lt;/li&gt;
  &lt;li&gt;Move file (includes helper app)&lt;/li&gt;
  &lt;li&gt;New file with clipboard&lt;/li&gt;
  &lt;li&gt;Open file with specific apps&lt;/li&gt;
  &lt;li&gt;Optimize GIF&lt;/li&gt;
  &lt;li&gt;Set GIF to loop infinitely&lt;/li&gt;
  &lt;li&gt;Show GIF info&lt;/li&gt;
  &lt;li&gt;Playdate Colorize IMG&lt;/li&gt;
  &lt;li&gt;Replace Existing App (warning)&lt;/li&gt;
  &lt;li&gt;Scale image to certain percentage&lt;/li&gt;
  &lt;li&gt;Set as MacJapanese encoding&lt;/li&gt;
  &lt;li&gt;Show hashes (includes helper app)&lt;/li&gt;
  &lt;li&gt;Stub out file (warning)&lt;/li&gt;
  &lt;li&gt;Touch file&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 30 Jul 2024 16:14:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/07/30/taking-command-of-the-context-menu-in-macos/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/07/30/taking-command-of-the-context-menu-in-macos/</guid>
        </item>
      
    
      
        <item>
          <title>FRP bypass and unlock on a Samsung Galaxy Tab S2</title>
          <description>&lt;p&gt;I use an Android tablet for reading ebooks. My favourite ebook reader is the &lt;a href=&quot;https://www.the-ebook-reader.com/sony-prs-650.html&quot;&gt;Sony PRS-650&lt;/a&gt; which was ahead of its time in a lot of ways. But these days I like to have internet connectivity whilst reading so I can do both dictionary lookups, Wikipedia lookups, and quick Google searches. But at the same time I don’t really want the total distraction of an iPad, or Android tablet, so the ideal solution for me was to use KOReader - an ebook reading app heavily inspired by the Sony Reader interface - and which has the ability to do those dictionary and Wikipedia lookups and keep me mostly self-contained.&lt;/p&gt;

&lt;p&gt;So I bought a Google Asus Nexus 7 which is really nice, but for my use case running any modern version of Android on it shows how much it is lacking in both CPU and RAM. It was OK for a while, I read a good handful of books on it, but I eventually wanted something more capable. I’d heard about the Samsung Galaxy Tab S2 8.0 which is slightly wider and perhaps slightly lighter and above all more powerful. Both devices have very high resolution displays, which is a must for font rendering, and seems to bea thing if the past for Android tablets.&lt;/p&gt;

&lt;p&gt;I managed to snag a “faulty” Tab S2 8.0 on eBay that was listed as powering on and off, but was locked to a Google Account. This is something I knew could be worked around, and this blog post gives a brief outline of how I did it.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;frp-bypass&quot;&gt;FRP bypass&lt;/h2&gt;

&lt;p&gt;FRP is basically an account lock. The device will require the owners account to be logged in even after a facory reset. A bit like iCloud lock on Apple devices, but this is much less well implemented. A comprehensive &lt;a href=&quot;https://www.youtube.com/watch?v=R4g5KZFRM1Y&quot;&gt;YouTube video by Muhammad Mubeen&lt;/a&gt; was of great help to do the bypass.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;SamFW FRP TOOL &amp;gt; MRP &amp;gt; Open YouTube &amp;gt; View&lt;/li&gt;
  &lt;li&gt;Choose browser &amp;gt; Open Samsung Browser&lt;/li&gt;
  &lt;li&gt;Download and install &lt;a href=&quot;https://androidfilehost.com/?fid=5862345805528059176&quot;&gt;technocare.apk from androidfilehost.com&lt;/a&gt; (this adds options we’ll use)&lt;/li&gt;
  &lt;li&gt;Open a new tab with &lt;a href=&quot;https://frpbypass.io&quot;&gt;frpbypass.io&lt;/a&gt; for the FRP bypass links&lt;/li&gt;
  &lt;li&gt;Choose link from frpbypass tab: Settings&lt;/li&gt;
  &lt;li&gt;Settings &amp;gt; Lock Screen &amp;amp; Security &amp;gt; Other Security Settings &amp;gt; Device Administrators &amp;gt; Find My Device (uncheck)&lt;/li&gt;
  &lt;li&gt;Settings &amp;gt; Apps &amp;gt; Google Play Store &amp;gt; Disable&lt;/li&gt;
  &lt;li&gt;Cloud &amp;amp; Accounts &amp;gt; Add Account &amp;gt; Google&lt;/li&gt;
  &lt;li&gt;(login and verify)&lt;/li&gt;
  &lt;li&gt;Settings &amp;gt; Apps &amp;gt; Google Play Store &amp;gt; Enable&lt;/li&gt;
  &lt;li&gt;Restart&lt;/li&gt;
  &lt;li&gt;Settings &amp;gt; Apps &amp;gt; TecnocareTricks &amp;gt; Uninstall&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;frp-unlock&quot;&gt;FRP unlock&lt;/h2&gt;

&lt;p&gt;By enabling OEM Unlock the FRP lock will also be switched off, which is very handy. By factory resetting the device whilst in that state, FRP will be permanently disabled. Result!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Settings &amp;gt; System &amp;gt; About &amp;gt; Build number (tap 7 times)&lt;/li&gt;
  &lt;li&gt;Settings &amp;gt; System &amp;gt; Developer Options &amp;gt; OEM Unlock&lt;/li&gt;
  &lt;li&gt;Reboot to recovery mode (Power, Volume Up, Home)&lt;/li&gt;
  &lt;li&gt;Factory reset&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;android-update&quot;&gt;Android update&lt;/h2&gt;

&lt;p&gt;From here I installed &lt;a href=&quot;https://xdaforums.com/t/unofficial-rom-alpha-lineageos-20-for-sm-t713-may-16-2024.4667460/&quot;&gt;Lineage OS 20&lt;/a&gt; (Android 13) on the device, which would otherwise be limited to very old Android 6 or 7. Other options are &lt;a href=&quot;https://xdaforums.com/t/rom-unofficial-10-lineageos-17-1-t713-t719-t813-t819.4070161/&quot;&gt;Lineage OS 17.1&lt;/a&gt; (Android 10) &lt;a href=&quot;https://xdaforums.com/t/rom-7-0-nougat-abandoned-stock-lite-v8-t713-t813.3746583/&quot;&gt;Stock Android 7 Lite&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;for-mac-users&quot;&gt;For Mac Users&lt;/h2&gt;

&lt;p&gt;on macOS you can use Odin or SamFw Tool in a virtual machine, using the latest official Samsung Android USB drivers. I did it in VMware Fusion 13 running Windows 11 ARM on an M1 MacBook Pro running macOS Sonoma 14.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 30 May 2024 18:24:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/05/30/frp-bypass-and-unlock-on-a-samsung-galaxy-tab-s2/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/05/30/frp-bypass-and-unlock-on-a-samsung-galaxy-tab-s2/</guid>
        </item>
      
    
      
        <item>
          <title>Mouse Support for Playdate</title>
          <description>&lt;p&gt;Since some of my first prototypes with Playdate I’ve wanted there to be a way to interact with the device using a mouse. Well, today is that day!&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 3/2;&quot; videoid=&quot;PF4emlHhYCM&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;p&gt;No doubt you have some questions? How are you doing this? What’s the weather like? etc.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;how&quot;&gt;How?&lt;/h2&gt;

&lt;p&gt;A custom &lt;a href=&quot;https://www.hammerspoon.org&quot;&gt;Hammerspoon&lt;/a&gt; script monitors mouse coordinates and sends them to the Playdate over serial connection. The game receives those messages through the recently added API &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;playdate.serialMessageReceived(message)&lt;/code&gt;, parses them, and acts accordingly.&lt;/p&gt;

&lt;p&gt;Currently the script sends messages such as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;msg mouse on
msg 0,0
msg -19,84
msg mouse off
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I use those to trigger on screen notifications, reset the game state, and move the cursor.&lt;/p&gt;

&lt;p&gt;Note: use of serial means you can’t have the Playdate Simulator or Mirror open when trying this. If you invoke the script and it can’t open the serial connection for any reason it will sound an error beep.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;Originally, back in 2020, I wanted to be able to position items in the in-game level editor of &lt;a href=&quot;/tag/dailydriver/&quot;&gt;my game Daily Driver&lt;/a&gt; more directly, using a mouse rather than typing co-ordinates into a file. Then I forgot about it for a while. Then in &lt;a href=&quot;https://sdk.play.date/changelog/#_2_4_0&quot;&gt;&lt;em&gt;Playdate SDK 2.4.0&lt;/em&gt;&lt;/a&gt; (March 1, 2024) a new API was added that made me wonder if it was now possible.&lt;/p&gt;

&lt;p&gt;I knew the &lt;a href=&quot;https://itch.io/jam/uncrankdjam&quot;&gt;Uncrank’d Game Jam&lt;/a&gt; was in progress and was thinking about what to do, and then the mouse control idea came back to me. I first put together the bare bones of an app that drew a dot to the screen, mirrored both horizontally and vertically. Then I set about proving I could forward mouse events, which resulted in a &lt;a href=&quot;https://github.com/gingerbeardman/playdate-mouse-support/blob/main/mouse/mouse_to_playdate.lua&quot;&gt;small Hammerspoon script&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After that, I built out the drawing app and that’s how &lt;a href=&quot;https://gingerbeardman.itch.io/rorschach&quot;&gt;Rorschach&lt;/a&gt; came to be. It was originally built to showcase the mouse control, but has since evolved into a lovely little toy of its own. I thought a drawing app would be an easy-to-understand visual showcase for this innovation. But, really, the possibilities for its use are endless.&lt;/p&gt;

&lt;p&gt;Developers can use it to speed up and simplify development, add finer control to their in-game level editors, and generally make their lives easier. There’s nothing stopping you giving users access to it in a game.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Make sure you’re running &lt;a href=&quot;https://gingerbeardman.itch.io/rorschach&quot;&gt;Rorschach&lt;/a&gt; or &lt;a href=&quot;https://github.com/gingerbeardman/playdate-mouse-support/releases/tag/240511&quot;&gt;Mouse Demo&lt;/a&gt; on your device&lt;/li&gt;
  &lt;li&gt;Attach your Playdate over USB and unlock the device&lt;/li&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://www.hammerspoon.org/&quot;&gt;Hammerspoon&lt;/a&gt; automation tool for macOS&lt;/li&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://github.com/gingerbeardman/playdate-mouse-support/blob/main/mouse/mouse_to_playdate.lua&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mouse_to_playdate.lua&lt;/code&gt;&lt;/a&gt; (instructions in the file)&lt;/li&gt;
  &lt;li&gt;Press the hotkey (defaults to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F13&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;The guide window (virtual trackpad?) appears so you can gauge Playdate screen size&lt;/li&gt;
  &lt;li&gt;Move your mouse pointer and watch the drawing appear!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve tested this on a MBP running Sonoma with a mouse and a trackpad. There’s very little lag, it’s really quite usable. The video has substantial lag as I was capturing through QuickTime and my phone camera. Anyway, it’s super cool!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;demos&quot;&gt;Demos&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Rorschach drawing app is at: &lt;a href=&quot;https://gingerbeardman.itch.io/rorschach&quot;&gt;gingerbeardman.itch.io/rorschach&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;An example project with source code is at: &lt;a href=&quot;https://github.com/gingerbeardman/playdate-mouse-support&quot;&gt;github.com/gingerbeardman/playdate-mouse-support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;This is a technical proof of concept, so it can be improved&lt;/li&gt;
  &lt;li&gt;It was created to show that this can be done. It is not a final version or the most robust way of doing this. And, yes, sorry, it’s only available for macOS&lt;/li&gt;
  &lt;li&gt;My dream scenario would be for Playdate Simulator and Mirror to have this feature built-in, so you can enable the feature and then interact with the virtual screen to forward mouse events to Playdate&lt;/li&gt;
  &lt;li&gt;If that can’t happen, then maybe a more native mouse forwarding client can be created for each of macOS, Windows, and Linux&lt;/li&gt;
  &lt;li&gt;On macOS it would be cool if it supported Apple Pencil via an iPad/Sidecar&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 11 May 2024 10:52:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/05/11/mouse-support-for-playdate/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/05/11/mouse-support-for-playdate/</guid>
        </item>
      
    
      
        <item>
          <title>Adding your own multi-channel audio to Music app</title>
          <description>&lt;p&gt;I’m really enjoying Dolby Atmos, “spatial audio”, or “surround sound” as we used to call it. There is so much music in this format on streaming platforms, both new albums and classic albums. I wanted to listen to Björk’s first four albums in “spatial audio”, but they don’t currently exist in that format on streaming platforms. But I was reminded about DVD-Audio versions of these from back in the day, and I could find most of mine, so I figured I’d have a go of converting them myself whilst I’m waiting for Dolby Atmos versions to hit streaming.&lt;/p&gt;

&lt;p&gt;This blog post lists the three main steps needed to add your own multi-channel audio to Music.app (you might know it by its old name: iTunes) and have them be accessible across all of your devices thanks to Apple Music’s Cloud Library (you might know it by its old name: iCloud Music Library).&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;dvd-audiovideo&quot;&gt;DVD Audio/Video&lt;/h2&gt;

&lt;p&gt;Three albums I have were released on DVD and contain multi-channel audio in both DTS and Dolby Digital as DVD-Video, these are Debut, Post, and Homogenic. Vespertine is better mastered and was released as a disc that included DVD-Audio. Regardless, we can take the same approach for these two disc types: use &lt;a href=&quot;https://www.dvdae.com&quot;&gt;DVD Audio Extractor&lt;/a&gt; (the 30-day trial is OK) to extract the tracks to FLAC files:&lt;/p&gt;

&lt;p&gt;Settings:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sample rate: Same as input&lt;/li&gt;
  &lt;li&gt;Channels: All 6 Channels&lt;/li&gt;
  &lt;li&gt;Bits per sample: 24-bit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’ll take a minute or two for the files to be generated.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: many multi-channel FLAC files&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;aside-cloud-library&quot;&gt;Aside: Cloud Library&lt;/h2&gt;

&lt;p&gt;Music.app (formerly iTunes) doesn’t really go out of its way to support multi-channel audio users might add themselves. We won’t be creating Dolby Atmos tracks, but rather we’ll take advantage of a loop hole in Apple Music’s Cloud Library feature.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://www.quadraphonicquad.com/forums/threads/ripping-in-surround-for-apple-tv.33931/post-699285&quot;&gt;a post on the QuadrophonicQuad forum&lt;/a&gt; we know that we need to limit the bitrate to 256kb/s for it to be uploaded unmodified to our Cloud Library and sync to all our devices. Well, it turns out that trick also works with 768kb/s bitrate files, which is the same bitrate as Dolby Atmos releases on Apple Music. That’s good enough for me.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conversionencoding&quot;&gt;Conversion/Encoding&lt;/h2&gt;

&lt;p&gt;There are many GUI tools that you might use to do this but I’ve chose to use ffmpeg on the command line. Feel free to adapt these settings to your tool of choice. The most important property is the 768kb/s bitrate, as mentioned earlier.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for n in *.flac; do ffmpeg -i &quot;$n&quot; -vn -c:a aac_at -b:a 768k -sample_fmt s16 -ar 48000 &quot;$n.m4a&quot;; done&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Breaking down the command above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for n in *.flac; do&lt;/code&gt; = loop through all *.flac files&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i &quot;$n&quot;&lt;/code&gt; = input, using filename from for loop&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-vn&lt;/code&gt; = remove the video track&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c:a aac_at&lt;/code&gt; = use Apple’s AAC encoder&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-b:a 768k&lt;/code&gt; = target 768kb/s bitrate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-sample_fmt s16&lt;/code&gt; = 16-bit sample format&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ar 48000&lt;/code&gt; = 48KHz sample rate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;$n.m4a&quot;&lt;/code&gt; = output, using filename from for loop, adding .m4a&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;; done&lt;/code&gt; = end for loop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully that makes sense.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: many multi-channel M4A files&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;chapters&quot;&gt;Chapters&lt;/h2&gt;

&lt;p&gt;If the M4A audio file contains chapters they will need to be removed or the file will not be playable on iPhone. We can load it into &lt;a href=&quot;https://subler.org&quot;&gt;Subler&lt;/a&gt;, then select and delete the “Text Track”.&lt;/p&gt;

&lt;p&gt;The process above should not introduce them, but I’m mentioning it as I was repeatedly caught out by this on my journey to this solution. If you fail to remove it the track will either either upload but not be playable, error and not upload, or be matched rather than uploaded.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: multi-channel M4A file (modified)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;final-steps&quot;&gt;Final steps&lt;/h2&gt;

&lt;p&gt;Add the final files to Music.app on your desktop. At this point you can add artwork and other meta data, such as tagging them as “multi-channel” or similar.&lt;/p&gt;

&lt;p&gt;When you’re done make sure to choose &lt;em&gt;File &amp;gt; Library &amp;gt; Update Cloud Library&lt;/em&gt;. After they finish uploading, open the Music app on your iPhone and you’ll see the multi-channel files ready to play.&lt;/p&gt;

&lt;p&gt;These multi-channel files work wonderfully with AirPods Pro spatial audio head tracking!&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/music-multi-channel.heic&quot; alt=&quot;WEBP&quot; title=&quot;My own multi-channel audio in Music app on my iPhone&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 09 May 2024 20:19:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/05/09/adding-your-own-multi-channel-audio-to-music-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/05/09/adding-your-own-multi-channel-audio-to-music-app/</guid>
        </item>
      
    
      
        <item>
          <title>Per-game skins in the Delta classic video game emulator for iOS</title>
          <description>&lt;p&gt;Apple recently changed the App Store rules to allow emulators, which means we’re now seeing emulators for classic video game consoles available for download! This is great news for a retro gamer like myself. &lt;a href=&quot;https://apps.apple.com/gb/app/delta-game-emulator/id1048524688&quot;&gt;Delta&lt;/a&gt; is one such emulator that currently focuses on Nintendo platforms: NES, Game Boy, Game Boy Color, SNES, N64 and DS.&lt;/p&gt;

&lt;p&gt;I thought it would be fun to play my favourite Nintendo DS game: &lt;a href=&quot;/2013/06/29/maboshi/&quot;&gt;MaBoShi&lt;/a&gt;. This is an odd choice of game for a few reasons, but it really tests what Delta and the melonDS emulation core can do.&lt;/p&gt;

&lt;p&gt;Immediately I noticed that the Nintendo DS emulation quality is high: MaBoShi’s mosaic transitions are shown, whereas in other emulators such as DraStic or DeSmuME they are not. That’s a good start!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/delta-maboshi-default.png&quot; alt=&quot;PNG&quot; title=&quot;Delta&apos;s default Nintendo DS skin—pretty nice! BUT&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;when-good-isnt-good-enough&quot;&gt;When good isn’t good enough&lt;/h2&gt;

&lt;p&gt;But, we can see some odd things about MaBoShi:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;it is played with the DS rotated on its side&lt;/li&gt;
  &lt;li&gt;the second screen goes mostly unused&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there are some other things we can’t see:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;it doesn’t use the touch screen&lt;/li&gt;
  &lt;li&gt;only the D-pad is used during play (and Start button to pause)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usually, when I play this game in an emulator I activate single screen mode and forgo seeing the high score, which is no big deal as the game ends at 1 million and that’s always my goal.&lt;/p&gt;

&lt;p&gt;So I looked into the &lt;a href=&quot;https://faq.deltaemulator.com/using-delta/controller-skins&quot;&gt;Delta docs&lt;/a&gt; and &lt;a href=&quot;https://noah978.gitbook.io/delta-docs/skins&quot;&gt;skin docs&lt;/a&gt; and find that the options to show a single screen, or rotate it, are controlled by the skin (visual theme) you are using.&lt;/p&gt;

&lt;p&gt;Reading further into skins the capabilities seemed quite comprehensive, so I began to hatch a plan… &lt;em&gt;maybe I can create a custom skin just for MaBoShi&lt;/em&gt;?&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;i-love-it-when-a-plan-comes-together&quot;&gt;I love it when a plan comes together&lt;/h2&gt;

&lt;p&gt;Here’s the feature list I came up with:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;show the main game screen&lt;/li&gt;
  &lt;li&gt;show only the score from the secondary screen&lt;/li&gt;
  &lt;li&gt;remove all controls other than the d-pad, start and delta button&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I achieved this goal as follows:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;define two seperate screens to split the DS image&lt;/li&gt;
  &lt;li&gt;rotate each of the screens&lt;/li&gt;
  &lt;li&gt;overlap the screens so that only the score from the secondary screen is visible&lt;/li&gt;
  &lt;li&gt;rotate the d-pad direction controls&lt;/li&gt;
  &lt;li&gt;made the touch/game screen a big button&lt;/li&gt;
  &lt;li&gt;make empty space equivalent to nearest button&lt;/li&gt;
  &lt;li&gt;create a nice PDF to show the buttons visually&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re interested in the JSON associated with this skin, &lt;a href=&quot;https://gist.github.com/gingerbeardman/00a75a0675da8a98faa0812383eb822e&quot;&gt;here it is on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;maximum-joy&quot;&gt;Maximum joy&lt;/h2&gt;

&lt;p&gt;Installation and download instructions are at the bottom of the page, but here’s the final skin:&lt;/p&gt;

&lt;p class=&quot;deltaskin&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/delta-maboshi-deltaskin.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/delta-maboshi-deltaskin-instructions.png&quot; alt=&quot;PNG&quot; title=&quot;Game instructions are presented when the phone is in landscape&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;p&gt;To install the skin you need to do a little bit of busy work, but boy is it worth it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install &lt;a href=&quot;&quot;&gt;Delta&lt;/a&gt; and get it running DS games (&lt;a href=&quot;https://www.youtube.com/watch?v=lV_QfVvXA-o&quot;&gt;check this video&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Create a folder called Delta in your iCloud Drive so you can easily store/retrieve some downloads&lt;/li&gt;
  &lt;li&gt;Download the following files:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://archive.org/download/maboshi-nintendo-ds/MaBoShi.nds.zip&quot;&gt;MaBoShi.nds.zip&lt;/a&gt; game file&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://archive.org/download/maboshi-nintendo-ds/MaBoShi.jpg&quot;&gt;MaBoShi.jpg&lt;/a&gt; artwork&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://cdn.gingerbeardman.com/files/NDS_MaBoShi.deltaskin&quot;&gt;MaBoShi.deltaskin&lt;/a&gt; custom skin&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Open the .nds.zip game file in Delta&lt;/li&gt;
  &lt;li&gt;Tap and hold the game icon to show a menu&lt;/li&gt;
  &lt;li&gt;Choose “Change Artwork”&lt;/li&gt;
  &lt;li&gt;Select the .jpg you downloaded earlier&lt;/li&gt;
  &lt;li&gt;Choose “Change Controller Skin”&lt;/li&gt;
  &lt;li&gt;Select the skin using the + button and set it as both Portrait and Landscape skin for the game&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Check out &lt;a href=&quot;https://gamefaqs.gamespot.com/wii/946472-maboshis-arcade/faqs&quot;&gt;my MaBoShi Guide at GameFAQs&lt;/a&gt; to learn more about the game and how to play it. It’s essentially a one button game so isn’t affected by the lack of physical buttons.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 18 Apr 2024 19:55:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/04/18/per-game-skins-in-the-delta-classic-video-game-emulator-for-ios/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/04/18/per-game-skins-in-the-delta-classic-video-game-emulator-for-ios/</guid>
        </item>
      
    
      
        <item>
          <title>Adding the “Move to Trash” function to System 7.1</title>
          <description>&lt;p&gt;First, a little bit of Macintosh History. You probably know that on modern macOS you can select a file in Finder, on your Desktop, or in an app, and send it to the Trash by choosing the Move to Trash menu item, or by pressing Cmd+Backspace/Delete. This keyboard shortcut was added in System 7.5.3 where it was largely unadvertised and somewhat of a secret feature, but quickly became indispensable for those who knew. Of course, these days it is proudly displayed in macOS Finder menu.&lt;/p&gt;

&lt;p&gt;Fast forward to today, when &lt;a href=&quot;https://twitter.com/james_wages&quot;&gt;James Wages&lt;/a&gt; asked about a good way to do this on System 7.1, and posed a partial solution along with a challenge. But we’ll get to that in a moment. Since I regularly use &lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh/&quot;&gt;System 7 on my iPad Pro&lt;/a&gt; I’d also missed this function and had come up with a few different workarounds to map Move to Trash to Cmd+Backspace/Delete.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#keyquencer&quot;&gt;KeyQuencer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#finderhack&quot;&gt;FinderHack &amp;amp; KeyQuencer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#hexedit&quot;&gt;FinderHack &amp;amp; HexEdit&lt;/a&gt; &lt;br /&gt;Bonus:&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#keyquencer-redux&quot;&gt;KeyQuencer, Redux&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;keyquencer&quot;&gt;KeyQuencer&lt;/h2&gt;

&lt;p&gt;The first workaround was to script a &lt;a href=&quot;https://macintoshgarden.org/apps/keyquencer&quot;&gt;KeyQuencer&lt;/a&gt; macro to simulate the mouse drag of the selected item to the trash can. This relied on you positioning the mouse pointer over the selected file and also making sure the Trash icon was always in the required position on screen. It worked, but it was less than ideal because it was far too fragile. I’ll leave reproducing this as an excercise for the reader.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;KeyQuencer&lt;/em&gt; is one of my favourite and most used classic Macintosh apps, written by prolific Italian developer &lt;a href=&quot;http://www.montalcini.com&quot;&gt;Alessandro Levi Montalcini&lt;/a&gt; who is still developing &lt;a href=&quot;https://www.usboverdrive.com&quot;&gt;useful things&lt;/a&gt; today! Anyway, it could be used for a wide variety of macro and automation purposes. It was very versatile as it contained its own scripting language and a dictionary of functions that touched most aspects of System 7. I use it to map &lt;a href=&quot;/tag/keyquencer/&quot;&gt;all sorts of esoteric functions&lt;/a&gt; to hotkeys, like changing screen resolutions and colour depth, or performing complicated multi-step tasks on a single key stroke. An equivalent for modern macOS is &lt;a href=&quot;https://www.keyboardmaestro.com/main/&quot;&gt;Keyboard Maestro&lt;/a&gt;, which is no doubt more capable but also more difficult to use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;finderhack&quot;&gt;FinderHack&lt;/h2&gt;

&lt;p&gt;So, James mentioned &lt;a href=&quot;https://macintoshgarden.org/apps/finderhack&quot;&gt;FinderHack&lt;/a&gt; which is a system Extension that gave earlier versions of System 7 a similar set of Finder features to those introduced in 7.5.3. But, crucially, it mapped the Move to Trash hotkey to Cmd+T, which is different enough to become annoying as you can no longer rely on muscle memory. The challenge: how can we map this to the modern Cmd+Backspace/Delete shortcut?&lt;/p&gt;

&lt;p&gt;So, I turned to KeyQuencer once again and created a short macro which would type the Cmd+T key combination and I assigned it to Cmd+Backspace/Delete. It worked! Pressing my hotkey typed the FinderHack hotkey.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/move-to-trash-1.png&quot; alt=&quot;PNG&quot; title=&quot;This macro, bound to Cmd+Backspace/Delete, types Cmd+T to effectively map one hotkey to another&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So we already have a good solution but resources are often scarce on classic Macintosh, so I wondered if I might be able to cut out the middleman and change the keyboard shortcut by modifying FinderHack directly. James had noticed that ResEdit would not allow you to type the Backspace character. I confirmed the same was true in Resorcerer. So, what to do?&lt;/p&gt;

&lt;h2 id=&quot;hexedit&quot;&gt;HexEdit&lt;/h2&gt;

&lt;p&gt;Opening FinderHack directly in &lt;a href=&quot;https://macintoshgarden.org/apps/hexedit&quot;&gt;HexEdit&lt;/a&gt; we can scroll down a little to find the menu definitions, find “Move to Trash” and can change the following hex value from 54 (“T”) to 08 (“Backspace/Delete”), those being ASCII values, and reminding ourselves that the Backspace/Delete key is different than Forward Delete key on some Apple keyboards that have both.&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/move-to-trash-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/move-to-trash-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/move-to-trash-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/move-to-trash-3.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 100%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;p&gt;The final modified file is up over at Macintosh Garden: &lt;a href=&quot;https://macintoshgarden.org/apps/finderhack&quot;&gt;macintoshgarden.org/apps/finderhack&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/move-to-trash-4.png&quot; alt=&quot;PNG&quot; title=&quot;Notice that System 7 has no glyph for the Backspace key&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Removing the confirmation alert&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used a &lt;a href=&quot;https://github.com/fuzziqersoftware/resource_dasm/issues/77&quot;&gt;disassembler&lt;/a&gt; to figure out the code that was responsible for showing the confirmation alert that appears after pressing the hotkey, but I wasn’t sure how best to remove it. So I asked on 68KMLA.org and user &lt;em&gt;cheesestraws&lt;/em&gt; came up with a solution (thanks!) that involved NOPing out the Alert syscall setup, invokation, and return, and making the comparison that usually checks the alert button always default to the OK. I was so close to figuring out this solution myself, but I lacked a key bit of knowledge for how to figure out the hex code for a totally new instruction. Well, now I know how to do that! &lt;a href=&quot;https://68kmla.org/bb/index.php?threads/skipping-a-confirmation-alert-and-doing-the-ok-code-path.47220/post-529695&quot;&gt;Here’s all the details of how to change the machine code&lt;/a&gt;. I could have and probablt should have used Ghidra to figure out this edit to the code.&lt;/p&gt;

&lt;p&gt;Once this was done it became obvious how much of a hack FinderHack really is. After deleting the file the icon of the now missing file persists in Finder for up to a few seconds on my emulated Mac, and up to 20 seconds on period hardware! This is unacceptable, so I had to go deeper.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;keyquencer-redux&quot;&gt;KeyQuencer, Redux&lt;/h2&gt;

&lt;p&gt;I went back to old faithful, KeyQuencer, to see if there was a way I could force Finder to refresh the icon more quickly. Whilst I was reading the docs I saw the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;What Is KeyQuencer?

KeyQuencer is a utility that lets you create shortcuts, called macros, that
perform a series of tasks with a single keystroke. A macro is a set of
instructions that KeyQuencer uses to perform a task on a computer, for
example you can use KeyQuencer macros to:

   * Apply shortcuts directly to the current Finder selection, like moving
     items to the Trash.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well! It turns out KeyQuencer could have solved our problem from day one! Lesson learned: RTFM.&lt;/p&gt;

&lt;p&gt;So, how do we go about setting up KeyQuencer to enable this feature? Well, before we get to that let’s talk about how KeyQuencer does its thing. It consists of three main parts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;em&gt;KeyQuencer Engine&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;one or more &lt;em&gt;KeyQuencer Extensions&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;KeyQuencer Panel&lt;/em&gt; and/or &lt;em&gt;KeyQuencer Editor&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;one or more &lt;em&gt;KeyQuencer Macros&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The beating heart of KeyQuencer is the &lt;em&gt;KeyQuencer Engine&lt;/em&gt; system extension that lives in the usual System/Extensions folder, along with a folder in System called &lt;em&gt;KeyQuencer Extensions&lt;/em&gt; that contains KeyQuencer’s own type of extensions. Still with me?&lt;/p&gt;

&lt;p&gt;Inside the &lt;em&gt;KeyQuencer Extensions&lt;/em&gt; folder you put any KeyQuencer Extension files that you want to use, by copying them from the KeyQuencer installation folder. This was a method to keep memory usage low by only loading the functions you’re using rather than the whole suite. So if you’re using a function from the File category, you copy the “File” KeyQuencer Extension in there.&lt;/p&gt;

&lt;p&gt;Once all that is set up you can create/add KeyQuencer Macros, using either KeyQuencer Panel or KeyQuencer Editor. There are lots of sample macros included with the app to get you started, and many were made and shared by the community. The app also includes detailed command help so you know exactly what is possible and how to make it so. Eventually you can construct your own macro, which is what I’ll do here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step by step: words&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download &lt;a href=&quot;https://macintoshgarden.org/apps/keyquencer&quot;&gt;KeyQuencer 2.5.6&lt;/a&gt; which gives us a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyQuencer 2.5.6 99/07/18&lt;/code&gt; and copy the following files to their destinations:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyQuencer Engine&lt;/code&gt; goes in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System/Extensions&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyQuencer Panel&lt;/code&gt; goes in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System/Control Panels&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KeyQuencer Extensions/Archive and Files/File&lt;/code&gt; goes in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System/KeyQuencer Extensions&lt;/code&gt; (create the destination folder)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://macintoshgarden.org/apps/applescript-11&quot;&gt;AppleScript 1.1&lt;/a&gt; &lt;em&gt;Finder Scripting Software&lt;/em&gt; (this is the updated &lt;em&gt;Scriptable Finder 7.1.3&lt;/em&gt; and &lt;em&gt;Finder Scripting Extension&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;Restart the Macintosh when prompted&lt;/li&gt;
  &lt;li&gt;Create the Macro (you could also copy and paste it from a sample macro file):
    &lt;ul&gt;
      &lt;li&gt;Open &lt;em&gt;KeyQuencer Panel&lt;/em&gt; control panel&lt;/li&gt;
      &lt;li&gt;Inside the panel choose File &amp;gt; New Macro&lt;/li&gt;
      &lt;li&gt;Give the macro a name: “Move to Trash”&lt;/li&gt;
      &lt;li&gt;Give the macro a hotkey: Cmd+Backspace/Delete&lt;/li&gt;
      &lt;li&gt;Enter the macro script: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File move selected to trash enforce&lt;/code&gt; by pasting, typing, or using the gui&lt;/li&gt;
      &lt;li&gt;Click OK&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Select some files and press Cmd+Backspace/Delete to move them to the trash!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step by step: video&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s a screen recording of me doing the above on a fresh System 7.1:&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;99oQ5zZMHkI&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 12 Apr 2024 22:08:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/04/12/adding-the-move-to-trash-function-to-system-7/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/04/12/adding-the-move-to-trash-function-to-system-7/</guid>
        </item>
      
    
      
        <item>
          <title>Remote monitoring a web server job queue</title>
          <description>&lt;p&gt;I use some software called &lt;a href=&quot;https://github.com/huginn/huginn&quot;&gt;&lt;em&gt;Huginn&lt;/em&gt;&lt;/a&gt; to do various automated web searching and scraping. One use case is &lt;a href=&quot;/2023/04/10/where-can-i-see-hokusai-great-wave-today/&quot;&gt;checking the status of all the institutions where The Great Wave is currently on view&lt;/a&gt;, or not. I also have it do automatic auction searches for various items I’m looking for, that would otherwise take up a bunch of my time. And much more.&lt;/p&gt;

&lt;h2 id=&quot;huginn-we-have-a-problem&quot;&gt;Huginn, we have a problem&lt;/h2&gt;

&lt;p&gt;Huginn is installed on one of my web servers and does its thing on hourly, every day of the week without fail. Well, that’s not quite true. Sometimes, something happens outside of its control and the job queue gets jammed. It should be able to cope with this event, but for whatever reason it doesn’t. There’s no easy way to tell when this has happened, and the way I usually notice is by my RSS feed appearing less busy than usual. So, the most recent time this happened I promised myself I’d find a solution to monitoring the job queue remotely.&lt;/p&gt;

&lt;h2 id=&quot;help-from-the-community&quot;&gt;Help from the community&lt;/h2&gt;

&lt;p&gt;Another Huginn user &lt;a href=&quot;https://github.com/huginn/huginn/issues/3368#issuecomment-2045510388&quot;&gt;mentioned&lt;/a&gt; that I could send a regular request to &lt;a href=&quot;https://www.healthchecks.io&quot;&gt;healthchecks.io&lt;/a&gt;, which would raise a notification if the requests stopped coming in (a method known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Dead_man%27s_switch&quot;&gt;the Dead man’s switch&lt;/a&gt;) which is exactly what would happen when the job queue is jammed. This was a good idea! Whilst I could be notfications by email or various push notification services, I’d prefer to get my notifications the same way as my server notifications - through &lt;a href=&quot;https://uptimerobot.com&quot;&gt;uptimerobot.com&lt;/a&gt; - so I added that to the process, with the help of &lt;a href=&quot;https://toot.lv/@cuu508/112246652626290022&quot;&gt;Pēteris Caune&lt;/a&gt; the creator of healthchecks.io.&lt;/p&gt;

&lt;p&gt;Here is my final recipe:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://healthchecks.io/&quot;&gt;healthchecks.io&lt;/a&gt; to get a Ping URL&lt;/li&gt;
  &lt;li&gt;create a new Huginn Scenario with a Website Agent to request the above Ping URL every hour&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://healthchecks.io/&quot;&gt;healthchecks.io&lt;/a&gt; to get a Status Badge URL (JSON format)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://uptimerobot.com&quot;&gt;uptimerobot.com&lt;/a&gt; Keyword monitor watching the contents of the Status Badge URL (check for keyword “up”, and start incident when keyword does not exist)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that’s it! Next time the job queue jams, the Uptime Robot app will send me a push notification. I know it works because I tested it—a very important step!&lt;/p&gt;

&lt;h2 id=&quot;future-features&quot;&gt;Future features?&lt;/h2&gt;

&lt;p&gt;Maybe this setup will help me spot a pattern in the queue jams? Or perhaps I can take this further and have the Huginn restart when it gets jammed? As with all these sorts of things, I take it step by step and make one change at a time. Let’s see how it goes.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 10 Apr 2024 19:04:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/04/10/remote-monitoring-a-web-server-job-queue/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/04/10/remote-monitoring-a-web-server-job-queue/</guid>
        </item>
      
    
      
        <item>
          <title>Running modern macOS on non-retina displays</title>
          <description>&lt;p&gt;It’s 2024 and for some years now Apple have stopped supporting non-retina displays. From their perspective it makes sense given that all of their devices run retina displays, but from the user’s perspective it’s annoying given that so many of us are still using non-retina displays as either an external main display or as a secondary display. Not all of us are capable or comfortable buying multi-thousand dollar 5K displays.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My personal situation is that I run a portrait display so that I can display a long page of code. Hey, if portrait was good enough for the &lt;a href=&quot;https://en.wikipedia.org/wiki/Xerox_Alto&quot;&gt;Xerox Alto&lt;/a&gt; and is good enough for the &lt;a href=&quot;https://en.wikipedia.org/wiki/IPhone&quot;&gt;iPhone&lt;/a&gt; then it’s good enough for me. I have shortcut keys to arrange windows into halves, thirds or quarters and I’m super productive with it. I’ve been using this setup for a handful of years now, with my current setup consisting of a 1920×1200 display rotated 90-degrees giving me a desktop of 1200×1920.&lt;/p&gt;

  &lt;p&gt;You might be thinking, “why not use a rotated 1080p display?” and the answer to that question is that a lot of software won’t display correctly or even fit on a desktop of width 1080px, but is fine at 1200px. You might also be thinking “why not buy a 4K display?” and that’s because none of them are wide enough in portrait. Finally, if you’re asking “why not buy a 5K display?” then, yes, that’s a very good point. But until I do that, there’s a problem waiting to be solved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;

&lt;p&gt;There are a number of issues running modern macOS on non-retina displays that Apple seems to not want to fix, and in fact they are introducing new problems with every new version of macOS. Apple’s stance is that they don’t make resolution-specific adjustments, but I find that to not be quite true. Of course, abandoning non-retina elements or not paying attention to how they display on non-retina displays has the affect of being a resolution-specific adjustment. Plus, one of the most recent issues proves that there was a specific change made for non-retina displays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text cursor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since macOS 14, Sonoma, there’s a new redesign text cursor. It changes colour and flashes in a different way than the normal cursor, but more importantly it is 50% wider (1.5x) on non-retina displays! On retina displays the cursor is equivalent to 2px wide and on non-retina displays it is equivalent to 3px side. You might think that a single pixel might not make much of a difference, but when there’s only 2px between characters it means &lt;a href=&quot;https://forums.macrumors.com/attachments/cursor-non-retina-png.2338640/&quot;&gt;the neighbouring text is obscured&lt;/a&gt;. So it’s difficult, perhaps even impossible, to tell whether a c character to the left of the cursor is a c, d, o, q, etc. In some situations, depending on your choice of font or text size the new wider cursor will completely obscure the letters i, I, l, 1 that are next to the cursor. Reported to Apple as FB13536508.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abandoned icons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Across macOS from Finder to Safari &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1539963156570820608&quot;&gt;icons are stretched and badly positioned&lt;/a&gt; on non-retina displays, which may or may not be limited to icons made from SF Symbols. As recent as Monterey, System Preferences app would &lt;a href=&quot;https://twitter.com/gingerbeardman/status/1539963168654589954&quot;&gt;transition using retina icons and then after the transition they would be replaced with non-retina icons&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;workaround&quot;&gt;Workaround&lt;/h2&gt;

&lt;p&gt;The most useful workaround to these problems is to trick macOS into thinking that your non-retina display is retina. This is done by changing the scale value of your display.&lt;/p&gt;

&lt;p&gt;An alternative way of doing this is to use &lt;a href=&quot;https://github.com/waydabber/BetterDisplay&quot;&gt;BetterDisplay&lt;/a&gt; where you can simply click the High Resolution (HiDPI) toggle for your non-retina display.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you want to do it by hand you can open up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.apple.windowserver.displays...plist&lt;/code&gt; at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Preferences/ByHost/&lt;/code&gt;, in something like PlistEdit Pro, and search for “Scale” to change each instance of a value 1 into 2. Thanks to &lt;a href=&quot;https://www.getvladimir.com&quot;&gt;Vladimir Kochkovski&lt;/a&gt; for sharing this solution!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;improves cursor width issue (it’s less wide so neighbouring text is not obscured quite as much)&lt;/li&gt;
  &lt;li&gt;fixes many other user interface elements that are bad on non-retina displays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;screenshots are @2x, which might be unexpected or undesirable&lt;/li&gt;
  &lt;li&gt;websites that do not provide retina-ready images may appear slightly blurry&lt;/li&gt;
  &lt;li&gt;(in other words: the same things as a real retina display)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 25 Jan 2024 16:25:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/01/25/running-modern-macos-on-non-retina-displays/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/01/25/running-modern-macos-on-non-retina-displays/</guid>
        </item>
      
    
      
        <item>
          <title>JINZO Paint: vintage mobile drawing app</title>
          <description>&lt;p&gt;At my core I’m a software guy. I don’t really get attached to hardware: in my mind it exists only as a conduit to software. I use emulation whenever I can to benefit from the increased convenience and reliability. But when I can’t… I buy old devices, and with old devices come old problems.&lt;/p&gt;

&lt;p&gt;I’m fascinated by &lt;a href=&quot;/2023/10/21/list-of-vintage-japanese-pixel-dot-art-software/&quot;&gt;vintage digital art software&lt;/a&gt;, from my beginnings on Atari ST, though classic Macintosh and vintage Japanese PCs, to handhelds like Palm devices or, in this case, a PocketPC running Windows CE. As with all software, many great ideas have been abandoned in the name of “progress”.&lt;/p&gt;

&lt;h2 id=&quot;zen-and-the-art-of-software-design&quot;&gt;Zen and the Art of Software Design&lt;/h2&gt;

&lt;p&gt;Japanese art software is notable in a number of ways because their drawing tools evolved differently to those in the West, at least until Photoshop took hold. In Japan the first breakthrough digital art software was the PC-98’s &lt;a href=&quot;https://www.youtube.com/watch?v=nIdFor2WOnw&quot;&gt;Multi Paint System&lt;/a&gt; (MPS, マルチペイントシステム) released by C-Lab in 1992, though an earlier version was released in 1991 as Maguro Paint System, or Tuna Paint System (鮪ペイントシステム) both of which were programmed by Woody_RINN.&lt;/p&gt;

&lt;p&gt;MPS introduced &lt;a href=&quot;https://twitter.com/_blubot_/status/1727397680895476153&quot;&gt;the ability to lock colours and prevent them from being drawn over&lt;/a&gt;. You might think of it as a mask featuring every instance of that particular colour. You could also replace colours in a similar way. This was done through an intuitive mechanism of a toolbar button or key press to lock one or more specific colours on the palette, which is quite different to the selection or mask approaches popularised by Photoshop and common today.&lt;/p&gt;

&lt;p&gt;Regardless of whether or not the method of handling colours in MPS is novel or unique, it was adopted as a standard by most Japanese art software for many years. JINZO Paint (JZP), a digital art app for PocketPC (Windows CE) created by t-ueno (&lt;a href=&quot;http://www.tomozon.sakura.ne.jp/wince/&quot;&gt;Tomohiro Ueno&lt;/a&gt;), was one such app that adopted the MPS way of doing things and I’ve been using it a bunch recently. It offers first class support for drawing in dither patterns, and you can load in custom patterns. If you like HyperCard, NewtPaint, TealPaint, you’ll love it. &lt;em&gt;Jinzo&lt;/em&gt; is the Japanese word for kidney, which is also the icon of the app.&lt;/p&gt;

&lt;p&gt;If you’re interested in reading about the interface and functions of JINZO Paint &lt;a href=&quot;https://www.gingerbeardman.com/jzpaint/&quot;&gt;I’ve mirrored the manuals for two early versions on my website&lt;/a&gt; as the original location is no longer available. A later version supports full 24-bit colour, but removes some useful functions. Regardless, all versions use a similar interface so they’re easy to use once you’re familiar with the general operation. One thing I would say is that the toolbars can be opened and selected with a single tap-drag-release action, which a huge win for usability and one that reminds me of the original Apple Macintosh and Palm OS. If you’re lucky your operating system today will support such fluidity.&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;e&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;f&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;g&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;h&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-3.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-4.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-4.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-5.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-5.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-6.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-6.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-7.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-7.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-8.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-ui-8.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 133%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}

.carousel__activator:nth-of-type(5):checked ~ .carousel__track {
  -webkit-transform: translateX(-400%);
          transform: translateX(-400%);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__slide:nth-of-type(5) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__controls:nth-of-type(5) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(5) {
  opacity: 1;
}

.carousel__activator:nth-of-type(6):checked ~ .carousel__track {
  -webkit-transform: translateX(-500%);
          transform: translateX(-500%);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__slide:nth-of-type(6) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__controls:nth-of-type(6) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(6) {
  opacity: 1;
}

.carousel__activator:nth-of-type(7):checked ~ .carousel__track {
  -webkit-transform: translateX(-600%);
          transform: translateX(-600%);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__slide:nth-of-type(7) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__controls:nth-of-type(7) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(7) {
  opacity: 1;
}

.carousel__activator:nth-of-type(8):checked ~ .carousel__track {
  -webkit-transform: translateX(-700%);
          transform: translateX(-700%);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__slide:nth-of-type(8) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__controls:nth-of-type(8) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(8) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(5) {
  -webkit-transform: translateX(400%) translateZ(0);
          transform: translateX(400%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(6) {
  -webkit-transform: translateX(500%) translateZ(0);
          transform: translateX(500%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(7) {
  -webkit-transform: translateX(600%) translateZ(0);
          transform: translateX(600%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(8) {
  -webkit-transform: translateX(700%) translateZ(0);
          transform: translateX(700%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(5),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(5) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(6),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(6) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(7),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(7) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(8),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(8) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },10000);
  
&lt;/script&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;for-my-next-trick-i-will-run-it-on-a-dictionary&quot;&gt;For my next trick I will run it on a Dictionary&lt;/h2&gt;

&lt;p&gt;My Windows CE device is a bit odd because it’s a Brain. These are a range of electronic dictionaries made by Sharp and sold only in Japan. With a bit of gentle coaxing it can be used as a little computer running Windows CE. My particular model is the PW-SH1 which is a 3rd generation device with a high resolution screen whose hinge can rotate 360° so it’s back-to-back against the keyboard, effectively becoming a tablet computer. Some years ago it was figured out that you could sideload apps and even force these devices to open the Windows CE desktop and do all manner of crazy things. Japanese hackers and modders seem to love using these devices to run emulators for old computers like Sharp MZ-series and NEC PC-series. If you want to know more check out the &lt;a href=&quot;https://brain.fandom.com/ja/wiki/Brain_Wiki&quot;&gt;Brain Wiki&lt;/a&gt; and if you want to pick up a device &lt;a href=&quot;https://brain.fandom.com/ja/wiki/Brain機種別解説&quot;&gt;here’s a list of them all&lt;/a&gt; (browser translation required for those links).&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint.jpg&quot; alt=&quot;JPG&quot; title=&quot;JINZO Paint, 4-colour version&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;a-problem-with-file-selection&quot;&gt;A problem with file selection&lt;/h2&gt;

&lt;p&gt;Anyway, I noticed whilst using JZP that the file selector would often fail to open properly. It worked the first time but subsequent attempts saw it open and then immediately vanish. The source code to the first two versions is available, and with the help of Brain Hackers’ &lt;a href=&quot;https://twitter.com/watamario15&quot;&gt;@watamario15&lt;/a&gt; the reason for the problem was traced. He found that the value of the parameter being passed to the file selector was invalid, and provided proof by modifying the source code to those versions resulting in new working binaries. But with the final full-colour version has no source code, so how do we fix that?&lt;/p&gt;

&lt;h2 id=&quot;multiple-solutions&quot;&gt;Multiple solutions&lt;/h2&gt;

&lt;p&gt;The binaries for my device are ARM so there’s good support for debugging that type of code. I fired up Ghidra and started poking around in the earliest, smallest version of JZP. I quickly found the section of code that defined the parameters, helped by the placement of the setup of the strings used in the file selector. &lt;a href=&quot;https://www.coalfire.com/the-coalfire-blog/reverse-engineering-and-patching-with-ghidra&quot;&gt;A quick tutorial later&lt;/a&gt; and I knew how to use Ghidra to find references to data, patch instructions, and save a new binary. The workaround was to set the parameter to NULL, which works nicely but removes the ability for the app to remember the most recently used directory.&lt;/p&gt;

&lt;p&gt;However the source is available for the two earlier versions, which I prefer using, so more complete changes could be implemented. Rather than simply nulling out the parameter we could set it to the correct initial value: the root directory. Additional changes were needed to make sure the program would cope with saving and loading from the root directory of the device directly. And finally, just for good measure, the 16-colour version’s extremely slow bitmap saving has been optimised!&lt;/p&gt;

&lt;p&gt;Many thanks to &lt;a href=&quot;https://twitter.com/watamario15&quot;&gt;@watamario15&lt;/a&gt; for his invaluable help with debugging and his generous source code wrangling. And to &lt;a href=&quot;https://brain.fandom.com/ja/wiki/&quot;&gt;Brain Wiki&lt;/a&gt; for being such a valuable resource for crazy old software nerds like me!&lt;/p&gt;

&lt;h2 id=&quot;keyboard-controls&quot;&gt;Keyboard controls&lt;/h2&gt;

&lt;p&gt;I’ve also added keyboard control to JINZO Paint 16, with standard Photoshop keys to switch tools, plus keys for undo, set zoom, quick zoom (hold space bar), and more. If I get permission from the original author I’ll release a patch.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/jinzo-paint-dev.png&quot; alt=&quot;JPG&quot; title=&quot;JINZO Paint 16, source code modifications&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;repo&quot;&gt;Repo&lt;/h2&gt;

&lt;p&gt;My changes to the 16-color version are now available at the BRAIN hackers GitHub:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/brain-hackers/jinzo-paint/tree/16-color&quot;&gt;github.com/brain-hackers/jinzo-paint/tree/16-color&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://brain.fandom.com/ja/wiki/JINZO_Paint&quot;&gt;JINZO Paint page on Brain Wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://discord.com/channels/759813579120836608/1198349406878060646&quot;&gt;JINZO Paint thread on Brain Hackers Discord Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/jzpaint/&quot;&gt;JINZO Paint documentation mirror&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tomozon.sakura.ne.jp/wince/JINZO_COLLECTION/DATA_BOOK/JZP_DATA/tel/howtojzp/howtojzp.htm&quot;&gt;JINZO Paint tutorial (4-colours)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.tomozon.sakura.ne.jp/wince/JINZO_COLLECTION/DATA_BOOK/JZP_DATA/emugaro/cgmake.htm&quot;&gt;JINZO Paint tutorial (16-colours)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=nIdFor2WOnw&quot;&gt;Playback of Woody_RINN drawing in Multi Paint System&lt;/a&gt; watch the dithering by blending at &lt;a href=&quot;https://www.youtube.com/watch?v=nIdFor2WOnw&amp;amp;t=434&quot;&gt;07:14&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 22 Jan 2024 20:53:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/01/22/jinzo-paint-vintage-mobile-drawing-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/01/22/jinzo-paint-vintage-mobile-drawing-app/</guid>
        </item>
      
    
      
        <item>
          <title>Using game controllers and keyboards for custom shortcuts</title>
          <description>&lt;p&gt;Recently I’ve been following a trend in &lt;a href=&quot;https://www.thetechedvocate.org/what-is-a-macro-pad-and-what-do-you-use-it-for/&quot;&gt;macro-pads&lt;/a&gt;, specialised/bespoke keyboards that provide an easy way to trigger keyboard shortcuts. A host of small companies have flooded the market with modified Bluetooth numeric pads that target Procreate, and Figma have teamed up with Work Louder to create a branded keypad with additional jog and rotary dials.&lt;/p&gt;

&lt;p&gt;But none of these seem quite right for me. I have so many controllers and devices already it felt better to make use of what I have to hand.&lt;/p&gt;

&lt;p&gt;I’m encouraged to find that there are lots of options! I’m a macOS user and I also dabble with Windows on occasion, but I have no idea how this can be done well on Linux.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/8bitdo-micro-shortcuts.jpg&quot; alt=&quot;8Bitdo Micro&quot; title=&quot;&amp;lt;em&amp;gt;8Bitdo Micro&amp;lt;/em&amp;gt; is marketed as a multi-tasking controller&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;controllers&quot;&gt;Controllers&lt;/h2&gt;

&lt;p&gt;An obvious choice for a device with multiple buttons is a game controller. In modern macOS it’s easy to pair Nintendo Switch controllers, and the JoyCon (left or right) is an ideal candidate for a hand-held shortcut device. Xbox and PlayStation controllers can also be paired but they are much larger. Wired or wireless controllers will work.&lt;/p&gt;

&lt;p&gt;You can even use a Wii remote using an adapter like the &lt;a href=&quot;https://www.mayflash.com/product/magic_ns_lite.html&quot;&gt;&lt;em&gt;Mayflash MAGIC-NS Lite&lt;/em&gt;&lt;/a&gt;. Or you might use more esoteric controllers with an adapter from Robert Dale Smith’s &lt;a href=&quot;https://controlleradapter.com&quot;&gt;Controller Adapter&lt;/a&gt; store. In fact, I use one of his adapters to get an old &lt;a href=&quot;https://x.com/gingerbeardman/status/1629936413801062403?s=20&quot;&gt;&lt;em&gt;Sony Jog Controller&lt;/em&gt;&lt;/a&gt; to act like a GameCube controller, which I then map to keyboard shortcuts using the methods below. The sky’s the limit!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;8Bitdo&lt;/em&gt; also have their &lt;a href=&quot;https://www.8bitdo.com/micro/&quot;&gt;&lt;em&gt;Micro&lt;/em&gt; controller&lt;/a&gt; which offers more buttons and slightly better one-handed ergonomics than a Switch JoyCon. This controller is really interesting as it can pose as a Switch Pro Controller, generic controller, or keyboard. This gives us even more options. 8Bitdo are aware of this market and have created this device to fit, including a bespoke mobile app, and they even go so far as to &lt;a href=&quot;https://www.8bitdo.com/micro/#content-1-9&quot;&gt;call out this use case in their marketing material&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;keyboards&quot;&gt;Keyboards&lt;/h2&gt;

&lt;p&gt;These could be cheap bluetooth numeric pads or other small keyboards. I’ve personally tried a &lt;a href=&quot;https://niwanetwork.org/wiki/Nintendo_Wireless_Keyboard&quot;&gt;&lt;em&gt;Nintendo Wireless Keyboard&lt;/em&gt;&lt;/a&gt; that came with Nintendo DS game &lt;em&gt;Pokémon Typing Adventure&lt;/em&gt;. And of course the 8Bitdo controller mentioned above has a keyboard mode. There are many macro-pads listed for sale online, with various numbers of keys and rotary dials. Wired or wireless keyboards will work, so get creative!&lt;/p&gt;

&lt;h2 id=&quot;remapping&quot;&gt;Remapping&lt;/h2&gt;

&lt;p&gt;The 8Bitdo controller has a specific app for iOS/Android that can change its keyboard mappings. That’s cool for those mobile devices, and the iOS version will even run on an M1 Mac. But we can achieve a more versatile solution by doing the remapping in software on the computer.&lt;/p&gt;

&lt;p&gt;The general concept is to use an app that will take an input from your device of choice and map it to a keyboard shortcut or some other action that you specify. There are many apps that do this, so I’ve limited my list below to those that offer one important feature: &lt;em&gt;per-app mapping&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This means you can set different shortcuts for each app you use, and they will change as you move between apps. Of course, it is best if this happens automatically to prevent any friction in the process.&lt;/p&gt;

&lt;p&gt;I might have a button set to “zoom in” or “zoom out” that will trigger slightly different shortcuts in my text editor than in my image editor, or I might have a button set to “primary tool” and it will trigger a tool in a different way depending on the design app I am currently using.&lt;/p&gt;

&lt;p&gt;My personal favourite app is &lt;a href=&quot;https://github.com/qibinc/JoyMapperSilicon&quot;&gt;JoyMapperSilicon&lt;/a&gt;, but we really are spoiled for choice.&lt;/p&gt;

&lt;h2 id=&quot;mouse&quot;&gt;Mouse&lt;/h2&gt;

&lt;p&gt;It’s also possible to use the analog sticks and gyro of a Switch JoyCon as a mouse using QJoyControl. Analog is straightforward and as you’d expect. But using gyro basically gives you an Air Mouse which is pretty amazing.&lt;/p&gt;

&lt;h2 id=&quot;apps&quot;&gt;Apps&lt;/h2&gt;

&lt;p&gt;You can sort the table by headings: name, cost, platform, how the per-app function works, relative ease-of-use, and whether it works with devices keyboard (key) or controller (joy) or both.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Name&lt;/th&gt;
        &lt;th&gt;Cost&lt;/th&gt;
        &lt;th&gt;Platform&lt;/th&gt;
        &lt;th&gt;Per‑app?&lt;/th&gt;
        &lt;th&gt;Easy?&lt;/th&gt;
        &lt;th&gt;Type&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://support.8bitdo.com/ultimate/micro.html&quot;&gt;8BitDo Ultimate Software&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;iOS/Android&lt;/td&gt;
        &lt;td&gt;Manual&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Key&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://www.autohotkey.com&quot;&gt;AutoHotKey&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;Windows&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://folivora.ai&quot;&gt;BetterTouchTool&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$10&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Both&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/gamepad-companion/id428799479?mt=12&quot;&gt;GamePad Companion&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$10&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Manual&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/joykeymapper/id1511416593?mt=12&quot;&gt;JoyKeyMapper&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/qibinc/JoyMapperSilicon&quot;&gt;JoyMapperSilicon&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://joytokey.net/en/&quot;&gt;JoyToKey&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$7&lt;/td&gt;
        &lt;td&gt;Windows&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/zenangst/KeyboardCowboy&quot;&gt;Keyboard Cowboy&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Key&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://www.keyboardmaestro.com&quot;&gt;Keyboard Maestro&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$36&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Both&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/erikmwerner/QJoyControl&quot;&gt;QJoyControl&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://www.usboverdrive.com&quot;&gt;USB Overdrive&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$20&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Both&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 23 Dec 2023 23:06:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/12/23/using-game-controllers-and-keyboards-for-custom-shortcuts/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/12/23/using-game-controllers-and-keyboards-for-custom-shortcuts/</guid>
        </item>
      
    
      
        <item>
          <title>Going back to the old (pre-X) Twitter iOS app</title>
          <description>&lt;p&gt;There are two main ways to do this. As of 2025 I recommend the first one, using a tweaked app, but I’ll leave the second one up for the sake of history as it still works, albeit more limited in use.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;using-a-tweaked-app&quot;&gt;Using a tweaked app&lt;/h2&gt;

&lt;p&gt;My current recommended way of doing this to sideload a tweaked version of the Twitter/X app.&lt;/p&gt;

&lt;p&gt;You’ll need a tweaked Twitter.ipa and the version I use is &lt;a href=&quot;https://github.com/ghl3m0n/FuckElon&quot;&gt;a version by ghl3m0n&lt;/a&gt; that also replaces all X branding with the bird.&lt;/p&gt;

&lt;p&gt;Tweaked apps have additional plugins and extensions added to them. The most common and useful tweak is BHTwitter, which will block ads and most bots whilst enabling some extra features. The only real downside to tweaked apps is that they don’t support deep linking like the original apps, but you can use the bundled Safari extensions and/or &lt;a href=&quot;https://www.opener.link&quot;&gt;Opener app&lt;/a&gt; to work around that.&lt;/p&gt;

&lt;p&gt;Then you will need to pick a method of installing, using &lt;a href=&quot;https://altstore.io&quot;&gt;AltStore&lt;/a&gt;, &lt;a href=&quot;https://appdb.to&quot;&gt;appdb&lt;/a&gt;, &lt;a href=&quot;https://sidestore.io&quot;&gt;SideStore&lt;/a&gt;, &lt;a href=&quot;https://github.com/khcrysalis/Feather&quot;&gt;Feather&lt;/a&gt;, &lt;a href=&quot;https://sideloadly.io&quot;&gt;Sideloadly&lt;/a&gt;, or similar. Depending which method you choose you may have to reinstall or reactivate the app from time to time.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;using-an-old-version-of-the-app&quot;&gt;Using an old version of the app&lt;/h2&gt;

&lt;p&gt;Here’s how you can downgrade to an older version of Twitter (pre-X changes) and install it in a way that means &lt;em&gt;it will not be automatically updated&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;img-with-caption&quot;&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.avif&quot; type=&quot;image/avif&quot; /&gt;
  &lt;source srcset=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.webp&quot; type=&quot;image/webp&quot; /&gt;
  &lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.png&quot; alt=&quot;&quot; title=&quot;&quot; loading=&quot;lazy&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption class=&quot;caption&quot;&gt;Twitter 9.66 (858339189) running on an iPhone Xs just now&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;downloading-the-old-version&quot;&gt;Downloading the old version&lt;/h3&gt;

&lt;p&gt;This is the trickiest part of the process as it requires installing some old software and following a guide. But don’t worry—it’s not that difficult! You’ll need access to an old version of iTunes (so it’s most easily done using Windows) and about 15 minutes to carry out the steps.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/qnblackcat/How-to-Downgrade-apps-on-AppStore-with-iTunes-and-Charles-Proxy&quot;&gt;Follow this great step-by-step tutorial&lt;/a&gt;. It might seem complicated but it is quite easy. A high level summary of what is involved in the guide is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install old iTunes &amp;amp; Charles Proxy&lt;/li&gt;
  &lt;li&gt;Configure intercepting of the latest app download&lt;/li&gt;
  &lt;li&gt;Change the version of the app to be downloaded&lt;/li&gt;
  &lt;li&gt;Download the old version&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id 858339189 (version 9.66) predates the rebrand to X&lt;/li&gt;
  &lt;li&gt;id 848443565 (version 9.7.2) predates Blue&lt;/li&gt;
  &lt;li&gt;id 840768123 (version 8.56) predates Spaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result of this process is a completely legitimate .ipa file, tied to your Apple ID in exactly the same way that apps normally are. As such, the resulting file can be installed on your device and once installed would be no different to an app you’ve downloaded directly from the App Store. This also means that if you share your .ipa with somebody else they would need to log in to your account using your Apple ID to do so, which is undesirable. Best if they download their own! Send them this blog post.&lt;/p&gt;

&lt;h3 id=&quot;avoiding-updates&quot;&gt;Avoiding updates&lt;/h3&gt;

&lt;p&gt;If you install the .ipa file onto your phone using Finder, iTunes, Apple Configurator it will install just fine. But, because of the metadata that is included the .ipa, it will be checked for updates and automatically updated soon after installation. We need to go deeper.&lt;/p&gt;

&lt;p&gt;There is a little-known method of installing apps on an iOS device which will prevent it from being checked for updates. I discovered this method &lt;a href=&quot;/2016/07/19/how-to-prevent-an-individual-ios-app-from-updating-forever/&quot;&gt;back in 2016&lt;/a&gt; when I used it to downgrade the eBay and Gmail Inbox apps.&lt;/p&gt;

&lt;h3 id=&quot;installing-the-app&quot;&gt;Installing the app&lt;/h3&gt;

&lt;p&gt;Before we begin, you don’t need to remove the current app you’re using. But best to make sure to backup any data you need from it regardless! You may, or may not, need to login once the old version of the app is reinstalled.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Copy your backup of .ipa somewhere where you can work on it&lt;/li&gt;
  &lt;li&gt;Open the .ipa with Archive Utility to decompress it&lt;/li&gt;
  &lt;li&gt;Expand the resulting folder until you go into the Payload folder&lt;/li&gt;
  &lt;li&gt;You’ll see Twitter.app (on macOS the icon has a “no entry sign” because it’s an iOS app)&lt;/li&gt;
  &lt;li&gt;Connect your device&lt;/li&gt;
  &lt;li&gt;Open Apple Configurator (you might need to &lt;a href=&quot;https://archive.org/details/apple-configurator&quot;&gt;use an old version&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Double click on your device (missing this will mean you can’t do step 8)&lt;/li&gt;
  &lt;li&gt;Click Add &amp;gt; App &amp;gt; Choose from my Mac…&lt;/li&gt;
  &lt;li&gt;Confirm you want to overwrite the old app&lt;/li&gt;
  &lt;li&gt;Wait for the app to install on your device&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’re done!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-payload.png&quot; alt=&quot;PNG&quot; title=&quot;Locating the Twitter .ipa Payload&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;questions&quot;&gt;Questions&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why can’t I add the app to my phone?&lt;/strong&gt;
    Either you missed step 7, or you need to &lt;a href=&quot;https://archive.org/details/apple-configurator&quot;&gt;use an older version of Apple Configurator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does downloading this way work?&lt;/strong&gt;
    iTunes used to be able to download apps this way, so we’re just persuading it to download a particular version. The app is attached to your Apple ID and totally legitimate, no funny business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does installing this way work?&lt;/strong&gt;
    Installing the payload directly means the App Store app has no record of the app being installed so it does not check for updates to it. You can confirm this by going to the App Store page for the app, where it will show it is yet to be downloaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will the old app stop working at some point?&lt;/strong&gt;
    Eventually, yes. Older versions lack support for modern Twitter features but that can be a desirable feature depending on your point of view. But let’s enjoy the old experience for as long as we can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get help to do this?&lt;/strong&gt;
    No, sorry. You need to do it yourself for various reasons. It will take less than 1 hour to read all the steps, then follow them one-by-one.&lt;/p&gt;

&lt;p&gt;Long live the bird!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 17 Aug 2023 18:39:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/</guid>
        </item>
      
    
      
        <item>
          <title>Extracting sounds from Macromedia Director files</title>
          <description>&lt;p&gt;For my latest “quick” &lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; project—a remaster of a ~1997 web game by &lt;a href=&quot;https://lostmediawiki.com/Thoru_Yamamoto_works_(partially_found_interactive_media;_1990s)&quot;&gt;Thoru Yamamoto&lt;/a&gt;—I decided to add sound effects. In order to keep it as authentic as possible I decided to use only sound effects created by Thoru Yamamoto that were used in his other productions.&lt;/p&gt;

&lt;p&gt;The largest collection of sounds I could think of were those included in his Macromedia Director web experiments which include everything from short animations, through games and interactive toys, to abstract slideshows. The problem is, these Director files are tricky to deal with some 25 years later.&lt;/p&gt;

&lt;h2 id=&quot;halt-and-catch-fire&quot;&gt;Halt and Catch Fire&lt;/h2&gt;

&lt;p&gt;A bit of reading and some help from &lt;a href=&quot;https://www.mistys-internet.website&quot;&gt;Misty De Méo&lt;/a&gt; led me to &lt;a href=&quot;https://github.com/ProjectorRays/ProjectorRays&quot;&gt;ProjectorRays&lt;/a&gt; which can convert a protected .dcr file into an editable .dir file, and also allows saving of all the individual chunks that comprise each file. Think of it as one chunk for each piece of graphics, sound, and so on.&lt;/p&gt;

&lt;p&gt;Once you’ve converted the .dcr to .dir you can open the file in, say, &lt;a href=&quot;https://vinizinho.net/projects/shockwave-rip&quot;&gt;Macromedia Director 2004 and use CastRipperTool&lt;/a&gt; to export sounds and graphics and more. But it’s a very manual process and the whole setup is prone to crashing. Crucially, it won’t open some older Director movies so this wasn’t a good enough solution for me.&lt;/p&gt;

&lt;h2 id=&quot;chunks&quot;&gt;Chunks&lt;/h2&gt;

&lt;p&gt;I looked to the chunks that had been dumped by ProjectorRays and some quick experimentation showed that the ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snd &lt;/code&gt;’ chunks contained raw PCM audio date with a bespoke header. Loading these into something like ocenaudio wave editor was proof enough, but I would need to figure out some of the header to see if I could get the correct sample rate of each file.&lt;/p&gt;

&lt;p&gt;With a bit of help bouncing ideas off &lt;a href=&quot;https://hikari.noyu.me&quot;&gt;hikari_no_yume&lt;/a&gt; in a &lt;a href=&quot;https://github.com/hikari-no-yume/dream-sparer/issues/1&quot;&gt;bug report&lt;/a&gt; on one of their tools I figured out the location of the sample rate. Interestingly, the values were unexpected and not the usual values 11025, 22050, etc. It turns out that classic Apple Macintosh used some slightly &lt;a href=&quot;https://whitefiles.org/dta/pgs/c08.htm&quot;&gt;different sample rate values&lt;/a&gt;: 11127, 22254, etc.&lt;/p&gt;

&lt;p&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xxd&lt;/code&gt; tool I dumped the relevant section of the headers of 1163 sounds from 105 .dcr files. The breakdown in sample rates found was as follows:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Rate (Hz)&lt;/th&gt;
      &lt;th&gt;Hex&lt;/th&gt;
      &lt;th&gt;Quantity Found&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;7,418&lt;/td&gt;
      &lt;td&gt;0x1cfa&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;11,127&lt;/td&gt;
      &lt;td&gt;0x2b77&lt;/td&gt;
      &lt;td&gt;616&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;22,050&lt;/td&gt;
      &lt;td&gt;0x5622&lt;/td&gt;
      &lt;td&gt;31&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;22,254&lt;/td&gt;
      &lt;td&gt;0x56ee&lt;/td&gt;
      &lt;td&gt;515&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The lack of any unexpected sample rate values was further proof that this was the real deal.&lt;/p&gt;

&lt;h2 id=&quot;shell-script&quot;&gt;Shell script&lt;/h2&gt;

&lt;p&gt;Armed with this information I decided to write a short shell script that would do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;extract the sample rate from the ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snd &lt;/code&gt;’ chunk&lt;/li&gt;
  &lt;li&gt;create a trimmed raw pcm file excluding the 78-byte header&lt;/li&gt;
  &lt;li&gt;use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sox&lt;/code&gt; to add a new WAV header using the correct sample rate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This script allows me to batch process all the audio and that’s much more to my liking: it captures everything compared to CastRipperTool and it’s a lot quicker.&lt;/p&gt;

&lt;p&gt;So, dump your chunks using ProjectorRays and then call my script like this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.bin&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; ./bin2wav.sh &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/1e6170d2652352bf30623b2a6c8d12fd&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/1e6170d2652352bf30623b2a6c8d12fd.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;I’m assuming 8-bit, mono, unsigned PCM data in the ‘&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snd &lt;/code&gt;’ chunks and have not found anything else in them.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sox.sourceforge.net/sox.html&quot;&gt;SoX&lt;/a&gt; (Sound eXchange, the Swiss Army knife of audio manipulation) requires the .raw extension for raw PCM audio data, it will refuse to process the files without it.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 12 Aug 2023 19:42:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/08/12/extracting-sounds-from-macromedia-director-files/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/08/12/extracting-sounds-from-macromedia-director-files/</guid>
        </item>
      
    
      
        <item>
          <title>The first colour Playdate game?</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;This blog post assumes some familiarity with &lt;a href=&quot;https://play.date&quot;&gt;Playdate&lt;/a&gt; (a handheld game console with a cool crank control scheme), &lt;a href=&quot;https://play.date/dev/&quot;&gt;Playdate SDK&lt;/a&gt; and the &lt;a href=&quot;https://www.lua.org/manual/5.4/&quot;&gt;Lua programming language&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;During the development of my forthcoming Playdate game &lt;em&gt;&lt;a href=&quot;/2023/06/26/ball-und-panzer-golf-making-a-playdate-game-in-a-week/&quot;&gt;Ball und Panzer Golf&lt;/a&gt;&lt;/em&gt; (tentative title), I wanted to be able to draw to the debug layer from anywhere in my code. The SDK allows you to draw to the debug layer only from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drawDebug&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;After filing a &lt;a href=&quot;https://devforum.play.date/t/additional-way-to-do-debug-draw-from-anywhere-in-code/11735&quot;&gt;feature request&lt;/a&gt; I thought about it some more and &lt;a href=&quot;&quot;&gt;came up with a workaround&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. (on initialisation) create a full screen opaque image&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graphics&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;display&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;overlay&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;disp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getHeight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kColorBlack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. (anywhere in your code) draw into that using pushContext (or lockFocus)&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pushContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kColorWhite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setLineWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drawCircleAtPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- draw some debug stuff into our overlay image&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;popContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. (in debugDraw function) draw the single image of our collected debug drawing&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;playdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debugDraw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- draw our overlay image containing all debug draws&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;overlay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gfx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kColorBlack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;-- blank overlay ready for the next update&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means I can draw debug info about a thing from the same code and logic responsible for that thing. For me, with this game, that makes a lot of sense.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/playdate-debugdraw.png#playdate&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;abusing-the-system&quot;&gt;Abusing the system&lt;/h2&gt;

&lt;p&gt;After debugging the positions of everything during my collision logic, it occurred to me that I could abuse this system to give the game a colour overlay.&lt;/p&gt;

&lt;p&gt;I do this by using the debug draw in the opposite way to how it’s supposed to be used. Instead of drawing just the debug information, I set the hole screen to draw in colour and then—in the same way as above—at various points in my code &lt;em&gt;I punch out areas of the screen I do not want drawn in colour&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I’m not quite drawing everything twice, as the areas that are punched out are simple shapes approximating the elements in my game. Plus, this is made easier for me because everything in the game is already being drawn only with filled shapes.&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 5/3;&quot; videoid=&quot;fqv1kwfW5r8&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;p&gt;One interesting thing about this technique is that changing the contrast (dither pattern opacity) of the golf greens only when running in “colour mode” on the Simulator made things look better. That’s to say that adding colour also adds an extra complexity with regards to contrast. I think that’s the only change to the graphics I’ve done so far but there is opportunity for more.&lt;/p&gt;

&lt;p&gt;When I sent a build out to testers I put a cryptic note in the changelog “added: chartreuse tinted glasses mode” but only one tester figured out what it was referring to. I had asked ChatGPT to suggest a colour that sounds like a shade of red, but is actually a shade of green: it suggested chartreuse (and so I use the “proper” chartreuse hex colour for the base colour of my green layer).&lt;/p&gt;

&lt;h2 id=&quot;multi-colour&quot;&gt;Multi-colour?&lt;/h2&gt;

&lt;p&gt;This approach could be taken further if the Playdate SDK allowed multiple colours to be used for debug drawing, &lt;a href=&quot;https://devforum.play.date/t/support-mutiple-colors-in-debugdraw/5848&quot;&gt;keep an eye on this feature request&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;physical-overlays&quot;&gt;Physical overlays&lt;/h2&gt;

&lt;p&gt;Back in October 2021 I bought transparency film in a range of colours to make a physical screen overlay, inspired by early arcade games like Space Invaders and the Vectrex gaming system, which worked but is obviously more hassle as the transparency picks up lint and gets dirty really quickly. This split of blue/green was for sky/grass in a 3D golf game.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/playdate-physical-overlays.jpg&quot; alt=&quot;JPG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/06/26/ball-und-panzer-golf-making-a-playdate-game-in-a-week/&quot;&gt;Ball und Panzer Golf: making a Playdate game in a week&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 09 Jul 2023 12:59:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/07/09/the-first-colour-playdate-game/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/07/09/the-first-colour-playdate-game/</guid>
        </item>
      
    
      
        <item>
          <title>Beyerdynamic Blue Byrd (1st generation) battery replacement</title>
          <description>&lt;p&gt;I’ve had a pair of Beyerdynamic Blue Byrd 1st generation bluetooth earbud headphones since June 2020. They are great Bluetooth headphones but were recalled just after I bought them. Somebody got too sweaty and managed to somehow burn themselves on the headphone wire. Given that no replacement was being offered I refused to send mine back in the recall. I don’t exercise with mine so I figured I’d be safe enough and I have been.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Warning: I’d like to take the opportunity to mention how bad Beyerdynamic customer support is: absolutely atrocious. Everybody I dealt with over the phone were really aggressive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The headphones have a companion app that you can do a hearing test with, and you can supplement that one with a second test through the &lt;a href=&quot;https://mimi.io/mimi-hearing-test-app&quot;&gt;Mimi hearing test app&lt;/a&gt;. The results can be used to personalised the sound that is produced, counteracting any hearing loss you have. For me, it seems to amplifies sound in the frequency range of my age-related hearing loss and tinnitus so that I can hear sounds I can’t hear so clearly using normal headphones. It works well and the sound is really great. A short while after the recall official support for upgrading the firmware was pulled from the app, but everything else continues to work as well as I need it to.&lt;/p&gt;

&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;

&lt;p&gt;After three years the expected battery life was down from around 6 hours to perhaps 2 or 3 hours at best. To give you an idea of what an inconvenience this is: I’d have to go into a movie with a full charge or they’d run out part way through. I looked around for a replacement set but they’re hard to come by and I don’t like the design of the 2nd generation model so I decided to see if I could change the batteries myself. I watched some YouTube videos and it seemed doable!&lt;/p&gt;

&lt;p&gt;I couldn’t find any direct replacements, the &lt;em&gt;3.7V VDL 53mAh 410920 lithium-ion&lt;/em&gt; seem unobtainable in 2023 and I needed two of them. I eventually settled on two &lt;em&gt;3.7V 80mAh 401119 lithium-polymer&lt;/em&gt; batteries, of no particular brand, but from a reputable eBay seller. They are pretty much identical sizes—roughly 2cm x 1cm x 0.4cm—and are a straight swap. The new batteries have a higher power rating so I’m hoping to get longer usage between charges: we’ll see.&lt;/p&gt;

&lt;h2 id=&quot;lets-do-this&quot;&gt;Let’s do this!&lt;/h2&gt;

&lt;p&gt;The battery cover compartments separate by pulling apart—carefully—perpendicular to the seam (so, each half directly away from each other and the line of the wire). One battery, I think the furthest from the remote, has two wires: ground and positive. The other battery, I think closest to the remote, has three wires: ground, positive and negative. Just desolder the existing batteries and solder the new batteries in their place.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Warning: the remote itself doesn’t need to be opened, as it contains only a small circuit board with surface mounted buttons, the USB-C charging socket, and related bluetooth electronics. It opens with a similar process to the battery covers, but I found that it is more easily damaged (and I did damage mine) so I would recommend leaving well enough alone!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All in all replacing the batteries was pretty straight forward if you have the right tools, I recommend at least a magnifier or preferably microscope screen, and a good hot soldering iron. My friend &lt;a href=&quot;https://twitter.com/daver888&quot;&gt;Dave&lt;/a&gt; had all the gear which made the job easy. So, thanks to him!&lt;/p&gt;

&lt;p&gt;Initial usage with the new batteries is encouraging. In a week or two I’ll edit this blog post with the sort of usage time I’m getting.&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 16/9;&quot; videoid=&quot;ktpNqWAlB0s&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;p&gt;Here’s a little bit of video showing me unsoldering one of the original batteries. Everything is tiny!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 30 Jun 2023 11:18:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/06/30/beyerdynamic-blue-byrd-replacement-batteries/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/06/30/beyerdynamic-blue-byrd-replacement-batteries/</guid>
        </item>
      
    
      
        <item>
          <title>Preserving the Marguerite Hanafuda browser game</title>
          <description>&lt;p&gt;Marguerite is a defunct Japanese website, previously at &lt;a href=&quot;https://marguerite.jp&quot;&gt;marguerite.jp&lt;/a&gt; (dead link) that hosted HTML5 implementations of Hanafuda and Mahjong.&lt;/p&gt;

&lt;p&gt;Their Hanafuda in particular was very well done, offering a variety of rulesets some of which are difficult to find in video game form and impossible to find in a browser game. The experience was single player versus one or two CPU players.&lt;/p&gt;

&lt;p&gt;The complete list of rules offered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2-player&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Koi-Koi&lt;/li&gt;
  &lt;li&gt;Mushi (aka “Insect”)&lt;/li&gt;
  &lt;li&gt;Roppyakken (aka “600”)&lt;/li&gt;
  &lt;li&gt;Hachi-Hachi (aka “88”)&lt;/li&gt;
  &lt;li&gt;Hachi (aka “8”)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3-player&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Hana-Awase&lt;/li&gt;
  &lt;li&gt;Hachi-Hachi (aka “88”)&lt;/li&gt;
  &lt;li&gt;Sudaoshi&lt;/li&gt;
  &lt;li&gt;Roppyakken (aka “600”)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/marguerite-hanafuda.png&quot; alt=&quot;PNG&quot; title=&quot;Marguerite Hanafuda&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;gone-but-not-forgotten&quot;&gt;Gone but not forgotten&lt;/h2&gt;

&lt;p&gt;The site went offline mid-2022, about a year ago at this point, and all was thought to be lost. We had tried the Wayback Machine but the archive seemed incomplete.&lt;/p&gt;

&lt;p&gt;This week Marguerite was mentioned on the &lt;a href=&quot;https://discord.com/invite/mKbdwy9&quot;&gt;Hanafuda Discord&lt;/a&gt;, so I decided to try again. Taking a fresh look at the state of the site, it seemed to be stalling trying to load two images.&lt;/p&gt;

&lt;p&gt;A couple of small changes later (two hard-coded URLs in the JavaScript pointed to the dead website) I managed to get the desktop version of the Marguerite Hanafuda working locally!&lt;/p&gt;

&lt;h2 id=&quot;its-alive&quot;&gt;It’s alive!&lt;/h2&gt;

&lt;p&gt;So, I’m now hosting a mirror copy on my website: &lt;a href=&quot;https://marguerite.gingerbeardman.com&quot;&gt;marguerite.gingerbeardman.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sound requires Chrome&lt;/li&gt;
  &lt;li&gt;Safari/Chrome built-in translation works well for this web app&lt;/li&gt;
  &lt;li&gt;Some links out of the game will be broken&lt;/li&gt;
  &lt;li&gt;if Marguerite.jp comes back online I’ll remove my mirror&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read more about the game rules:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/games&quot;&gt;Fuda Wiki&lt;/a&gt; (English)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://marguerite.gingerbeardman.com/Nihongo/Games/しらぎく花札/index.html&quot;&gt;Marguerite rules website&lt;/a&gt; (Japanese)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;mobile--mahjong&quot;&gt;Mobile &amp;amp; Mahjong?&lt;/h2&gt;

&lt;p&gt;Sadly the Wayback Machine archive of the Marguerite website is incomplete, so Mobile Hanafuda is lost as are both versions of Marguerite Mahjong.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 23 Jun 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/06/23/preserving-the-marguerite-hanafuda-browser-game/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/06/23/preserving-the-marguerite-hanafuda-browser-game/</guid>
        </item>
      
    
      
        <item>
          <title>Resurrecting an ASMedia USB hard drive enclosure</title>
          <description>&lt;p&gt;A while ago I bought a 2.5” Crucial SSD and a &lt;em&gt;UGREEN USB 3.1 Gen 2 Hard Drive Enclosure&lt;/em&gt; (SKU: &lt;a href=&quot;https://www.ugreen.com/collections/hard-drive-enclosure/products/usb-3-1-gen-2-hard-drive-enclosure&quot;&gt;70499&lt;/a&gt;) on which to keep some working files whilst connected to a Mac mini. I used that on macOS 10.13 High Sierra, 10.14 Mojave, and 11 Big Sur (I skipped 10.15 Catalina). Over the years the setup served me well.&lt;/p&gt;

&lt;p&gt;After I upgraded to an M1 MacBook Pro I didn’t need to use that sort of setup, so I just kept the external drive in my travel bag to be used as a backup drive.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;The first time I plugged it in on macOS 12 Monterey it didn’t show up, but I didn’t have time to troubleshoot so I put it away again. Today I had some free time so I plugged it in and, unsurprisingly, it did not mount. Time to do some research!&lt;/p&gt;

&lt;p&gt;First, I tried it in my Windows VM. No joy. Then I tried it on a real Windows PC: it mounted briefly and then disappeared. Interesting! Thankfully Windows gave me a little more information to go on than macOS, it showed the chipset model number: ASMT2235&lt;/p&gt;

&lt;h2 id=&quot;the-diagnosis&quot;&gt;The diagnosis&lt;/h2&gt;

&lt;p&gt;A quick web search found some &lt;a href=&quot;https://www.station-drivers.com/index.php/en/outils/Drivers/Asmedia/ASM-105x-115x-215x-(ASMT-xxxx)-Sata-USB-3.x-controllers/ASM2235-Sata-USB-3.1-controllers/ASMedia-ASM2235-SATA-USB-3.1-Firmware-Version-161102_D1_05_01/lang,en-gb/&quot;&gt;references to a firmware update&lt;/a&gt; that “resolves intermittent mounting issues in Windows” which sounded like just the ticket!&lt;/p&gt;

&lt;h2 id=&quot;the-plan&quot;&gt;The plan&lt;/h2&gt;

&lt;p&gt;I found a great &lt;a href=&quot;https://www.youtube.com/watch?v=DOxrXnEwqJY&quot;&gt;YouTube video that showed the process&lt;/a&gt; and explained what you should and should not do. The video was created in late 2022 and advised installing the firmware that started with the same number as the one on your drive, but my drive already had that installed (version 171120_D1_1E_80). Then, whilst I was looking at all the other available firmware, I noticed that the first part of the filename looked more like a date to me, one that roughly matched when the firmware was released.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;/h2&gt;

&lt;p&gt;So I decided to try flashing the firmware with the most recent date (version &lt;a href=&quot;https://www.usbdev.ru/files/asmedia/asmt2235firmware/&quot;&gt;220906_D1_45_01&lt;/a&gt;). The flashing tool, MPTools, needs to have its interface unlocked using the password “asmedia” and then you should select the line in the window that corresponds to your drive. It shouldn’t let you flash the wrong drive, but please be careful!&lt;/p&gt;

&lt;p&gt;After displaying a green progress bar, the tool declared the process was a “PASS”, and everything looked great. I unmounted the disk from Windows and it appeared in macOS — it had worked!&lt;/p&gt;

&lt;h2 id=&quot;lights-out&quot;&gt;Lights out?&lt;/h2&gt;

&lt;p&gt;The only change after all of this is that the drive enclosure no longer shows a LED when powered on, which is a minor inconvenience in the grand scheme of things.&lt;/p&gt;

&lt;p&gt;The LED can be enabled by flashing a slightly less than newest firmware (version &lt;a href=&quot;https://www.usbdev.ru/files/asmedia/asmt2235firmware/&quot;&gt;200416_D1_43_00&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;At this point, given that it’s proven these firmware updates do contain fixes, the choice is yours whether to have the light or not.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 22 Apr 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/04/22/resurrecting-an-asmedia-usb-hard-drive-enclosure/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/04/22/resurrecting-an-asmedia-usb-hard-drive-enclosure/</guid>
        </item>
      
    
      
        <item>
          <title>Working around a breaking change in WebKit CSS</title>
          <description>&lt;p&gt;Safari 16.4 has some breaking changes (fixes?) to CSS, which means &lt;a href=&quot;https://github.com/uetchy/Polyglot&quot;&gt;Polyglot&lt;/a&gt; the Safari Extension by &lt;a href=&quot;https://twitter.com/uechz&quot;&gt;Yasuaki Uechi&lt;/a&gt; that I use every day no longer displays its popup. Oh no!&lt;/p&gt;

&lt;p&gt;It turns out that some &lt;a href=&quot;https://github.com/uetchy/Polyglot/blob/2c3c52e6eb35f0bd1a59a067afc92ff8f876fbc1/PolyglotSafariExtension/ContentScript/content.css#L39&quot;&gt;6-year-old CSS&lt;/a&gt; and recent changes in WebKit mean the translation popup disappears immediately after it appears.&lt;/p&gt;

&lt;p&gt;But, all is not lost, I created a quick-fix workaround with a user stylesheet!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/uetchy/Polyglot/issues/128#issuecomment-1487229870&quot;&gt;github.com/uetchy/Polyglot/issues/128#issuecomment-1487229870&lt;/a&gt;&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/5baabc9b141e1c395c73af237fdd0e49&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/5baabc9b141e1c395c73af237fdd0e49.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 28 Mar 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/03/28/working-around-a-breaking-change-in-webkit-css/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/03/28/working-around-a-breaking-change-in-webkit-css/</guid>
        </item>
      
    
      
        <item>
          <title>Working around the YouTube Channel RSS limit</title>
          <description>&lt;p&gt;I’ve been falling behind watching Masahiro Sakurai’s game development videos, so decided to subscribe to his YouTube channel using RSS. This is as simple as plugging the channel URL https://youtube.com/@sora_sakurai_en into your RSS feed reader.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BUT only the most recent 15 videos will be listed!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google’s YouTube API allows listing of the most recent 50 videos, but that’s easier said than done as you’d have to get an API key, write some code to do this query, and host it somewhere online. Wouldn’t it be great if somebody else has already done the hard work and is sharing their solution?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Well, we’re in luck!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thanks to this &lt;a href=&quot;https://stackoverflow.com/questions/56430703/how-to-use-youtube-data-api-v3-to-get-more-than-15-videos-in-an-rss-reader-ne#comment99478631_56430703&quot;&gt;helpful StackOverflow comment&lt;/a&gt;, you can use the following URL to list the most recent 50 videos from Sakurai’s channel:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;https://scriptbarrel.com/xml.cgi?channel_id=UCv1DvRY5PyHHt3KN9ghunuw&amp;amp;name=%40sora_sakurai_en&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will work for any other YouTube channel by simply substituting the relevant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;channel_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; details.&lt;/p&gt;

&lt;h2 id=&quot;if-you-need-more-than-50&quot;&gt;If you need more than 50&lt;/h2&gt;

&lt;p&gt;You can &lt;a href=&quot;https://authory.com/blog/create-a-youtube-rss-feed-with-vastly-increased-limits/&quot;&gt;use Authory&lt;/a&gt; (free 30 day trial) to create a feed that will return up to 1000 most recent videos.&lt;/p&gt;

&lt;h2 id=&quot;important-getting-back-on-track&quot;&gt;Important: getting back on track&lt;/h2&gt;

&lt;p&gt;After you’ve retrieved the historic videos, you should edit the RSS feed link in your reader to change it back to the original YouTube channel link. This will let you keep the historic videos from the workaround feed and retrieve any future videos from the standard YouTube feed, so you’re not relying on a third-party service.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 09 Jan 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/01/09/working-around-the-youtube-channel-rss-limit/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/01/09/working-around-the-youtube-channel-rss-limit/</guid>
        </item>
      
    
      
        <item>
          <title>Turning a Twitter Thread into a Blog Post</title>
          <description>&lt;p&gt;Sometimes after posting to Twitter I’ll decide that I should really archive the information as a blog post. For single tweets that’s as easy (or difficult) as copying the text and images and publishing. For Twitter threads, it could be a lot more complicated. With a bit of lateral thinking I’ve found an easy way to do it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Get the URL to a post in the thread&lt;/li&gt;
  &lt;li&gt;Unroll the thread using &lt;a href=&quot;https://threadreaderapp.com&quot;&gt;threadreaderapp.com&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Open the web inspector&lt;/li&gt;
  &lt;li&gt;Highlight the element that contains the content (the one with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data-controller=&quot;mentions&quot;&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Right click, and choose Copy &amp;gt; HTML&lt;/li&gt;
  &lt;li&gt;Paste the HTML into this &lt;a href=&quot;https://codebeautify.org/html-to-markdown&quot;&gt;Markdown converter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Copy the Markdown&lt;/li&gt;
  &lt;li&gt;Paste into your blogging system&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The conversion from HTML to Markdown cleans or sanitises the content making for a straightforward paste into most blogging software.&lt;/p&gt;

&lt;h2 id=&quot;optional-post-processing&quot;&gt;Optional post-processing&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;You might want to repoint all embedded images from Twitter to locally-hosted copies&lt;/li&gt;
  &lt;li&gt;Some images in long threads might be lazy loaded, so you’ll need to fix those&lt;/li&gt;
  &lt;li&gt;Check and reinstate any embedded video iframes&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 09 Jan 2023 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/01/09/turning-a-twitter-thread-into-a-blog-post/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/01/09/turning-a-twitter-thread-into-a-blog-post/</guid>
        </item>
      
    
      
        <item>
          <title>Updating the Becker Cascade navigation filesystem</title>
          <description>&lt;p&gt;I have a &lt;a href=&quot;https://ifdesign.com/en/winner-ranking/project/becker-traffic-pro-7949/2069&quot;&gt;Becker Traffic Pro 7949&lt;/a&gt; car audio and navigation system in my LHD Renault Twingo mk1. This unit, effectively a re-badged Becker Cascade with a green OLED screen rather than colour, was bundled with high-end sports cars of German and Italian origin, but I added one to my lowly Twingo because it’s a 1DIN headunit with great sound quality, navigation and music support from CF card, that gives spoken and on-screen turn-by-turn directions!&lt;/p&gt;

&lt;p&gt;Even today the interface is rock solid and obvious, and the turn-by-turn directions pack a lot of information on screen. Below we can see (clockwise from top right): menu marker, ETA, distance to next turn as number and as shaded road edge, turn-by-turn arrow, traffic message control badge, remaining journey time, current road and next road.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/becker-7949.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2009/09/05/hacking-the-becker-cascade-navigation-cf-card/&quot;&gt;A while ago I hacked the CF card structure&lt;/a&gt; to allow use of larger capacity cards to more easily store maps and music side-by-side. Good times!&lt;/p&gt;

&lt;h2 id=&quot;upgrading-maps&quot;&gt;Upgrading maps&lt;/h2&gt;

&lt;p&gt;The maps for this thing were long discontinued, but for a while users such as myself were able to use maps made for BMW’s business fleet (with partial postcode search) and more recently Mercedes-Benz (with no postcode search).&lt;/p&gt;

&lt;p&gt;It’s also possible to add speed limit and camera data through injecting a new .lwd file (thanks SCDB!) into the split ISO filesystem. This relied on using Windows and some old apps.&lt;/p&gt;

&lt;p&gt;Today I wrote a small shell script to do the same on macOS (and probably Linux, maybe Windows).&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/f18de0abe61c8dd548b4901b606bf39b&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/f18de0abe61c8dd548b4901b606bf39b.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 11 Dec 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/12/11/updating-the-becker-cascade-navigation-filesystem/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/12/11/updating-the-becker-cascade-navigation-filesystem/</guid>
        </item>
      
    
      
        <item>
          <title>Fixing bugs using Bird on Palm OS</title>
          <description>&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-pixels.jpg&quot; alt=&quot;JPG&quot; title=&quot;Tenohira Hanafuda for Palm OS, running on a Sony CLIÉ PEG-SJ22&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fudawiki.org/en/hanafuda/video-games/palm/tenohira-hanafuda-kai&quot;&gt;Tenohira Hanafuda Kai&lt;/a&gt; (掌花札 kai) is a koi-koi card game for Palm OS, created in 2001 by Hiroki Takahashi. It’s a fun game with varying difficulty, stats tracking, and lovely high-resolution (for the time!) colour graphics.&lt;/p&gt;

&lt;p&gt;But, it has one serious problem: sometimes it will forfeit the current round when you choose to continue!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-buggy.gif#pixel&quot; alt=&quot;GIF&quot; title=&quot;https://youtube.com/shorts/0fZefFpGd5Y&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;analysis&quot;&gt;Analysis&lt;/h2&gt;

&lt;p&gt;After repeated play I figured out that the game would continue as intended only if I pressed the right half of the こいこい (koi-koi) button.&lt;/p&gt;

&lt;p&gt;But if I pressed the left half of the button it would not behave as expected and forfeit the round. More on that later.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-problem.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;The problem happens when we press the left side of the こいこい button&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-plan&quot;&gt;The Plan&lt;/h2&gt;

&lt;p&gt;One of the beautiful things about Palm OS is that apps and their resources are viewable, and even editable, right there on the device itself! It’s a lot like Classic Macintosh in that regard, which is no surprise as Palm took a lot of inspiration from the original Mac.&lt;/p&gt;

&lt;p&gt;And much like ResEdit on Macintosh, Palm had it’s own equivalent app in &lt;a href=&quot;https://palmdb.net/app/rsrcedit&quot;&gt;RsrcEdit&lt;/a&gt; by Quartus, though I preferred to use an app called &lt;a href=&quot;https://palmdb.net/app/bird&quot;&gt;Bird&lt;/a&gt; by Philippe Guillot. You can view strings, bitmaps, menu bars, and other user interface elements (organised as Forms).&lt;/p&gt;

&lt;h2 id=&quot;lets-do-this&quot;&gt;Let’s do this&lt;/h2&gt;

&lt;p&gt;So I launched Bird and loaded up the contents of Tenohira Hanafuda Kai, and went through all the forms until I found the one that displays the continue prompt.&lt;/p&gt;

&lt;p&gt;Interestingly the form is dual-purpose. It contains the continue/stop buttons (a List of two items) and also a single button (了解; confirm) used on a different prompt. It’s overlaid on the continue button in a close enough position to be suspect. Perhaps it’s moved slightly at run time?&lt;/p&gt;

&lt;p&gt;We can easily change the order of the controls on the form by cutting and pasting, so we do that with the List and it now comes below the button in the order and will be drawn last on the screen. Presumably the things drawn last are the first to capture interactions? Let’s see.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-bird.gif#pixel&quot; alt=&quot;GIF&quot; title=&quot;https://youtube.com/shorts/yRLfHoHkjTY&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

&lt;p&gt;I apply all changes and they are saved directly into the app data. Launching the game again and playing enough to trigger another continue/stop prompt, I tap the left half of the button and… the game continues as expected. There is no strange forfeit. The problem has been solved!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hana-fixed.gif#pixel&quot; alt=&quot;GIF&quot; title=&quot;https://youtube.com/shorts/3faHHcuSQv4&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;addendum&quot;&gt;Addendum&lt;/h2&gt;

&lt;p&gt;I figured this out back in 2019 and edited the app on my Sony CLIÉ device. Recently I’ve been using &lt;a href=&quot;https://cloudpilot-emu.github.io&quot;&gt;CloudPilot&lt;/a&gt; to run Palm OS apps and games on my iPhone. So to play Tenohira Hanafuda Kai I had to either find the old modified game file, or do it all over again from scratch. I chose to do it again to test my memory and so I could document the process.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 07 Oct 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/10/07/fixing-bugs-using-bird-on-palm-os/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/10/07/fixing-bugs-using-bird-on-palm-os/</guid>
        </item>
      
    
      
        <item>
          <title>Accessing Yahoo! Japan from Europe</title>
          <description>&lt;p&gt;Since 6th April 2022, &lt;a href=&quot;https://www.theverge.com/2022/2/1/22911965/yahoo-japan-europe-offline-regulations-compliance-gdpr&quot;&gt;Yahoo! Japan has blocked access to the majority of their services for anybody located in the EEA&lt;/a&gt; (European Economic Area). That means the following countries: Austria, Belgium, Bulgaria, Croatia, Republic of Cyprus, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Iceland, Ireland, Italy, Latvia, Liechtenstein, Lithuania, Luxembourg, Malta, Netherlands, Norway, Poland, Portugal, Romania, Slovakia, Slovenia, Spain and Sweden.&lt;/p&gt;

&lt;p&gt;I can’t say I blame Yahoo! Japan for doing this, as the demands of GDPR, etc. are ill-considered and almost impossible for even the biggest companies to put into practice. But, regardless, here we are.&lt;/p&gt;

&lt;p&gt;My interest lies mostly in Yahoo! Japan Auctions, but the methods of access that I outline will work for any of their services. I’ll provide links where I can, and do feel free to reach out to me on Twitter, but sadly I’m unable to offer unpaid one-to-one help.&lt;/p&gt;

&lt;p&gt;With that said let’s find some workarounds!&lt;/p&gt;

&lt;h2 id=&quot;proxy-or-vpn&quot;&gt;Proxy or VPN?&lt;/h2&gt;

&lt;p&gt;We can use a couple of service to place the source of our access to Yahoo! Japan outside of the EEA blocked area, and any checks as to our physical location will get the physical location of the service instead. Neat!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Proxy: is a middleman service that relays your accesses to/from websites, usually at an application level. It is often not secure and encrypted.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;VPN: this Virtual Private Network encompasses all access to/from your computer,at a system level. It is generally secure and encrypted.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information about the difference between a proxy and VPN &lt;a href=&quot;https://nordvpn.com/blog/vpn-vs-proxy/&quot;&gt;read this page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we know what tools we’re going to use, let’s talk about specific uses.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-browsers&quot;&gt;1. Browsers&lt;/h2&gt;

&lt;p&gt;Certain browsers offer built in proxy/VPN, at zero cost. These will allow you to access Yahoo! Japan pretty much as you did before. But read the notes below!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://addons.opera.com/en-gb/extensions/details/opera-vpn/&quot;&gt;Opera browser&lt;/a&gt; (desktop only)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://alohabrowser.com&quot;&gt;Aloha browser&lt;/a&gt; (mobile only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;whilst using a proxy all your traffic goes through it&lt;/li&gt;
  &lt;li&gt;only use proxy to access selected websites, not all sites&lt;/li&gt;
  &lt;li&gt;avoid proxy/VPN apps with free trials and expensive monthly subscriptions thereafter&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-standalone-vpns&quot;&gt;2. Standalone VPNs&lt;/h2&gt;

&lt;p&gt;You can use a stand-alone VPN that is installed at a system level. This will affect all apps running on your system. Such VPNs are provided by the likes of PIA, ExpressVPN, SurfShark, NordVPN. Whilst I’ll refrain from recommending any one in particular, I will say that if you hunt around you can almost always find special offer pricing.&lt;/p&gt;

&lt;p&gt;Personally I use PIA, so &lt;a href=&quot;https://www.privateinternetaccess.com/pages/buy-a-vpn/1218buyavpn?invite=U2FsdGVkX18DlbIFebZBqvJIfoIVUNXHTGwHDesd0ksu2LJWgn2ljTzF91SYnAql%2C-6K1dJvsW9WQAp6IM1xnkkTq6sM&quot;&gt;here’s a referral to save some money&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The above solutions are all you need, but if you’re looking for more solutions then read on.&lt;/p&gt;

&lt;h2 id=&quot;3-proxy&quot;&gt;3. Proxy&lt;/h2&gt;

&lt;p&gt;You don’t really want to route all of your traffic through a proxy, especially if it’s a free proxy that you shouldn’t trust. It’s recommended to enable the proxy only for *.yahoo.co.jp URLs, which is possible using a &lt;a href=&quot;https://en.wikipedia.org/wiki/Proxy_auto-config&quot;&gt;Proxy Auto-Config&lt;/a&gt; (PAC file), an example of which is below.&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/d02b61f86eadd29a86b3ae42082eee7f&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/d02b61f86eadd29a86b3ae42082eee7f.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;&lt;a href=&quot;https://thorsen.pm/proxyforurl&quot;&gt;This handy website can be used to create and test your PAC file&lt;/a&gt;, then you should host the PAC file somewhere publicly accessible on the internet (so no passwords or personal information should be in it!), and finally enter the proxy URL into your system or browser network settings. This can all be done on both iOS, macOS and likely other platforms.&lt;/p&gt;

&lt;h2 id=&quot;which-proxy&quot;&gt;Which Proxy?&lt;/h2&gt;

&lt;p&gt;There are many free proxies listed at &lt;a href=&quot;https://www.freeproxy.world&quot;&gt;freeproxy.world&lt;/a&gt; with the caveat that you may have to hunt to find one that works, find new ones when things stop working, and not trust them with any personal data.&lt;/p&gt;

&lt;p&gt;It’s possible to host your own! You could do this on your home network using a Synology NAS, Raspberry Pi or some other computer. Or you can do it on a wider scale by hosting your proxy in the cloud. This is an advanced level solution, but is currently my preferred method. I have a proxy installed on a cloud compute instance located outside of the EEA. More specifically, I am using an install of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tinyproxy&lt;/code&gt; on an &lt;em&gt;Oracle Cloud Instance&lt;/em&gt; located on a server in Switzerland. I then combine this with the PAC method on the network connection each of my devices.&lt;/p&gt;

&lt;h3 id=&quot;server-level-access&quot;&gt;Server-level access&lt;/h3&gt;

&lt;p&gt;I run a server that does automated searches of Yahoo! Japan Auctions, giving me a sort of “Saved Search” mechanism for my own hobbies and interests. I now route all accesses to Yahoo! Japan websites through my Swiss proxy by specifying its proxy URL in my server configuration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-redirection&quot;&gt;4. Redirection&lt;/h2&gt;

&lt;p&gt;My main browser on macOS is Safari and regardless of the previous workarounds I still visit Yahoo! Japan Auctions pages during general browsing and image searching. This results in the dreaded error page.&lt;/p&gt;

&lt;p&gt;To workaround this I have configured the &lt;a href=&quot;https://apps.apple.com/gb/app/pageextender-for-safari/id1457557274?mt=12&quot;&gt;PageExtender extension&lt;/a&gt; (maOS) and the &lt;a href=&quot;https://apps.apple.com/sg/app/makeover-custom-css/id1602361167&quot;&gt;Makeover extension&lt;/a&gt; (iOS) to apply custom CSS and JS to Yahoo! Japan Auctions URLs.&lt;/p&gt;

&lt;p&gt;I do the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CSS
    &lt;ul&gt;
      &lt;li&gt;hide everything on the page (so I don’t see the ugly error)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;JS
    &lt;ul&gt;
      &lt;li&gt;redirect the page to an alternative website so I can see details of the auction item (such as pages provided by one of the many “buy from japan” websites)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/789bcd8786fb76578fc11c491bd5b97b&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/789bcd8786fb76578fc11c491bd5b97b.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;This means I can keep browsing uninterrupted, though of course you can no longer see the original pages as intended.&lt;/p&gt;

&lt;p&gt;Here is &lt;a href=&quot;https://github.com/gingerbeardman/dot-css-js/&quot;&gt;my archive of CSS and JS for modifying other sites&lt;/a&gt; in a similar way.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 07 Apr 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/04/07/accessing-yahoo-japan-from-europe/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/04/07/accessing-yahoo-japan-from-europe/</guid>
        </item>
      
    
      
        <item>
          <title>Working with classic Macintosh text encodings in the age of Unicode</title>
          <description>&lt;p&gt;One of my “lockdown projects” is a collection of media from vintage Japanese Apple Macintosh magazines. These are mostly CD-ROMs but there are some floppy disks too. I started the project in July 2021 and have so far collected an archive of over 250 items spanning just over a decade, uncovering many long lost classic pieces of software in the process. I call the project &lt;a href=&quot;/2021/10/30/macintosh-magazine-media/&quot;&gt;Macintosh Magazine Media&lt;/a&gt; and contributions are always welcome.&lt;/p&gt;

&lt;h2 id=&quot;the-relentless-march-of-progress&quot;&gt;The Relentless March of Progress&lt;/h2&gt;

&lt;p&gt;Accessing vintage Macintosh media from the “classic” era is more difficult than it should be. This is largely due to Apple removing support for accessing the HFS Standard format in modern macOS, since Catalina. My guess is that the feature comprised of 32-bit code and the move to 64-bit and Apple Silicon meant it would have to be rewritten, so instead they removed it completely. You can still access HFS Standard disks in Mojave, but there are problems when exotic text encodings are used.&lt;/p&gt;

&lt;h2 id=&quot;before-unicode&quot;&gt;Before Unicode&lt;/h2&gt;

&lt;p&gt;Classic Macintosh was created before the world moved to Unicode. But of course many languages existed and people speaking those languages wanted to use Macintosh computers. So Apple were forced to provide support for those languages. They did so by offering their system software in multiple languages. If you think of how embedded Unicode is in our software today, it was the same sort of thing: only repeated for each individual language!&lt;/p&gt;

&lt;p&gt;For European, Western, or Latin-based languages you’d be forgiven for not noticing the differences as most of the characters are the same. The problem comes with non-Latin languages, like Japanese.&lt;/p&gt;

&lt;h2 id=&quot;opening-pandoras-box&quot;&gt;Opening Pandora’s Box&lt;/h2&gt;

&lt;p&gt;I figured out quickly that the correct way of viewing the contents of media containing Japanese files was to use a Japanese version of Macintosh system software. It sounds obvious in hindsight, but it was not at the time! Seeing as I prefer System 7 to later versions I installed System J-7.5.3 in the BasiliskII emulator, alongside my existing systems so I can switch to it on demand. Emulation makes the whole thing so much easier by removing the friction of old, slow, possibly failing hardware.&lt;/p&gt;

&lt;p&gt;There are many apps capable of cataloguing removable media, but it took me a long time to find one that could cope with Japanese. I learned an important lesson here: if you’re dealing with Japanese look for apps made in Japan! More on that later. &lt;a href=&quot;https://macintoshgarden.org/apps/diskcatalogmaker&quot;&gt;DiskCatalogMaker&lt;/a&gt; (formerly DiskChoboMaker) was the cataloguing app that I settled on that worked for my needs. In fact, it’s still being updated today and can even import files created with much older versions, so I can copy my database from classic Mac OS to modern macOS and “it just works”, at least it does if you do that with modern macOS set to Japanese locale. That said, as good as DiskCatalogMaker is it still has problems with some filenames resulting in missing or duplicate entries, it uses a proprietary database format, it has cumbersome way of exporting plain text listings, and does not support bulk operations. I did go so far as scripting an automated bulk export solution using &lt;a href=&quot;https://www.keyboardmaestro.com&quot;&gt;Keyboard Maestro&lt;/a&gt; but that was slow and tedious to do whenever there were changes or additions to my collection.&lt;/p&gt;

&lt;h2 id=&quot;diy&quot;&gt;DIY&lt;/h2&gt;

&lt;p&gt;With no better solutions to be found the only remaining choice was to do it myself. This decision was made in October 2021.&lt;/p&gt;

&lt;p&gt;I found two apps that can be used on modern operating systems to view HFS format media, both of which seemed like good places to start. I didn’t want to reinvent the wheel:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/unsound/hfsexplorer&quot;&gt;HFSExplorer&lt;/a&gt; - a Java GUI app&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mars.org/home/rob/proj/hfs/&quot;&gt;hfsutils&lt;/a&gt; - a command-line suite of tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;hfsexplorer&quot;&gt;HFSExplorer&lt;/h3&gt;

&lt;p&gt;This app opened a lot of my HFS media, but failed on others for reasons I didn’t immediately understand. &lt;a href=&quot;https://github.com/unsound/hfsexplorer/issues/15&quot;&gt;I field an issue on GitHub&lt;/a&gt; and to my surprise it was quickly resolved. This led to the discovery that the filenames on the media were in MacJapanese text encoding, so that capability was also added to HFSExplorer. Things went well for a while until certain other media failed to be read completely. Characters in certain filenames were out-of-range for MacJapanese. A &lt;a href=&quot;https://github.com/unsound/hfsexplorer/issues/26&quot;&gt;workaround&lt;/a&gt; was to read the filenames as MacJapanese and drop down to MacRoman for any filenames with out-of-range characters. This worked well enough.&lt;/p&gt;

&lt;p&gt;At this point my attention moved on to wanting to search the contents of all media. It was possible with DiskCatalogMaker but I was limited to using apps on classic Macintosh or modern macOS. Ideally I’d want the search to be web based. So I needed to generate text file listings of each disk. This was the end of the line for HFSExplorer for me, as I found no easy way of exporting full listings.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h3 id=&quot;aside-out-of-range-characters&quot;&gt;Aside: out-of-range characters&lt;/h3&gt;

  &lt;p&gt;You might be wondering: how can there be out of range characters in text of a specified encoding, and what the hell are they? Well, there are a few scenarios that cause these problem characters to appear in filenames:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;Files originating on other systems that are encoded as MacRoman, Shift-JIS or some other encoding can be copied onto a computer running MacJapanese, but the filenames are not re-encoded.&lt;/li&gt;
    &lt;li&gt;Pressing forward delete key on an extended keyboard whilst renaming a file inserts an invisible DEL control character into the filename, rather than doing any actual deleting!&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;hfsutils&quot;&gt;hfsutils&lt;/h3&gt;

&lt;p&gt;Next, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hfsutils&lt;/code&gt;. It’s trivial to export the contents of a disk image as a text file—using the command line tool &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hls&lt;/code&gt;—but I couldn’t make sense of the contents. It didn’t seem to adhere to any one encoding. I had no luck with the best text editors on classic Mac OS: BBEdit, Nisus, Tex-Edit Plus, even Japanese apps like LightWayText couldn’t deal with the text. The same can be said for a bespoke text conversation app called Cyclone Classic, but it hit the same problem as HFSExplorer when it encountered out-of-range characters.&lt;/p&gt;

&lt;p&gt;The modern tool &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iconv&lt;/code&gt; couldn’t deal with the listings as it has no support for MacJapanese. I could get by processing as Shift-JIS and forcing unsupported characters to be ignored. But it wasn’t a good enough solution: MacJapanese is not Shift-JIS.&lt;/p&gt;

&lt;p&gt;Thinking back to using Japanese apps to work with Japanese text I looked for any modern Japanese text editors for macOS. There are a handful and I eventually stumbled across a modern Japanese text editor called &lt;a href=&quot;https://coteditor.com&quot;&gt;CotEditor&lt;/a&gt; which handles old Macintosh text files with aplomb. This app is now my default text file viewer and it comes highly recommended. I can’t go so far as to use it as my work editor because it doesn’t support opening folders or projects containing  multiple files. It still has problems with my directory listings but at least it’s a modern way to view the majority of Japanese text files.&lt;/p&gt;

&lt;p&gt;At this point, I was at an impasse and couldn’t think of any way to proceed. Eventually, after ruminating in the problem for some months I had a couple of breakthroughs.&lt;/p&gt;

&lt;h3 id=&quot;scummvm&quot;&gt;ScummVM&lt;/h3&gt;

&lt;p&gt;What on earth does a point-and-click video game engine have to do with text encoding? Well it turns out that in July 2021, at the same time I was trying to solve this problem, the ScummVM team were also trying to solve it! They needed a tool to be able to handle Japanese media that contained games the wanted to run on their engine. Their solution is &lt;a href=&quot;https://github.com/einstein95/scummvm/blob/master/devtools/dumper-companion.py&quot;&gt;dumper-companion&lt;/a&gt; and once it had &lt;a href=&quot;https://github.com/scummvm/scummvm/pull/3485&quot;&gt;support for MacJapanese added&lt;/a&gt;,in the same way it had been for HFSExplorer, it was a reasonable solution. But it was far too slow, reading the whole disk image into memory at once—no mean feat for a bunch of 650MB CD-ROM images—and it also had the same problem with the out-of-range characters.&lt;/p&gt;

&lt;h3 id=&quot;tickle&quot;&gt;Tickle&lt;/h3&gt;

&lt;p&gt;From time to time I would search for possible ways to deal with MacJapanese encoding. One day in November 2021 I stumbled upon Tcl (pronounced “tickle”) which has support for a whole bunch of text encodings, including MacJapanese! What’s more &lt;a href=&quot;https://opensource.apple.com/source/tcl/tcl-10/tcl/tools/encoding/macJapan.txt&quot;&gt;the encoding maps were written by Apple&lt;/a&gt; in the mid-‘90s, so it’s likely to be as correct as can be. Note: Peter Edberg, who wrote the Tcl mappings, is still working at Apple after almost 35 years!&lt;/p&gt;

&lt;p&gt;The Tcl solution to convert from MacJapanese to Unicode is a beautiful one-liner:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;puts out.txt [encoding convertfrom macJapan [read in.txt]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…so much effort to arrive at this simple solution!&lt;/p&gt;

&lt;h2 id=&quot;convert2unicode&quot;&gt;convert2unicode&lt;/h2&gt;

&lt;p&gt;From here I wrapped the Tcl one-liner in a bunch more script so that it can handle both files and directories, as well as wildcards and stdin. It can also list all known encodings, and can take an argument representing the source encoding (of course it defaults to MacJapanese). Essentially, I made the one-liner into a proper command-line tool.&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/4a3b66236e018b72b32ca17953474e12&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/4a3b66236e018b72b32ca17953474e12.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;I have a secondary &lt;a href=&quot;https://gist.github.com/gingerbeardman/892e2c92b6fe17838a1443608c111a56&quot;&gt;shell script&lt;/a&gt; that runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;convert2unicode&lt;/code&gt; against my drive full of disk images, along with some housekeeping and maintenance functions. The whole process of listing the disks and converting the resulting text files takes less than 30 seconds for 250 items.&lt;/p&gt;

&lt;h3 id=&quot;let-there-be-search&quot;&gt;Let there be search&lt;/h3&gt;

&lt;p&gt;So, at this point I can generate text files with the contents of each disk, but to get sensible search results each filename would have to have its full path. So I rolled my sleeves up and &lt;a href=&quot;https://github.com/gingerbeardman/hfsutils&quot;&gt;forked hfsutils to add a “full” output mode&lt;/a&gt; to display the filenames in exactly  the way I needed. My C skills were really rusty so this work was quite a challenge.&lt;/p&gt;

&lt;p&gt;Finally, I created a fairly naïve &lt;a href=&quot;https://www.gingerbeardman.com/mmm/&quot;&gt;web-based search engine&lt;/a&gt; that can search through hundreds of files, totalling almost half a million lines of text, in a fraction of a second.&lt;/p&gt;

&lt;h2 id=&quot;future&quot;&gt;Future&lt;/h2&gt;

&lt;p&gt;I’d like to offer the ability for individual files to be extracted from a disk image so they can be downloaded by interested parties. This would be similar to the way Internet Archive allows individual files to be downloaded from inside ISO disk images. However, this involves further challenges with text encoding and I would also have to address potential bandwidth concerns.&lt;/p&gt;

&lt;p&gt;Whilst the Tcl solution is great, it is not quite perfect. Currently the behaviour of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;encoding convertfrom&lt;/code&gt; command silently ignores problem characters. &lt;a href=&quot;https://core.tcl-lang.org/tcl/info/535705ffffffffff&quot;&gt;Future versions of Tcl will have the option of displaying errors&lt;/a&gt;. I’ll keep an eye on that progress and upgrade my scripts when the time comes.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 31 Mar 2022 12:34:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/03/31/working-with-classic-macintosh-text-encodings-in-the-age-of-unicode/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/03/31/working-with-classic-macintosh-text-encodings-in-the-age-of-unicode/</guid>
        </item>
      
    
      
        <item>
          <title>Using RSS to create saved searches for any website</title>
          <description>&lt;p&gt;I’ve recently started using RSS again, with a couple of goals in mind.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;to read the latest interesting blog posts in a more focussed way&lt;/li&gt;
  &lt;li&gt;to assist with personal research, and that’s what this post is about&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;internet-archive&quot;&gt;Internet Archive&lt;/h1&gt;

&lt;p&gt;You can set up an RSS feed for any search using their &lt;a href=&quot;https://archive.org/advancedsearch.php&quot;&gt;Advanced Search&lt;/a&gt; page:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;scroll half way down to “Advanced Search returning JSON, XML, and more”&lt;/li&gt;
  &lt;li&gt;enter your Query (default sort is newest first)&lt;/li&gt;
  &lt;li&gt;choose: RSS format&lt;/li&gt;
  &lt;li&gt;press Search&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should be prompted to add the feed to your default RSS reader.&lt;/p&gt;

&lt;p&gt;I use this to track searches for items that match my hobby interests:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Hanafuda&lt;/li&gt;
  &lt;li&gt;Classic Macintosh software&lt;/li&gt;
  &lt;li&gt;Japanese magazine scans&lt;/li&gt;
  &lt;li&gt;Authors&lt;/li&gt;
  &lt;li&gt;Publishers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every time a new item appears in my RSS reader it feels like Christmas!&lt;/p&gt;

&lt;p&gt;Thanks to the generosity of the Internet Archive there is no limit to the number of RSS feeds you can generate.&lt;/p&gt;

&lt;h1 id=&quot;yahoo-japan-auctions&quot;&gt;Yahoo! Japan Auctions&lt;/h1&gt;

&lt;p&gt;Sadly, Yahoo! no longer provides RSS feeds for their Auctions website.&lt;/p&gt;

&lt;p&gt;But where there is a will, and technology, there is a way. We can use a website called &lt;a href=&quot;https://politepol.com/en/&quot;&gt;PolitePol&lt;/a&gt; to generate RSS feeds! It works on any website, especially those with generated/structured content. A search results page is perfect.&lt;/p&gt;

&lt;p&gt;PolitePol offers a free account with certain limits. At the time of writing those are: max 20 feeds, no RSS images (more on that later), and no support for websites that require scripting to load their content (so I can’t do this with Mercari), and hourly refresh. You can pay to unlock those restrictions, but I’ve not yet found a need to do so. PolitePol is also Open Source so you can host it yourself - as easy as launching a Docker container.&lt;/p&gt;

&lt;h2 id=&quot;example-usage&quot;&gt;Example usage&lt;/h2&gt;

&lt;p&gt;The first thing we need to do is craft a Yahoo! Japan Auctions URL that makes sense so that we can paste it into PolitePol.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I’ll search for ペブルビーチ (Pebble Beach)&lt;/li&gt;
  &lt;li&gt;The default sort order is おすすめ順 (Recommended) but we need to set it to 新着順 (Newest)&lt;/li&gt;
  &lt;li&gt;We’ll limit the search to specific categories: おもちゃ、ゲーム (Toys &amp;amp; Games) followed by ゲーム (Games)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives us the following URL (which could be trimmed down manually but we’ll use it as-is):&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://auctions.yahoo.co.jp/search/search?va=ペブルビーチ&amp;amp;exflg=1&amp;amp;b=1&amp;amp;n=50&amp;amp;s1=new&amp;amp;o1=d&amp;amp;auccat=27727&amp;amp;tab_ex=commerce&amp;amp;ei=utf-8&amp;amp;aq=-1&amp;amp;oq=&amp;amp;sc_i=&amp;amp;exflg=1&amp;amp;p=ペブルビーチ&quot;&gt;auctions.yahoo.co.jp/search/search?va=ペブルビーチ&amp;amp;exflg=1&amp;amp;b=1&amp;amp;n=50&amp;amp;s1=new&amp;amp;o1=d&amp;amp;auccat=27727&amp;amp;tab_ex=commerce&amp;amp;ei=utf-8&amp;amp;aq=-1&amp;amp;oq=&amp;amp;sc_i=&amp;amp;exflg=1&amp;amp;p=ペブルビーチ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, in PolitePol:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;paste in the URL in and click Go!&lt;/li&gt;
  &lt;li&gt;click the Title button and select the first of any elements you’d like to use as the title (I used the auction name)&lt;/li&gt;
  &lt;li&gt;click Description button and select the first of any elements you’d like to use as the description (I used the “table cell”)&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;e&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;f&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;g&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-3.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-4.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-4.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-5.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-5.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-6.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-6.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-7.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-7.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 100%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}

.carousel__activator:nth-of-type(5):checked ~ .carousel__track {
  -webkit-transform: translateX(-400%);
          transform: translateX(-400%);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__slide:nth-of-type(5) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__controls:nth-of-type(5) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(5) {
  opacity: 1;
}

.carousel__activator:nth-of-type(6):checked ~ .carousel__track {
  -webkit-transform: translateX(-500%);
          transform: translateX(-500%);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__slide:nth-of-type(6) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__controls:nth-of-type(6) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(6) {
  opacity: 1;
}

.carousel__activator:nth-of-type(7):checked ~ .carousel__track {
  -webkit-transform: translateX(-600%);
          transform: translateX(-600%);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__slide:nth-of-type(7) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__controls:nth-of-type(7) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(7) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(5) {
  -webkit-transform: translateX(400%) translateZ(0);
          transform: translateX(400%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(6) {
  -webkit-transform: translateX(500%) translateZ(0);
          transform: translateX(500%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(7) {
  -webkit-transform: translateX(600%) translateZ(0);
          transform: translateX(600%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(5),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(5) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(6),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(6) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(7),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(7) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;h3 id=&quot;but-i-thought-you-said-no-images-were-allowed&quot;&gt;But I thought you said no images were allowed?&lt;/h3&gt;

&lt;p&gt;As previously mentioned a PolitePol free account does not allow selection of images for use in the RSS feed. This is a bit misleading. What it really means: you can not choose an image element to use as the thumbnail/enclosure for each item in the feed.&lt;/p&gt;

&lt;p&gt;Whilst it would be nice to have thumbnails in our list of articles, it’s not essential.&lt;/p&gt;

&lt;p&gt;Also, there’s a nice workaround: by selecting the whole table cell as the description we get all of the HTML it contains: its image, title, price, number of bids, remaining time. So we get images in the description anyway. That’ll do nicely!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s important to note that the listing details are not live; they are a snapshot taken at the time the feed was refreshed (up to an hour ago with a PolitePol free account).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s the final result in my RSS reader:&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/rss-saved-searches-8.png&quot; alt=&quot;PNG&quot; title=&quot;Saved Search: a generated RSS feed for a Yahoo! Japan Auctions search&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;its-up-to-you&quot;&gt;It’s up to you&lt;/h1&gt;

&lt;p&gt;You can use this technique to implement saved searches from any website. Just be sure to check the site doesn’t already have RSS feeds before you begin!&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;huginn&quot;&gt;Huginn&lt;/h1&gt;

&lt;p&gt;Since shortly after this blog post was published I’ve been using &lt;a href=&quot;https://github.com/huginn/huginn&quot;&gt;Huginn&lt;/a&gt; as a replacement for Politepol. There’s no real GUI so you have to scrape using CSS selectors or XPath, but it’s quite a lot more powerful. I’d say it’s a good advanced solution if you run into the limits of Politepol. Here’s &lt;a href=&quot;https://gist.github.com/gingerbeardman/e4b07db8d59dec441bc9ada1972789c4&quot;&gt;an example Huginn Website Agent&lt;/a&gt; that scrapes Yahoo! Japan Auctions search results.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 30 Jan 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/01/30/using-rss-to-create-saved-searches-for-any-website/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/01/30/using-rss-to-create-saved-searches-for-any-website/</guid>
        </item>
      
    
      
        <item>
          <title>Using a proxy to speed up access to specific websites</title>
          <description>&lt;p&gt;I use &lt;a href=&quot;https://world.hoyoyo.com/member~register~code~IZhM8F&quot;&gt;world.hoyoyo.com&lt;/a&gt; quite a lot. It’s a forwarding service for buying stuff from Japan. However, I found that their website took around a minute to load a page, which became hugely annoying very quickly.&lt;/p&gt;

&lt;p&gt;HOYOYO are great as they will negotiate discount prices from Mercari sellers, which has saved me a ton of money, so I wanted to stick with them.&lt;/p&gt;

&lt;p&gt;Over time I’d resorted to accessing their website through a trusted VPN in either &lt;a href=&quot;https://www.opera.com&quot;&gt;Opera browser&lt;/a&gt; (macOS) or &lt;a href=&quot;https://readdle.com/documents&quot;&gt;Documents app&lt;/a&gt; (iOS) which both worked very well. But launching a new browser for a specific website always bothered me.&lt;/p&gt;

&lt;h1 id=&quot;its-good-to-talk&quot;&gt;It’s good to talk&lt;/h1&gt;

&lt;p&gt;I was chatting with my brother about it and he said that he was seeing reasonably quick load times. After a little investigation I found that, for reasons I don’t understand, certain websites are very slow to load through my ISP (British Telecom). At this point, it was obvious that using a VPN in a specific browser was overkill for solving the problem.&lt;/p&gt;

&lt;h1 id=&quot;proxy-to-the-rescue&quot;&gt;Proxy to the rescue&lt;/h1&gt;

&lt;p&gt;So I tried using a web proxy and the load times dropped drastically, to a handful of seconds, which is a lot more reasonable! Not quite as quick as using the specific browsers, but good enough for me.&lt;/p&gt;

&lt;p&gt;Of course, I don’t want all traffic to go through the proxy. So I set up a PAC (Proxy Auto-Configuration) file to route only requests to HOYOYO through the proxy and leave everything else untouched.&lt;/p&gt;

&lt;p&gt;In terms of which proxy to use, you’ll have to hit Google or set up your own on an external server.&lt;/p&gt;

&lt;h1 id=&quot;pac-man&quot;&gt;PAC, man&lt;/h1&gt;

&lt;p&gt;Here’s what my final PAC file looks a bit like:&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/dd6691c2706a3d27a485a7a7dc3e5d60&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/dd6691c2706a3d27a485a7a7dc3e5d60.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Note: you’ll need to substitute a valid proxy IP address and port number, and host the file on a web server you can access from your device.&lt;/p&gt;

&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;For iOS, you enter the URL of the file in the Proxy setting of your current Wi-Fi connection. A correct setting will be shown as “Automatic”.&lt;/li&gt;
  &lt;li&gt;macOS: enter it in System Preferences &amp;gt; Network &amp;gt; Advanced &amp;gt; Proxies, as below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/proxy.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 29 Jan 2022 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2022/01/29/using-a-proxy-to-speed-up-access-to-specific-websites/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2022/01/29/using-a-proxy-to-speed-up-access-to-specific-websites/</guid>
        </item>
      
    
      
        <item>
          <title>Playing old 32-bit iOS games in 2021</title>
          <description>&lt;p&gt;With the introduction of iOS 11 in 2017 Apple stopped supporting 32-bit apps on iOS. This event came to be known as the app-ocalypse with users forced to stay on iOS 10 to keep their apps or upgrade and abandon them. I seem to remember I stuck around on iOS 10 for a while but eventually succumbed to the upgrade and said good by to a bunch of stuff.&lt;/p&gt;

&lt;p&gt;At this time, I was still managing my iPhone and app updates through iTunes, with a weird ritual of downloading the latest updates to my Mac. It was useful for keeping on top of what I had installed and deleting apps I was no longer interested in. Around the same time Llamasoft were disillusioned with the App Store and pulled all of their games.&lt;/p&gt;

&lt;p&gt;Anyway, I took one or both of those things as a sign to download and backup a couple of games in particular: Llamasoft’s &lt;a href=&quot;http://www.minotaurproject.co.uk/Minotaur/minotron.php&quot;&gt;Minotron: 2112&lt;/a&gt; and &lt;a href=&quot;http://www.minotaurproject.co.uk/Minotaur/gridrunner.php&quot;&gt;Gridrunner&lt;/a&gt;, part of their &lt;a href=&quot;http://www.minotaurproject.co.uk/Minotaur/minotaurprj2.php&quot;&gt;Minotaur Project&lt;/a&gt; series of games. I bought a few more from that series but I only kept my two favourites.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2021/03/08/two-old-llamasoft-iphone-and-ipad-games/&quot;&gt;Earlier this year&lt;/a&gt; I uploaded them to internet archive as an act of preservation. Somebody recently downloaded them and was trying to make them work on their devices, without much luck. It seemed that the apps were tied to my account and I’d have to share decrypted versions. I’d need an old device capable of running iOS 10 or older, and one susceptible to jailbreaking. I figured that would be a fun afternoon. Here’s the process I went through:&lt;/p&gt;

&lt;h2 id=&quot;restore-working-system&quot;&gt;Restore working system&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Bought a used &lt;a href=&quot;https://en.wikipedia.org/wiki/IOS_10#Supported_devices&quot;&gt;device that supports iOS 10&lt;/a&gt; (or earlier, if you prefer)&lt;/li&gt;
  &lt;li&gt;Downgraded my device to iOS 10.3.3 using &lt;a href=&quot;https://github.com/rA9stuff/LeetDown&quot;&gt;leetdown&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Installed &lt;a href=&quot;/2021/03/08/two-old-llamasoft-iphone-and-ipad-games/&quot;&gt;my two .ipa files&lt;/a&gt; using &lt;a href=&quot;https://support.apple.com/en-gb/apple-configurator&quot;&gt;Apple Configurator 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Confirmed that the games work by playing a little of each&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;jailbreaking-the-device&quot;&gt;Jailbreaking the device&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Installed &lt;a href=&quot;https://github.com/SongXiaoXi/sockH3lix/releases/latest&quot;&gt;sockH3lix&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Did jailbreak with sockH3lix (only takes a second or two!)&lt;/li&gt;
  &lt;li&gt;Noted that Cydia has been installed&lt;/li&gt;
  &lt;li&gt;Installed Clutch (took a couple of tries to find a &lt;a href=&quot;https://sharerepo.stkc.win/?repo=https://stek29.rocks/cyrepo/&quot;&gt;working repo&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;decrypt-the-minotron-game&quot;&gt;Decrypt the Minotron game&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenSSH&lt;/code&gt; via Cydia&lt;/li&gt;
  &lt;li&gt;Logged in over SSH from my Mac&lt;/li&gt;
  &lt;li&gt;Ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clutch -b uk.co.llamasoft.minotron&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;SFTP in from my Mac and copy the decrypted .ipa to my Mac&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;video&quot;&gt;Video&lt;/h2&gt;

&lt;p&gt;I could capture the attract loop using QuickTime Player but for some reason the recording crashed whenever a sound was played.&lt;/p&gt;

&lt;lite-youtube style=&quot;aspect-ratio: 4/3;&quot; videoid=&quot;RZSjR4dIykU&quot; params=&quot;start=0&amp;amp;modestbranding=2&quot;&gt;
&lt;/lite-youtube&gt;

&lt;p&gt;And &lt;a href=&quot;https://www.instagram.com/p/CTwautQAXUp/&quot;&gt;here’s an Instagram video of me playing the main game&lt;/a&gt;, albeit quite badly is I’m only using one hand!&lt;/p&gt;

&lt;h2 id=&quot;gridrunner&quot;&gt;Gridrunner&lt;/h2&gt;

&lt;p&gt;For some reason Clutch and other decrypting apps don’t work for me with Gridrunner. Since then, &lt;a href=&quot;https://archive.org/details/gridrunner-ios&quot;&gt;somebody was kind enough to create a decrypted version and upload it to Internet Archive&lt;/a&gt;.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 13 Sep 2021 23:59:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/09/13/playing-old-32-bit-ios-games-in-2021/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/09/13/playing-old-32-bit-ios-games-in-2021/</guid>
        </item>
      
    
      
        <item>
          <title>Changing the text size of a list using Resorcerer</title>
          <description>&lt;p&gt;I sometimes use an app called KeyQuencer Launcher that is part of the KeyQuencer software. The app presents a simple window containing a list of macros allowing quick and easy access.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/changing-textsize-1.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;KeyQuencer Launcher window&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The text in the window is very small, which makes sense as you want to see multiple macros in as little space as possible. However, my use case in System 7 on iPad is a little different as I’d like to see fewer, larger items that are easy to select by touch.&lt;/p&gt;

&lt;p&gt;Knowing a little about how Macintosh apps are put together I thought it should be easy enough to change the text size in the window by modifying the app’s binary code directly. This can be done using a tool called Resorcerer (you could also use Super ResEdit, or ResEdit with the CODE viewer resources installed).&lt;/p&gt;

&lt;p&gt;By searching for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xA88A&lt;/code&gt; (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_TextSize&lt;/code&gt; toolbox call) I was lucky enough to find a single place in the app where the text size is set. I changed the value passed to the preceding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;move.m&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0E&lt;/code&gt; (decimal 14) to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12&lt;/code&gt; (decimal 18) and the text size was increased.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/changing-textsize-2.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Toolbox Trap 0xA88A&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That was enough of a solution for me, though it is possible to change the font or to increase text size more dramatically if required.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/changing-textsize-3.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;KeyQuencer Launcher window with increased text size&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;related-posts&quot;&gt;Related posts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh&quot;&gt;Turning an iPad Pro into the Ultimate Classic Macintosh&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/21/building-basiliskii-for-ios/&quot;&gt;How to install BasiliskII on your iPad&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/19/automating-interactions-using-apple-events/&quot;&gt;Exploring Custom Keyboards and Automation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Optimising Macintosh app toolbars for touch&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/24/macintosh-drawing-software-compared/&quot;&gt;Macintosh drawing software compared&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/25/mixing-external-tools-across-deneba-software/&quot;&gt;Mixing External Tools across Deneba software&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/04/30/my-system-7-software-choices/&quot;&gt;My System 7 software choices&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/05/03/interoperability-of-system-7-and-ios/&quot;&gt;About the interoperability of System 7 and iOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 28 Apr 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/04/28/changing-the-text-size-of-a-list-using-resorcerer/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/04/28/changing-the-text-size-of-a-list-using-resorcerer/</guid>
        </item>
      
    
      
        <item>
          <title>Mixing External Tools across Deneba software</title>
          <description>&lt;p&gt;One of the most interesting things about Deneba’s early 1990s Macintosh drawing apps is that they were built in a modular way with additional files adding specific features. These days we call those Plugins but back in 1990 Deneba referred to them as External Tools.&lt;/p&gt;

&lt;p&gt;They are first seen in UltraPaint and later in Canvas 3.0, artWORKS and Canvas 3.5. Tools are categorised as several types: Effect, I/O, Manager, Modifier, Object, Setting and Other. Each adds functionality and/or user interface to the app in its own way.&lt;/p&gt;

&lt;p&gt;Amongst the Deneba material on an AppleLink archive disk from Spring 1993 there are a bunch of “non-shipping” tools labelled for use with Canvas 3.0. Interestingly, they are listed individually and also as a combined set. If we extract the combined set of tools there’s a single additional tool that is somewhat out of place: the Waco Tool (for Wacom pressure sensitive pen) which is marked with UltraPaint’s type and creator information.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/deneba-external-tools-0.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;Spot the odd one out&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I can confirm this that this tool - which I don’t believe was ever included with UltraPaint - allows my Wacom ArtPad II to work. Bonus! But why was this tool meant for UltraPaint included in a download for Canvas. Could it work in Canvas?&lt;/p&gt;

&lt;p&gt;I put the additional files in the Canvas Tools folder and started the app with the Space Bar held down. This starts the Tool Picker allowing you to select which tools you want to use for that session.&lt;/p&gt;

&lt;p&gt;Boom! There it was.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/deneba-external-tools-1.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;A wild Waco Tool appears in the Tool Picker&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;experimentation&quot;&gt;Experimentation&lt;/h2&gt;

&lt;p&gt;I tried copying some tools from Canvas to UltraPaint and artWORKS. They didn’t work. What was the difference? In ResEdit they looked largely the same. On a whim I looked at the type/creator codes using Snitch’s Get Info extension. I noticed that disabled tools had a different type code. If the app is using type to store information like that, maybe it’s only accepting certain type codes?&lt;/p&gt;

&lt;p&gt;So I copied a tool from Canvas over to artWORKS, being careful to pick one that already exists so it was as much like for like as it could be. It didn’t show up. Then I changed the type/creator codes and… it did show up! And not only that, but the tool worked in the app!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/deneba-external-tools-2.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;External Tool type/creator codes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was surprised, and also impressed. Deneba did a really great job with the architecture and API of these apps for things to remain this compatible over the course of several years. All three apps share code and resources. If you take a look at both UltraPaint and artWORKS in ResEdit you’ll see that they share a lot of DNA, artWORKS is basically an enhanced version of UltraPaint. I’d bet all three apps were built from the same code base.&lt;/p&gt;

&lt;h2 id=&quot;how-far-can-we-go&quot;&gt;How far can we go?&lt;/h2&gt;

&lt;p&gt;Some questions remained: how many tools could this be done with? Would they install and function? Could I use tools in artWORKS that were meant only for the more expensive Canvas? There was only one way to find out: try all the tools!&lt;/p&gt;

&lt;p&gt;I set about copying the tools from one app to artWORKS, my favourite app, changing the creator and type codes to match. To make bulk changes to type/creator I used Get Info (with Snitch) on multiple items, but I could have used software such as FileTyper.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Year&lt;/th&gt;
      &lt;th&gt;Software&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Creator&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1990&lt;/td&gt;
      &lt;td&gt;UltraPaint&lt;/td&gt;
      &lt;td&gt;TOOL&lt;/td&gt;
      &lt;td&gt;ULTR&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1992&lt;/td&gt;
      &lt;td&gt;Canvas 3.0&lt;/td&gt;
      &lt;td&gt;TOL2&lt;/td&gt;
      &lt;td&gt;DAD2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1993&lt;/td&gt;
      &lt;td&gt;artWORKS&lt;/td&gt;
      &lt;td&gt;TOL4&lt;/td&gt;
      &lt;td&gt;DAD3&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;very-far&quot;&gt;Very far!&lt;/h2&gt;

&lt;p&gt;The result was quite amazing with artWORKS total tools increasing from 59 to 92—that’s 33 additional tools! This is not counting a handful of tools that install but do not present themselves in the user interface. I’ll need to investigate those further at a later date.&lt;/p&gt;

&lt;p&gt;Doing the same for Canvas 3.0 takes us from 74 to an even higher total of 97 tools! It’s worth noting there are a couple of tools that go by the same internal name: PANTONE™ Colors (Color Manager) and Separations (PostScript) but I’ve chosen not to merge those rows of the table as there are some subtle differences in their capabilities. Also the functionality of the Grids &amp;amp; Rulers tool is built-in.&lt;/p&gt;

&lt;p&gt;The table below shows tools present in each of the apps, each with their own symbol, and then the final combined column shows all tools that are compatible with artWORKS with the symbols showing which app that tool originated from.&lt;/p&gt;

&lt;h1 id=&quot;external-tools&quot;&gt;External Tools&lt;/h1&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table id=&quot;deneba-external-tools&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Software Name&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;UltraPaint&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;Canvas 3.0&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;artWORKS&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;artWORKS+&lt;/th&gt;
        &lt;th style=&quot;text-align: center&quot;&gt;Canvas 3.0+&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt; Tool Loader&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt; ToolPicker&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Alignment Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Antialias&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Arrowhead Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Auto Trace&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Balloon Help&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Bézier Text&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Bind&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Blend Objects&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Blotter Pen&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Brush Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Canvas 1.0 I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Canvas 2.1 I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;CGM I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Chalk&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Charcoal&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Coil&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Color Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Combine Objects&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Concentric Circles&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Contrast&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Convert To&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Cube&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Cylinder&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Dash Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Dimensioning&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Doughnuts&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Drawing Size&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Dropper&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Duplication Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;DXF I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Filters&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fingertip&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fractals&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fuzz lasso/Soften Edges&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GIF I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Gradient Fill&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Gray Scale Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Grid Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Grid Tool/Maker&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Grids &amp;amp; Rulers&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Hatch Patterns&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;IGES I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Illustrator I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Imported Filters&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Impressionist&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Layer Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Luminance&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacDraw I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;MacPaint I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Magic Wand&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Masking&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Mover&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Multigon&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;NameStamp&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Object Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Object Tint&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;PANTONE™ Colors&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Parallel Lines&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Pattern Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Pen Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Place&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Point Rotate&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;PostScript&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Preferences&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Pressure Pen&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;QuickTime&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Quill Pen&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Registration Marks&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Resistor&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;RGB Color Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Rubber Stamp&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Ruler Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Scale Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Search &amp;amp; Replace&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Selection Specs&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Separations&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Sharpen and Blur&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Size Other&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Slides&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Smart Mouse&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Sounder&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spacing Other&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spelling&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spiral&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Split&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Spray Manager&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;StartupScreen I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;System 7.0 Options&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Text Ruler&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Text Utilities&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Textures&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Three Color Airbrush&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;TIFF I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;TimeStamp&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Tool Info&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;UltraPaint 1.0 I/O&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Undo Killer&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;○&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;●&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Water Droplet&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;▲&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;■&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;tfoot&gt;
      &lt;tr&gt;
        &lt;td&gt;Total Tools&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;30&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;74&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;58&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;92&lt;/td&gt;
        &lt;td style=&quot;text-align: center&quot;&gt;97&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tfoot&gt;
  &lt;/table&gt;

  &lt;p&gt;Key:&lt;/p&gt;

  &lt;p&gt;▲ UltraPaint tool
  ● Canvas 3.0 tool
  (○ that installs but doesn’t show any interface)
  ■ artWORKS tool&lt;/p&gt;

  &lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

  &lt;p&gt;So after all this I now have an install of artWORKS (let’s call it artWORKS+) that includes additional tools from both UltraPaint and Canvas 3.0 and I had a ton of fun figuring all this out. And I’m only 30 years late!&lt;/p&gt;

  &lt;p&gt;Here’s a download of my artWORKS Tools folder containing all the combined tools (use StuffIt! Expander 5.5 to extract): &lt;a href=&quot;/files/artWORKS-Tools-Combined.sit&quot;&gt;artWORKS-Tools-Combined.sit&lt;/a&gt; [1.1MB]&lt;/p&gt;

  &lt;h3 id=&quot;footnote&quot;&gt;Footnote&lt;/h3&gt;

  &lt;p&gt;A note about later versions of Canvas: version 3.5 provides roughly the same tools as version 3.0. The handful of unique tools in version 3.5 are not compatible with artWORKS. Version 5.0 features similar tools but they’re no longer compatible with the earlier apps.&lt;/p&gt;

  &lt;h3 id=&quot;related-posts&quot;&gt;Related posts&lt;/h3&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/17/turning-an-ipad-pro-into-the-ultimate-classic-macintosh&quot;&gt;Turning an iPad Pro into the Ultimate Classic Macintosh&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/21/building-basiliskii-for-ios/&quot;&gt;How to install BasiliskII on your iPad&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/19/automating-interactions-using-apple-events/&quot;&gt;Exploring Custom Keyboards and Automation&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/03/28/changing-the-size-of-toolbar-items-using-resedit/&quot;&gt;Optimising Macintosh app toolbars for touch&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/24/macintosh-drawing-software-compared/&quot;&gt;Macintosh drawing software compared&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Mixing External Tools across Deneba software&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/04/30/my-system-7-software-choices/&quot;&gt;My System 7 software choices&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/2021/05/03/interoperability-of-system-7-and-ios/&quot;&gt;About the interoperability of System 7 and iOS&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;

&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 25 Apr 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/04/25/mixing-external-tools-across-deneba-software/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/04/25/mixing-external-tools-across-deneba-software/</guid>
        </item>
      
    
      
        <item>
          <title>Macintosh Classic logic board recap</title>
          <description>&lt;ul&gt;
  &lt;li&gt;Difficulty: 7/10&lt;/li&gt;
  &lt;li&gt;Cost: &amp;lt; £20&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I last used my Macintosh Classic a few years ago. At that point everything worked OK. Switching it on this month I find there’s no sound and it won’t boot. This is par for the course with these 30-year-old machines. Time for a service!&lt;/p&gt;

&lt;p&gt;I decided quite early on that I would not rush any part of this, which turned out to be the right decision!&lt;/p&gt;

&lt;p&gt;The first thing to do is to check that the PRAM battery hasn’t leaked or worse, exploded, which would cover the logic board in acid and would be uneconomical to repair.&lt;/p&gt;

&lt;p&gt;Took me a while to find my long torx bit to open up the Macintosh case. But good news - no battery leakage!&lt;/p&gt;

&lt;p&gt;I removed the 30-year-old battery and bought a replacement.&lt;/p&gt;

&lt;p&gt;Surprisingly the replacement battery (not pictured) was already 5 years old, manufactured 2017. Not ideal, but it’ll do for now.&lt;/p&gt;

&lt;p&gt;Next, I figured I would make a quick system disk as I had figured out that I could boot it from floppy disk if I held down Cmd+Alt+Shift+Del, which is the first odd thing.&lt;/p&gt;

&lt;p&gt;Trying to reset PRAM on boot resulting in nothing happening, and there was no startup chime. Two more odd things.&lt;/p&gt;

&lt;p&gt;Somebody suggested maybe the keyboard wasn’t working. I booted the System floppy and tested all keys and they were fine.&lt;/p&gt;

&lt;p&gt;Next, I disconnected the hard drive to see if anything changed and I was able to boot using the system ROM (a feature only ever found on this specific model). This gets me into a bare-bones System 6 and from here there’s a hidden way to reset PRAM. I did that and there was no change to symptoms.&lt;/p&gt;

&lt;p&gt;Time to stop delaying the inevitable and take a look at the capacitors. Having never seen a leaking capacitor before I thought that everything looked fine from a cursory glance at the PCB. But with some guidance from &lt;a href=&quot;https://68kmla.org/forums/&quot;&gt;68KMLA forum&lt;/a&gt;, and a bit more time, I could see that there was a thin film of almost transparent dried residue surrounding almost all the caps, and some corrosion at the caps near the main power connector. Recap is needed!&lt;/p&gt;

&lt;p&gt;Removing the capacitors is the big challenge. I don’t have any dedicated soldering stations or heat flow gear, just a portable soldering iron, a pencil soldering iron and a twinkle in my eye.&lt;/p&gt;

&lt;p&gt;I spent a few days looking at YouTube videos and asking for opinions on methods of removing capacitors. Under advice, I found a practice board in a dead AV AMP that had a gazillion capacitors and I used that to practice. In the end I decided I would use the “twist and go” method (that’s what I’m calling it!) which involved using long nose pliers to grab the capacitor from above, and whilst pushing towards the board gently rotating the capacitor back and forth around its centre point. On the practice board some caps were more difficult that others, but I never lifted a pad in some 20 or so capacitors.&lt;/p&gt;

&lt;p&gt;I’ve no doubt you could lift a pad if you used a different technique or you went about it recklessly, but I found this seemingly brutal method worked very well. In years gone by I would have rushed this part!&lt;/p&gt;

&lt;p&gt;If I can do it on a practice board successfully, on-demand, with one hand, it should be more than believable that it’s possible to do it successfully in better circumstances.&lt;/p&gt;

&lt;p&gt;On the Macintosh board they were much easier, and fewer, and I also never lifted a pad. I was left with the odd leg that was easily removed with some excess solder during the cleanup phase.&lt;/p&gt;

&lt;p&gt;In total 8 capacitors were in need of replacement, and I swapped them with tantalum capacitors which I understand will never need replacing in future. They look different, but I don’t mind that it makes it more obvious that they have been replaced IMHO.&lt;/p&gt;

&lt;p&gt;Oh, and I bought some new rosin/flux-core solder of a reputable brand, and that really improved my soldering technique and results compared to when I was using the old/cheap/free solder I had in my tool box. You get what you pay for!&lt;/p&gt;

&lt;p&gt;The results are not my finest work, but I would say above average and the machine is now booting well with sound and a faster replacement drive so we’re good to go!&lt;/p&gt;

&lt;p&gt;I picked up an extra 2MB of RAM from eBay to take this machine to a maximum of 4MB! That’s megabytes!&lt;/p&gt;

&lt;p&gt;All that remains is for me to configure my SD card with the apps and games I like to have around. I’m going for multiple System installs that I can switch between after a quick reboot. Plus Japanese support. This task is made easier and quicker by using emulators to do the hard work many times faster than hardware, and finally copy the prepared disk image onto the SD card.&lt;/p&gt;

&lt;p&gt;Then, at last, I will be able to play games on this little thing once more!&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-1.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-1.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-2.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-2.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-3.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-3.jpg&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-4.jpg&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/macintosh-classic-recap-4.jpg&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 82%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 20 Feb 2021 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2021/02/20/macintosh-classic-logic-board-recap/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2021/02/20/macintosh-classic-logic-board-recap/</guid>
        </item>
      
    
      
        <item>
          <title>Shinji and Good Friends: Second Hanafuda Impact</title>
          <description>&lt;p&gt;Shinji and Good Friends: Second Hanafuda Impact is a hanafuda video game for Windows, released by Gainax in 1999. You can unlock wallpapers though beating each of the characters in the game.&lt;/p&gt;

&lt;p&gt;Just for fun I reverse engineered and edited the save game to unlock all the wallpapers. The save game data is not very big so I decided on a brute force approach: I beat one character to get enough save data and then set about changing and reloading it to figure out the location of everything else.&lt;/p&gt;

&lt;p&gt;I was surprised to find the 10th image, shown large in this post, as it did not have a typical placeholder like the others. Also surprising are the reserved save slots for more (seemingly abandoned) unlockable wallpapers.&lt;/p&gt;

&lt;p&gt;Anyway, these wallpapers are probably new material for most Evangelion fans!&lt;/p&gt;

&lt;p&gt;Whilst doing this hacking, I found a debug mode (dialog/speech tester) which is now documented at &lt;a href=&quot;https://tcrf.net/Shinji_and_Good_Friends:_Second_Hanafuda_Impact&quot;&gt;The Cutting Room Floor&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;carousel__holder&quot;&gt;
    &lt;div class=&quot;carousel&quot;&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;a&quot; checked=&quot;checked&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;b&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;c&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;d&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;e&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;f&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;g&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;h&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;i&quot; /&gt;
        
          &lt;input class=&quot;carousel__activator&quot; type=&quot;radio&quot; name=&quot;carousel&quot; id=&quot;j&quot; /&gt;
        
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;j&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;i&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;j&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
          
          
          
          
          &lt;div class=&quot;carousel__controls&quot;&gt;
              &lt;label class=&quot;carousel__control carousel__control--backward&quot; for=&quot;i&quot;&gt;&lt;/label&gt;
              &lt;label class=&quot;carousel__control carousel__control--forward&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
          &lt;/div&gt;
        
        &lt;div class=&quot;carousel__track&quot;&gt;
          &lt;ul&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-1.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-1.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-2.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-2.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-3.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-3.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-4.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-4.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-5.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-5.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-6.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-6.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-7.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-7.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-8.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-8.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-9.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-9.png&quot; /&gt;&lt;/li&gt;
            
            &lt;li class=&quot;carousel__slide&quot; style=&quot;background-image: url(&apos;https://cdn.gingerbeardman.com/images/posts/shinji-10.png&apos;);&quot;&gt;&lt;img class=&quot;carousel__staticimage&quot; src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-10.png&quot; /&gt;&lt;/li&gt;
            
          &lt;/ul&gt;
        &lt;/div&gt;
        &lt;div class=&quot;carousel__indicators&quot;&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;a&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;b&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;c&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;d&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;e&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;f&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;g&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;h&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;i&quot;&gt;&lt;/label&gt;
            
              &lt;label class=&quot;carousel__indicator&quot; for=&quot;j&quot;&gt;&lt;/label&gt;
            
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.carousel__holder {width: 100%; position: relative; padding-bottom: 75%; margin: 1rem 0 1rem;}
.carousel {
  height: 100%;
  width: 100%;
  overflow: hidden;
  text-align: center;
  position: absolute;
  padding: 0;
}
.carousel__staticimage,
.carousel__controls,
.carousel__activator {
  display: none;
}

.carousel__activator:nth-of-type(1):checked ~ .carousel__track {
  -webkit-transform: translateX(-000%);
          transform: translateX(-000%);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__slide:nth-of-type(1) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__controls:nth-of-type(1) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(1):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(1) {
  opacity: 1;
}

.carousel__activator:nth-of-type(2):checked ~ .carousel__track {
  -webkit-transform: translateX(-100%);
          transform: translateX(-100%);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__slide:nth-of-type(2) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__controls:nth-of-type(2) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(2):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(2) {
  opacity: 1;
}

.carousel__activator:nth-of-type(3):checked ~ .carousel__track {
  -webkit-transform: translateX(-200%);
          transform: translateX(-200%);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__slide:nth-of-type(3) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__controls:nth-of-type(3) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(3):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(3) {
  opacity: 1;
}

.carousel__activator:nth-of-type(4):checked ~ .carousel__track {
  -webkit-transform: translateX(-300%);
          transform: translateX(-300%);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__slide:nth-of-type(4) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__controls:nth-of-type(4) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(4):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(4) {
  opacity: 1;
}

.carousel__activator:nth-of-type(5):checked ~ .carousel__track {
  -webkit-transform: translateX(-400%);
          transform: translateX(-400%);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__slide:nth-of-type(5) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__controls:nth-of-type(5) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(5):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(5) {
  opacity: 1;
}

.carousel__activator:nth-of-type(6):checked ~ .carousel__track {
  -webkit-transform: translateX(-500%);
          transform: translateX(-500%);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__slide:nth-of-type(6) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__controls:nth-of-type(6) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(6):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(6) {
  opacity: 1;
}

.carousel__activator:nth-of-type(7):checked ~ .carousel__track {
  -webkit-transform: translateX(-600%);
          transform: translateX(-600%);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__slide:nth-of-type(7) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__controls:nth-of-type(7) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(7):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(7) {
  opacity: 1;
}

.carousel__activator:nth-of-type(8):checked ~ .carousel__track {
  -webkit-transform: translateX(-700%);
          transform: translateX(-700%);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__slide:nth-of-type(8) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__controls:nth-of-type(8) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(8):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(8) {
  opacity: 1;
}

.carousel__activator:nth-of-type(9):checked ~ .carousel__track {
  -webkit-transform: translateX(-800%);
          transform: translateX(-800%);
}
.carousel__activator:nth-of-type(9):checked ~ .carousel__slide:nth-of-type(9) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(9):checked ~ .carousel__controls:nth-of-type(9) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(9):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(9) {
  opacity: 1;
}

.carousel__activator:nth-of-type(10):checked ~ .carousel__track {
  -webkit-transform: translateX(-900%);
          transform: translateX(-900%);
}
.carousel__activator:nth-of-type(10):checked ~ .carousel__slide:nth-of-type(10) {
  transition: opacity 0.5s, -webkit-transform 0.5s;
  transition: opacity 0.5s, transform 0.5s;
  transition: opacity 0.5s, transform 0.5s, -webkit-transform 0.5s;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
  -webkit-transform: scale(1);
          transform: scale(1);
}
.carousel__activator:nth-of-type(10):checked ~ .carousel__controls:nth-of-type(10) {
  display: block;
  opacity: 1;
}
.carousel__activator:nth-of-type(10):checked ~ .carousel__indicators .carousel__indicator:nth-of-type(10) {
  opacity: 1;
}


.carousel__control {
  height: 30px;
  width: 30px;
  margin-top: -15px;
  top: 50%;
  position: absolute;
  display: block;
  cursor: pointer;
  border-width: 5px 5px 0 0;
  border-style: solid;
  opacity: 0.35;
  opacity: 1;
  outline: 0;
  z-index: 3;
  color: #fafafa;
  mix-blend-mode: difference;
}
.carousel__control:hover {
  opacity: 1;
}
.carousel__control--backward {
  left: 20px;
  -webkit-transform: rotate(-135deg);
          transform: rotate(-135deg);
}
.carousel__control--forward {
  right: 20px;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
}
.carousel__indicators {
  position: absolute;
  bottom: 20px;
  width: 100%;
  text-align: center;
}
.carousel__indicator {
  height: 10px;
  width: 10px;
  border-radius: 100%;
  display: inline-block;
  z-index: 2;
  cursor: pointer;
  opacity: 0.35;
  margin: 0 2.5px 0 2.5px;
}
.carousel__indicator:hover {
  opacity: 0.75;
}
.carousel__track {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0;
  margin: 0;
  transition: -webkit-transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s;
  transition: transform 0.5s ease 0s, -webkit-transform 0.5s ease 0s;
}
.carousel__track .carousel__slide {
  display: block;
  top: 0;
  left: 0;
  right: 0;
  opacity: 1;
}

.carousel__track .carousel__slide:nth-of-type(1) {
  -webkit-transform: translateX(000%) translateZ(0);
          transform: translateX(000%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(2) {
  -webkit-transform: translateX(100%) translateZ(0);
          transform: translateX(100%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(3) {
  -webkit-transform: translateX(200%) translateZ(0);
          transform: translateX(200%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(4) {
  -webkit-transform: translateX(300%) translateZ(0);
          transform: translateX(300%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(5) {
  -webkit-transform: translateX(400%) translateZ(0);
          transform: translateX(400%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(6) {
  -webkit-transform: translateX(500%) translateZ(0);
          transform: translateX(500%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(7) {
  -webkit-transform: translateX(600%) translateZ(0);
          transform: translateX(600%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(8) {
  -webkit-transform: translateX(700%) translateZ(0);
          transform: translateX(700%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(9) {
  -webkit-transform: translateX(800%) translateZ(0);
          transform: translateX(800%) translateZ(0);
}

.carousel__track .carousel__slide:nth-of-type(10) {
  -webkit-transform: translateX(900%) translateZ(0);
          transform: translateX(900%) translateZ(0);
}


.carousel--scale .carousel__slide {
  -webkit-transform: scale(0);
          transform: scale(0);
}
.carousel__slide {
  height: 100%;
  position: absolute;
  opacity: 0;
  overflow: hidden;
}
.carousel__slide .overlay {height: 100%;}
.carousel--thumb .carousel__indicator {
  height: 30px;
  width: 30px;
}
.carousel__indicator {
  background-color: #fafafa;
}

.carousel__slide:nth-of-type(1),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(1) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(2),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(2) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(3),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(3) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(4),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(4) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(5),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(5) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(6),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(6) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(7),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(7) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(8),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(8) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(9),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(9) {
  background-size: cover;
  background-position: center;
}

.carousel__slide:nth-of-type(10),
.carousel--thumb .carousel__indicators .carousel__indicator:nth-of-type(10) {
  background-size: cover;
  background-position: center;
}

&lt;/style&gt;

&lt;script&gt;
  function isVisible(el) {
        while (el) {
            if (el === document) {
                return true;
            }

            var $style = window.getComputedStyle(el, null);

            if (!el) {
                return false;
            } else if (!$style) {
                return false;
            } else if ($style.display === &apos;none&apos;) {
                return false;
            } else if ($style.visibility === &apos;hidden&apos;) {
                return false;
            } else if (+$style.opacity === 0) {
                return false;
            } else if (($style.display === &apos;block&apos; || $style.display === &apos;inline-block&apos;) &amp;&amp;
                $style.height === &apos;0px&apos; &amp;&amp; $style.overflow === &apos;hidden&apos;) {
                return false;
            } else {
                return $style.position === &apos;fixed&apos; || isVisible(el.parentNode);
            }
        }
  }
  
  setInterval(function(){
    var j=0;
    var elements = document.querySelectorAll(&apos;.carousel__control--forward&apos;);
    for(i=(elements.length - 1);i&gt;-1;i--) {
      if(isVisible(elements[i])) j=i;
    }
    elements[j].click();
  },7000);
  
&lt;/script&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-new.png&quot; alt=&quot;PNG&quot; title=&quot;New/empty save game file contents&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/shinji-hacked.png&quot; alt=&quot;PNG&quot; title=&quot;Hacked save game file contents&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;p&gt;Grab the ISO at &lt;a href=&quot;https://archive.org/details/shinji-and-good-friends-second-hanafuda-impact&quot;&gt;archive.org/details/shinji-and-good-friends-second-hanafuda-impact&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 22 May 2020 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2020/05/22/shinji-and-good-friends-second-hanafuda-impact/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2020/05/22/shinji-and-good-friends-second-hanafuda-impact/</guid>
        </item>
      
    
      
        <item>
          <title>Replacing bitmap graphics in a PlayStation game</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;A version of this article was &lt;a href=&quot;https://www.patreon.com/posts/28136581&quot;&gt;originally posted on my Patreon&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found an old Hanafuda Koi-Koi game that I can no longer easily play due to… sigh… what they call progress? It’s called Koikoi Komachi and was released around 2005 for Mac OS X (initially for PPC, and later Intel).&lt;/p&gt;

&lt;p&gt;I really like the cards images that it has, and was able to extract them using &lt;a href=&quot;https://echoone.com/filejuicer/&quot;&gt;File Juicer&lt;/a&gt; which is a kind of Swiss Army Knife for easily extracting files that might be embedded in an app, archive or disk image.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hanafuda-card-transplant-1.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;The original cards as a sprite sheet extract from Koikoi Komachi&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But just looking at the cards is not enough. I really needed to play a video game with them, you know? So I thought it would be cool to transplant them into the PlayStation game Youkai Hana Asobi.&lt;/p&gt;

&lt;h2 id=&quot;heres-what-i-did&quot;&gt;Here’s what I did&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Split the new card image into individual cards&lt;/li&gt;
  &lt;li&gt;Resize/shrink the individual cards to the dimensions used in the PS1 game&lt;/li&gt;
  &lt;li&gt;Extract the images I want to edit from the PS1 game&lt;/li&gt;
  &lt;li&gt;Edit the extracted images to add the new cards and make any other changes&lt;/li&gt;
  &lt;li&gt;Replace the images in the PS1 game with the new ones&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;image-manipulation&quot;&gt;Image Manipulation&lt;/h2&gt;

&lt;p&gt;To do the image splitting and resizing I used &lt;a href=&quot;https://flyingmeat.com/retrobatch/&quot;&gt;Retrobatch&lt;/a&gt; which makes this kind of stuff really easy. I created a workflow to crop out the individual cards and do the resizing all in one batch. Very cool!&lt;/p&gt;

&lt;h2 id=&quot;image-editing&quot;&gt;Image Editing&lt;/h2&gt;

&lt;p&gt;PlayStation images are palette-based so you need to use an image editor that respects the embedded indexed colour palette. There may be other capable editors, but Adobe Photoshop is very good at this sort of thing so that’s what I used. Any old version will do, you definitely do not need the latest version.&lt;/p&gt;

&lt;p&gt;I pasted each of my small card images into the two images used by the game, replacing the spectre/monster cards that are default. I also took time to change the options screen to modify the thumbnail that signifies which card design you’re using, and I also added a “NEW” label to the title screen.&lt;/p&gt;

&lt;p&gt;I stopped short of changing the “help” card images because that would have been a lot more work and I do not personally look at those whilst playing.&lt;/p&gt;

&lt;h2 id=&quot;rom-hacking&quot;&gt;ROM Hacking&lt;/h2&gt;

&lt;p&gt;There are many ways to get images out of a PS1 game, but I settled on &lt;a href=&quot;https://www.romhacking.net/utilities/799/&quot;&gt;Tim2View&lt;/a&gt; because it offers an all-in-one solution for extraction and insertion. It is as easy as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Export PNG… (F4)&lt;/li&gt;
  &lt;li&gt;(do your image editing elsewhere)&lt;/li&gt;
  &lt;li&gt;Import PNG… (F5)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Tim2View writes to your PS1 bin file at each operation—without prompting—so always keep a backup just in case something goes wrong!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gotcha:&lt;/strong&gt; be sure to respect whatever colour is marked as transparent when making your image edits. In my case this was the colour black (0,0,0) so I had to make sure to use an almost-black colour in my new graphics to avoid unwanted transparent pixels. You can quickly check the state of your image by toggling the background transparency type at the bottom of the window. If it’s wrong just tweak and re-import.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hanafuda-card-transplant-2.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;A composite image showing the various graphics that were replaced or edited&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;that-was-fun&quot;&gt;That was fun!&lt;/h2&gt;

&lt;p&gt;I then used &lt;a href=&quot;https://projects.sappharad.com/tools/multipatch.html&quot;&gt;MultiPatch&lt;/a&gt; to create an IPS patch file from the changes, so I could make this mod easy for other gamers to enjoy.&lt;/p&gt;

&lt;p&gt;The finished patch is available at: &lt;a href=&quot;https://www.romhacking.net/hacks/4593/&quot;&gt;www.romhacking.net/hacks/4593/&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;screenshot&quot;&gt;Screenshot&lt;/h2&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/hanafuda-card-transplant-3.png#pixel&quot; alt=&quot;PNG&quot; title=&quot;The final graphics being used whilst playing a game of Koi-Koi&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 04 Jul 2019 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2019/07/04/replacing-bitmap-graphics-in-a-playstation-game/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2019/07/04/replacing-bitmap-graphics-in-a-playstation-game/</guid>
        </item>
      
    
      
        <item>
          <title>Feeling for the headphone socket</title>
          <description>&lt;p&gt;This is how I easily feel where the headphone socket is around the back of my iMac.&lt;/p&gt;

&lt;p&gt;I stuck a little felt circle around the socket which has the benefit of being easy to physically feel as well as acting as a guide for the headphone jack.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/feeling-for-the-headphone-socket.jpg&quot; alt=&quot;JPG&quot; title=&quot;The headphone socket with felt circle and 3.5mm headphone jack&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 05 Jul 2017 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2017/07/05/feeling-for-the-headphone-socket/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2017/07/05/feeling-for-the-headphone-socket/</guid>
        </item>
      
    
      
        <item>
          <title>Koi-Koi by KIN SOFT for MSX (1994)</title>
          <description>&lt;p&gt;I &lt;a href=&quot;https://msx-fan-wiki.appspot.com/view/1099&quot;&gt;read about an old doujin user-created Koi-Koi game&lt;/a&gt; 花札こいこい made by KIN SOFT in 1994. This is my document of the hunt I went on to be able to find and play it!&lt;/p&gt;

&lt;h2 id=&quot;msxfan&quot;&gt;MSX・FAN&lt;/h2&gt;

&lt;p&gt;Firstly, the game came with &lt;a href=&quot;https://archive.org/details/MSXFAN199406/page/n29/mode/2up&quot;&gt;MSX・FAN Issue 26&lt;/a&gt; (1994/06) and by that I mean in the pages and on one of the two cover disks. So I had to find them in the existing archives. I found them on a Spanish website that I’ve since lost the link to. The game could be installed on a blank .dsk, which of course you have to &lt;a href=&quot;https://www.msx.org/wiki/How_to_make_a_floppy_disk_image&quot;&gt;generate and format&lt;/a&gt;. The resulting .dsk played just fine in OpenMSX but not fMSX. 🤔&lt;/p&gt;

&lt;h2 id=&quot;dsk2rom&quot;&gt;dsk2rom&lt;/h2&gt;

&lt;p&gt;After some thought I figured out that using the Windows tool &lt;a href=&quot;https://github.com/joyrex2001/dsk2rom&quot;&gt;dsk2rom.exe&lt;/a&gt; I could convert the .dsk into a .rom file that would boot straight into the game when using fMSX.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dsk2rom.exe -6d blank.dsk boot.rom
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Those command-line options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt; = use 60Hz video (the Japanese standard)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;d&lt;/code&gt; = allow booting of other diskroms (was required for compatibility)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there were still some errors in certain situations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;without MSXDOS2 or TurboR: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Syntax error in 40&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;with fMSX in RetroArch: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Disk full in 40&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;a href=&quot;https://www.msx.org/forum/msx-talk/emulation/help-running-converted-dsk-as-rom-in-retroarch-fmsx&quot;&gt;some help from my &lt;em&gt;MSX&lt;/em&gt; friends&lt;/a&gt; it became clear the game required:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RAMDISK&lt;/code&gt; command (part of MSXDOS2)&lt;/li&gt;
  &lt;li&gt;256KB RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: I think I had to create a .dsk containing both MSXDOS2 and then install the game onto to that. The MSX Turbo R has MSXDOS2 built-in.&lt;/p&gt;

&lt;h2 id=&quot;retroarch&quot;&gt;RetroArch&lt;/h2&gt;

&lt;p&gt;But fMSX in RetroArch doesn’t allow you to set RAM and VRAM and in fact its defaults for those were wrong for some types of &lt;em&gt;MSX&lt;/em&gt;. I &lt;a href=&quot;https://github.com/libretro/fmsx-libretro/pull/14&quot;&gt;made some changes to fmsx-libretro core&lt;/a&gt; and whilst I was in there made &lt;a href=&quot;https://github.com/libretro/fmsx-libretro/pulls?q=is%3Apr+author%3Agingerbeardman+is%3Aclosed+is%3Amerged&quot;&gt;several more improvements&lt;/a&gt;. I even &lt;a href=&quot;https://github.com/libretro/fmsx-libretro/pull/25&quot;&gt;updated the core to fMSX version 4.9&lt;/a&gt; for all RetroArch users. I’m skipping over the huge effort that all these changes took, but it was fun hacking on libretro!&lt;/p&gt;

&lt;p&gt;After all this I had a test build of the fmsx-libretro core for 3DS that I could use to play the game. 🎴&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;the-game&quot;&gt;The Game&lt;/h3&gt;

&lt;p&gt;The game is really interesting because of the yaku (card combos) it contains, and the fact that it has a Special Card Shop where you can use points to buy Special cards for cheating in imaginative ways against the CPU.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Peek at Deck: Check the next card in the deck&lt;/li&gt;
  &lt;li&gt;Peek at Opponent: View the opponent’s hand&lt;/li&gt;
  &lt;li&gt;Card Explosion: Destroy a card on the table + force the opponent to skip a turn&lt;/li&gt;
  &lt;li&gt;Card Swap: Swap cards in your hand&lt;/li&gt;
  &lt;li&gt;Landmine Card: Explodes if the opponent takes it&lt;/li&gt;
  &lt;li&gt;Deck Collapse: Turn over two cards from the deck consecutively&lt;/li&gt;
  &lt;li&gt;Time Reversal: Reset the game&lt;/li&gt;
  &lt;li&gt;Lightning Card: Prioritise acquiring 20-point cards&lt;/li&gt;
  &lt;li&gt;Electric Vortex Card: Makes it easier to take 20-point cards from the board&lt;/li&gt;
  &lt;li&gt;Storm: Swap the cards you are about to take&lt;/li&gt;
  &lt;li&gt;Bamboo Cutter Card: Probability effect allowing you to acquire any card&lt;/li&gt;
  &lt;li&gt;Atomic Bomb Card: Significantly reduces the opponent’s available cards&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;A .zip of both the .dsk and .rom file: &lt;a href=&quot;https://cdn.gingerbeardman.com/files/koikoi-kin-soft-1994.zip&quot;&gt;koikoi-kin-soft-1994.zip&lt;/a&gt; (247 KB)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If you are interested in Hanafuda video games or cards, why not join the &lt;a href=&quot;/2019/02/28/hanafuda-discord/&quot;&gt;Hanafuda Discord server&lt;/a&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/msx-koikoi-by-kin-soft-1994.jpg&quot; alt=&quot;IMG&quot; title=&quot;Koi-Koi by KIN SOFT (1994) running in fmsx-libretro on Nintendo 3DS (2017)&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://archive.org/details/MSXFAN199406/page/n29/mode/2up&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/msx-fan-magazine-1994-06.jpg&quot; alt=&quot;IMG&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;figcaption&gt;Magazine spread feature detailing the game, its controls, hanafuda, and valid yaku (card combos)&lt;/figcaption&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 28 Jun 2017 11:13:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2017/06/28/koikoi-by-kin-soft-for-msx-1994/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2017/06/28/koikoi-by-kin-soft-for-msx-1994/</guid>
        </item>
      
    
      
        <item>
          <title>Adding Markdown Support to Safari</title>
          <description>&lt;p&gt;I recently created a Safari Extension that renders plain text Markdown files as good looking HTML right in your browser. I also added a context menu item so you can swap between the rendered HTML and the original Markdown text.&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.gingerbeardman.com/safari/Doctor.safariextz&quot;&gt;gingerbeardman.com/safari/Doctor.safariextz&lt;/a&gt; (25Kb)&lt;/p&gt;

&lt;p&gt;To install: double click the file after it has downloaded.&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;Recently my buddy and fellow Former Apple Technology Evangelist @&lt;a href=&quot;https://medium.com/u/35a6b2e3855b?source=post_page-----c19f3d74f728--------------------------------&quot;&gt;TDRBY&lt;/a&gt; asked if there was an extension available for rendering Markdown files in Safari. I had a quick look—there wasn’t—and that got me thinking.&lt;/p&gt;

&lt;p&gt;On macOS you can use Quick Look plugins to add support for new file types to Finder’s Quick Look preview popup. These plugins are great, I use a bunch of them, but they only apply in Finder and not in Safari. Maybe I could do something similar?&lt;/p&gt;

&lt;p&gt;So, knowing that Safari renders plain text files by wrapping them in simple &lt;em&gt;HTML&lt;/em&gt; &amp;gt; &lt;em&gt;BODY&lt;/em&gt; &amp;gt; &lt;em&gt;PRE&lt;/em&gt; markup, I thought that an extension should be able to modify such files. That was enough to pique my interest—so I got to work!&lt;/p&gt;

&lt;h2 id=&quot;how&quot;&gt;How?&lt;/h2&gt;

&lt;p&gt;The extension took less than an hour to put together. This blog post took me a lot more time to write! Anyway, here’s how I did it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create new extension using &lt;em&gt;Safari&lt;/em&gt; &amp;gt; &lt;em&gt;Develop&lt;/em&gt; &amp;gt; &lt;em&gt;Extension Builder&lt;/em&gt; &amp;gt; &lt;em&gt;+ button&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Set website access level to &lt;em&gt;All&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Confirm extensions run on plain text files in Safari by adding a CSS file containing only &lt;em&gt;body { background-color: red !important; }&lt;/em&gt;—success, they do!&lt;/li&gt;
  &lt;li&gt;Find a suitable JavaScript Markdown to HTML converter &lt;a href=&quot;https://github.com/showdownjs/showdown&quot;&gt;showdown.js&lt;/a&gt;—this ticks two important boxes: it is easy to use, and is still being actively developed&lt;/li&gt;
  &lt;li&gt;Write a few lines of JavaScript to run &lt;em&gt;showdown.js&lt;/em&gt; on the page text and replace it with the generated HTML&lt;/li&gt;
  &lt;li&gt;Add &lt;a href=&quot;https://github.com/sindresorhus/github-markdown-css&quot;&gt;github-markdown.css&lt;/a&gt; and apply the &lt;em&gt;markdown-body&lt;/em&gt; class to make things look better&lt;/li&gt;
  &lt;li&gt;Configure the extension to run only on URLs ending in &lt;a href=&quot;http://superuser.com/a/285878&quot;&gt;common Markdown file extensions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Manually add &lt;em&gt;https://&lt;/em&gt; variations of the Markdown file extensions to the &lt;em&gt;Info.plist&lt;/em&gt; (far quicker than using the Extension Builder user interface)&lt;/li&gt;
  &lt;li&gt;Edit the .map file reference out of the minimised &lt;em&gt;showdown.js&lt;/em&gt; to avoid a runtime access warning&lt;/li&gt;
  &lt;li&gt;Build the release package&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point I’d like to mention a couple of great open-source projects that made this task so much easier: &lt;a href=&quot;https://github.com/sindresorhus/github-markdown-css&quot;&gt;github-markdown.css&lt;/a&gt; by &lt;em&gt;Sindre Sorhus&lt;/em&gt;, and &lt;a href=&quot;https://github.com/showdownjs/showdown&quot;&gt;showdown.js&lt;/a&gt; by &lt;em&gt;Estevão Soares dos Santos&lt;/em&gt;—nice work guys!&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;There are Markdown files all over the internet, but two notable source stand out.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Almost every GitHub project has a README.md file. View the RAW version to have the extension render it as HTML. Here’s &lt;a href=&quot;https://raw.githubusercontent.com/primer/primer-markdown/master/README.md&quot;&gt;the one I used&lt;/a&gt; for testing.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/u/182e1a455da3?source=post_page-----c19f3d74f728--------------------------------&quot;&gt;John Gruber&lt;/a&gt;’s excellent &lt;a href=&quot;http://daringfireball.net&quot;&gt;daringfireball.net&lt;/a&gt; blog is written using Markdown, and the source of each post can be seen by appending .text to the URL. Here are &lt;a href=&quot;http://daringfireball.net/linked/2016/11/15/designed-by-apple-in-california.text&quot;&gt;two&lt;/a&gt; &lt;a href=&quot;http://daringfireball.net/2016/11/new_touch_bar_equipped_macbook_pros.text&quot;&gt;posts&lt;/a&gt; that I used to test. Of course, John is also the creator of Markdown. Thanks, John! 👍&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;postmortem&quot;&gt;Postmortem&lt;/h2&gt;

&lt;p&gt;After the initial build, a period of testing revealed some issues:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Strikethrough, and some other non-standard but frequently used Markdown, was not being rendered. This was fixed by enabling some options in the &lt;em&gt;showdown.js&lt;/em&gt; file.&lt;/li&gt;
  &lt;li&gt;Rendering quirks due to using &lt;em&gt;github-markdown.css&lt;/em&gt; outside of the GitHub page structure required few manual CSS tweaks. Mainly this was to make tables look better.&lt;/li&gt;
  &lt;li&gt;Markdown on GitHub that was already rendered as HTML was being processed a second time, resulting in corrupted pages. This was fixed by blacklisting the &lt;em&gt;github.com&lt;/em&gt; domain.&lt;/li&gt;
  &lt;li&gt;Sometimes I wanted to see the plain text Markdown. I fixed this by spending an inordinate amount of time adding a context menu item that allows you to &lt;em&gt;Show&lt;/em&gt; or &lt;em&gt;Render Markdown&lt;/em&gt;. I hope it was worth it! 😬&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;safari-wish-list&quot;&gt;Safari Wish List&lt;/h2&gt;

&lt;p&gt;Despite the brief development, I noticed a few quirks to do with Safari that I did not expect to encounter:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You can’t open the Web Inspector whilst viewing a plain text file. But if you open the inspector on a blank tab, or an existing HTML page, you can then navigate to the plain text file and the inspector will remain open. Phew!&lt;/li&gt;
  &lt;li&gt;Due to the way Safari Extension access permissions work, I have had to enable the extension for all pages and then reduce its power by whitelisting a range of Markdown file extensions. This feels like cracking a nut with a sledgehammer.&lt;/li&gt;
  &lt;li&gt;Extensions do not run on local &lt;em&gt;file:///&lt;/em&gt; documents. 😩&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please join me in &lt;a href=&quot;http://bugreport.apple.com&quot;&gt;filing enhancement requests with Apple&lt;/a&gt; if you’d like to add your support to any of the above points.&lt;/p&gt;

&lt;h2 id=&quot;thanks-for-reading&quot;&gt;Thanks for reading!&lt;/h2&gt;

&lt;p&gt;If you have any questions feel free to get in touch using &lt;a href=&quot;http://twitter.com/gingerbeardman&quot;&gt;twitter&lt;/a&gt; or &lt;a href=&quot;http://www.gingerbeardman.com&quot;&gt;my website&lt;/a&gt;. I’m interested to see more extensions that add support for other file formats in Safari. And I’m available to help you make better products!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 25 Nov 2016 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2016/11/25/adding-markdown-support-to-safari/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2016/11/25/adding-markdown-support-to-safari/</guid>
        </item>
      
    
      
        <item>
          <title>How to prevent an individual iOS app from updating, forever</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;Note, August 2023: this method is outdated, for the latest &lt;a href=&quot;/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/&quot;&gt;please see this newer blog post&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Change is the only constant. Nowhere is this more visible than on the App Store. &lt;a href=&quot;http://www.statista.com/statistics/258160/number-of-new-apps-submitted-to-the-itunes-store-per-month/&quot;&gt;Thousands of new apps and games appear every day&lt;/a&gt; and existing apps receive regular updates to add features and fix bugs. This will only accelerate further now that the App Store is firing on all cylinders and review times are &lt;a href=&quot;http://appreviewtimes.com&quot;&gt;measured in hours rather than days&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what if you want to continue to use an old version of an app for your own reasons? Perhaps the old version has a better user interface, or the new version has some bugs? I can remember both of these issues affecting me numerous times over the years. The eBay app is the example that springs to mind.&lt;/p&gt;

&lt;p&gt;General consensus of opinion is that the only thing you can do is switch off automatic updates in iOS and avoid updating that particular app manually, which is easier said than done.&lt;/p&gt;

&lt;h2 id=&quot;there-has-to-be-a-better-way&quot;&gt;There has to be a better way&lt;/h2&gt;

&lt;p&gt;It’s a little known fact that there is actually a way to install an old version of an app on your iOS device which will prevent it from being checked for updates. Read on to find out how this can be done. This tutorial uses eBay as the example app, but I’ve also carried out this process on Gmail, Instagram, VSCO and more.&lt;/p&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;good old iTunes,&lt;/li&gt;
  &lt;li&gt;the iOS device you want to install it on,&lt;/li&gt;
  &lt;li&gt;the USB cable for your iOS device.&lt;/li&gt;
  &lt;li&gt;a backup of the old version of the app as an .ipa file on your Mac. You’ll only have this if you use iTunes to sync your iOS device or download app updates. Have a look in your Time Machine or other backups in the following location:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;~/Music/iTunes/iTunes Music/Mobile Applications&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Warning: as part of this process you will be required to delete the current version of the app that you have installed on your iOS device. So be sure to backup any data from the app that is not stored in the cloud!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;enay&quot;&gt;eNay&lt;/h2&gt;

&lt;p&gt;eBay introduced a disastrous fourth version of &lt;a href=&quot;https://itunes.apple.com/gb/app/ebay-shop-search-buy-sell./id282614216?mt=8&quot;&gt;their app&lt;/a&gt; back in September 2015. It was a complete rewrite of the app that made it much more cumbersome to use, and was also missing key functionality and information that were present in the previous app. It was, understandably, &lt;a href=&quot;https://community.ebay.com/t5/eBay-Mobile-App-iOS/gp-p/Mobile-iOS&quot;&gt;met with a user revolt&lt;/a&gt;. eBay are now up to version 5.0 and it is arguably still not as good as the old version I like to use.&lt;/p&gt;

&lt;h2 id=&quot;vsc-no&quot;&gt;VSC-NO!&lt;/h2&gt;

&lt;p&gt;The photo editing app &lt;a href=&quot;https://itunes.apple.com/gb/app/vsco/id588013838?mt=8&quot;&gt;VSCO&lt;/a&gt; is another app that has enraged users with an update that destroys the usability of the app. Strangely enough that is also version 5.x and users are also outraged over the change.&lt;/p&gt;

&lt;h2 id=&quot;gfail&quot;&gt;Gfail&lt;/h2&gt;

&lt;p&gt;Yes again another disaserous version 5.x! This tim it is Gmail, the new version being very similar to the “Inbox by Gmail” app that has been bad and stayed bad for some time now. Gmail v5 is at one-star on the App Store.&lt;/p&gt;

&lt;h2 id=&quot;to-the-time-machine&quot;&gt;To the Time Machine!&lt;/h2&gt;

&lt;p&gt;Due to the fact I frequently do a manual update of all my apps through iTunes on my Mac, I had a backup of eBay 3.6.1 for iPhone and VCO 4.6.1 sitting in my Time Machine backup on my external drive.&lt;/p&gt;

&lt;h2 id=&quot;how-to-install-an-old-app-and-prevent-it-from-updating&quot;&gt;How to install an old app and prevent it from updating&lt;/h2&gt;

&lt;p&gt;Before we begin, remove the app you want to downgrade from your device by tapping and holding on it. Make sure to backup any data you need from it!&lt;/p&gt;

&lt;p&gt;This example uses the eBay app, but applies equally well to VSCO.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Copy your &lt;em&gt;backup&lt;/em&gt; of .ipa somewhere where you can work on it.&lt;/li&gt;
  &lt;li&gt;Open the .ipa with &lt;em&gt;Archive Utility&lt;/em&gt; to decompress it.&lt;/li&gt;
  &lt;li&gt;Expand the folder until you get to the &lt;em&gt;Payload&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Drop the &lt;em&gt;eBay.app from the Payload folder&lt;/em&gt; onto iTunes “My Apps”. It’s the one whose icon has a “no entry” on it.&lt;/li&gt;
  &lt;li&gt;If prompted, &lt;em&gt;replace the newer version&lt;/em&gt; of the app in iTunes (you can always download the latest version again from the App Store).&lt;/li&gt;
  &lt;li&gt;Check that iTunes recognises the app payload (for this to work apps must have been downloaded with an Apple ID that is authorised on this Mac). Don’t worry about the missing app icon, that’s OK.&lt;/li&gt;
  &lt;li&gt;Go into your iOS device screen, Apps section, find the app through search and press &lt;em&gt;Install&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Apply&lt;/em&gt; changes to Sync, and you’re done!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;just-let-me-look-at-some-screenshots-to-figure-this-out-ok&quot;&gt;“Just let me look at some screenshots to figure this out”. OK!&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1520/1*gUV9EY1MRIoiXxbCDlTm7Q.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1520/1*3Kwyf5B9csphNzx-3GH2IA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1520/1*Jf9vqIDutU0YZiuUMpXQ9g.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps 1, 2 &amp;amp; 3:&lt;/strong&gt; Work on a copy of your .ipa backup, extract it, find the payload app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1520/1*vLWOprl3ADFZ1YqrbWpL5w.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1204/1*P1K_MTvZbHjCNdRpI21BeA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1520/1*6kOllWf4Bsvve3cBE1SRgw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps 4, 5 &amp;amp; 6:&lt;/strong&gt; Drop the payload app onto iTunes, replace the newer version if prompted, check that the app is listed without an icon.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/1400/1*XjEi0Yc59GdO6uhiEJpMzQ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7:&lt;/strong&gt; Browse to the apps on your phone, find the app you just added, mark it for install. &lt;strong&gt;Step 8:&lt;/strong&gt; Sync, you’re done!&lt;/p&gt;

&lt;h2 id=&quot;so-how-does-this-work&quot;&gt;So how does this work?&lt;/h2&gt;

&lt;p&gt;What we are doing here is installing the app on the iOS device &lt;em&gt;without the associated app receipt&lt;/em&gt;, so the App Store app is not aware the old version is on the device and so will never prompt you to update it.&lt;/p&gt;

&lt;h2 id=&quot;how-do-i-undo-this-kind-of-install&quot;&gt;How do I undo this kind of install?&lt;/h2&gt;

&lt;p&gt;If you want to get things back to normal, simply delete the app from your iOS device and reinstall a newer version of the app without doing this trick.&lt;/p&gt;

&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;/h2&gt;

&lt;p&gt;The one limitation of this is how long the developer will support the infrastructure that powers the old version of the app. For eBay this hasn’t been a problem to date, but there are no guarantees. Enjoy it whilst it lasts.&lt;/p&gt;

&lt;p&gt;Have fun keeping it old school! If you have any questions you as me on twitter @&lt;a href=&quot;https://twitter.com/gingerbeardman&quot;&gt;gingerbeardman&lt;/a&gt;.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 19 Jul 2016 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2016/07/19/how-to-prevent-an-individual-ios-app-from-updating-forever/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2016/07/19/how-to-prevent-an-individual-ios-app-from-updating-forever/</guid>
        </item>
      
    
      
        <item>
          <title>iStock: Download large comp</title>
          <description>&lt;p&gt;Back in 2012, before my time at Apple, I was a contract developer and would often work with designers on specific projects. Over the course of those projects, we’d have to find stock photos for the apps or websites we were building and generally turned to &lt;a href=&quot;https://www.istockphoto.com&quot;&gt;iStockPhoto&lt;/a&gt; as our main resource.&lt;/p&gt;

&lt;p&gt;During the design phase we would go through number of different image options, using the watermarked thumbnail images from iStock as temporary placeholders, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Comprehensive_layout&quot;&gt;comps&lt;/a&gt;, before settling on the final image that we would buy.&lt;/p&gt;

&lt;p&gt;As well as the thumbnail, iStock also offered a zoom function that let you scroll around a large version of the photo. It occurred to me that I might be able to use this to get a larger watermarked comp for download?&lt;/p&gt;

&lt;h2 id=&quot;version-1&quot;&gt;Version 1&lt;/h2&gt;

&lt;p&gt;In 2012, iStock’s zoom function loaded in the larger image split in tiles. My hunch is that this was to prevent people from downloading the larger version, as I doubt it would be much of a bandwidth saving mechanism.&lt;/p&gt;

&lt;p&gt;Regardless, I was undaunted and up for the challenge. My first attempt at solving the problem, to prove that it was possible, was a UserScript. This is a snippet of JavaScript that can be run using an extension like &lt;a href=&quot;http://www.greasespot.net&quot;&gt;GreaseMonkey&lt;/a&gt; or &lt;a href=&quot;https://github.com/os0x/NinjaKit&quot;&gt;NinjaKit&lt;/a&gt; in much the same way as a native browser extension. They’re a very quick way to prototype an idea.&lt;/p&gt;

&lt;p&gt;After the concept was proven I wrote extensions for both Safari and Chrome. When triggered—via a modified link on the page—the extension would zoom the image to the highest level, then programmatically grab each tile, composite the tiles together on a HTML5 canvas element, and finally convert the canvas into a large JPEG image that can easily be downloaded.&lt;/p&gt;

&lt;p&gt;A lot of effort, you might say, but it worked very well and became quite popular throughout the design community.&lt;/p&gt;

&lt;h2 id=&quot;version-2&quot;&gt;Version 2&lt;/h2&gt;

&lt;p&gt;In early 2013 iStock changed their website to remove the zoom feature, allowing the large comp to be accessed directly without having to stitch together a bunch of tiles. I updated my extensions to cope with these changes, which of course involved throwing out most of the code I’d written for the first version.&lt;/p&gt;

&lt;p&gt;As a software developer you learn to be ready to &lt;a href=&quot;http://c2.com/cgi/wiki?KillYourDarlings&quot;&gt;kill your darlings&lt;/a&gt; during any sort of refactoring.&lt;/p&gt;

&lt;h2 id=&quot;version-3&quot;&gt;Version 3&lt;/h2&gt;

&lt;p&gt;Today I release the third version of the extension, rewritten to work with the current iStock website. The extension becomes even simpler, but remains incredibly useful. You can &lt;a href=&quot;http://www.gingerbeardman.com/safari/&quot;&gt;download it at my website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This version adds a link—a green “Download large comp” button—below the existing, red “Download this photo” button. The beauty of this new link is that it is more usable and friendly than the previous versions of the tool.&lt;/p&gt;

&lt;p&gt;The download can be triggered in a few different ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Clicking the green button will open the zoomed image in a new tab.&lt;/li&gt;
  &lt;li&gt;Option-clicking the green button to download the large comp immediately.&lt;/li&gt;
  &lt;li&gt;Right-clicking on the green button will to show a contextual menu with further options, such as the ability to copy the web address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This version will hopefully last for a while, but I’ll be keeping my eye on it. &lt;a href=&quot;https://github.com/gingerbeardman/iStockLargeComp.safariextension&quot;&gt;The source code is, of course, on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://miro.medium.com/max/1400/1*LdtRYi2Bv7oBTKTpLtXC-w.jpeg&quot; alt=&quot;&quot; title=&quot;iStockLargeComp: notice the new green button&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;I have a lot of fun building extensions. Not just because it involves programming, DOM manipulation using JavaScript, and some HTML and CSS for styling. For me, the real fun comes out of deconstructing the inner workings of the webpage—I guess you could call it reverse engineering—and then implementing a solution that will not only work well, but be resistant to breaking at the slightest changes to the page it is modifying.&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 30 Jul 2012 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2012/07/30/istockphoto-download-large-comp/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2012/07/30/istockphoto-download-large-comp/</guid>
        </item>
      
    
      
        <item>
          <title>Hacking the Becker Cascade navigation CF card</title>
          <description>&lt;p&gt;I just had my tonsils removed and whilst recovering at home I decided to try to figure out how I could use larger CF cards in my &lt;a href=&quot;https://ifdesign.com/en/winner-ranking/project/becker-traffic-pro-7949/2069&quot;&gt;Becker Traffic Pro 7949&lt;/a&gt; car audio and navigation system, a close relative to the Becker Cascade.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/becker-7949.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The unit came with maps on a 2GB card which is barely big enough for the maps, leaving not much room for MP3 files. Later map distributions increased the size of the card to 4GB as the map quality increased, but still not much room remaining for music.&lt;/p&gt;

&lt;h2 id=&quot;lets-go-bigger&quot;&gt;Let’s go bigger&lt;/h2&gt;

&lt;p&gt;I bought a new CF card of the same type (SanDisk), opting for a SanDisk Ultra 8GB, and copied the files and volume name over. It didn’t work.&lt;/p&gt;

&lt;p&gt;Next, I cloned the smaller card onto the larger card and that worked! Then I expanded the size of the partition and it stopped working.&lt;/p&gt;

&lt;p&gt;What’s going on?&lt;/p&gt;

&lt;h2 id=&quot;partition-magic&quot;&gt;Partition Magic&lt;/h2&gt;

&lt;p&gt;At the time I had a ThinkPad laptop with a built in CF reader. This allowed me to look at the card contents at a device/block level rather than the filesystem level. I noticed that there was the usual space after the main partition, and low and behold what did I see at the start of that “unused” space? Some magic text!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HARMANBECKER BECKER V4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I even figured out the magic text for Ferrari branded units with the assistance of a helpful owner.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HARMANBECKER FERRARI V4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The version number would be increased in step with any updates to the system software.&lt;/p&gt;

&lt;p&gt;By partitioning a SanDisk card and giving it the same file contents, volume name, and this magic text made the upgrade work. For a while I sold these modified cards on eBay for a nice little markup and it kept me in video games for a while. Good times!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 05 Sep 2009 00:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2009/09/05/hacking-the-becker-cascade-navigation-cf-card/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2009/09/05/hacking-the-becker-cascade-navigation-cf-card/</guid>
        </item>
      
    
      
        <item>
          <title>Katamari Damacy iPod Color</title>
          <description>&lt;p&gt;I hacked my last iPod Color using &lt;a href=&quot;http://osx.iusethis.com/app/alterpod&quot;&gt;AlterPod&lt;/a&gt; so that I get the Prince of All Cosmos instead of the standard no entry sign. The iPod was later stolen, returning the little Prince to live amongst the stars!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/katamari-damacy-ipod-color.jpg&quot; alt=&quot;JPG&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 08 Aug 2005 11:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/08/08/katamari-damacy-ipod-color/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/08/08/katamari-damacy-ipod-color/</guid>
        </item>
      
    

  </channel>
</rss>
