I made a Firefox add-on (and so can you)

This morning, I came across someone complaining that the option to use the old Youtube layout has been removed. A quick search turned up this Reddit post:

Attaching "disable_polymer=1" to the arguments in the URL will keep the page using the previous layout. Unfortunately though that setting doesn't carry over to other pages when you load a new one.

Automatically appending an extra parameter to the end of Youtube URLs didn't sound too difficult, so I decided to use it as an opportunity to learn about building browser add-ons... and it turns out that it really is pretty simple:

  • I started off by looking at "Your First WebExtension" on MDN. This example is based around injecting scripts into pages, though, which isn't quite what I wanted to do.
  • A bit of searching led me to "Intercept HTTP Requests"... which is more-or-less exactly what I wanted to do!
  • After a few misadventures, I put everything in a .ZIP file (as per "Package Your Extension") and called it a day.

I've now uploaded it to the Firefox add-ons site, and have made the source code available on Gitlab.com.


PROTIP: Don't forget that your intercept-if-youtube.com logic will also catch any redirects to youtube.com... like, say, the ones with &disable_polymer=1 on the end that your code generates. Oops!

Kiniro updates

I finally updated Kiniro to use the actual, final version of Rails 5.2.0 last weekend, having left it running on 5.2.0.rc1 for a while. I also fixed various strange RSpec/MySQL connection problems, and made everything Rubocop-compliant, which - for some reason - I apparently didn't do before.

This weekend, I've attempted to make things look nicer by updating the CSS. I also made a small improvement to the post editor: It now delays the re-draw of the Markdown "live preview" area for about one second, so that continuous typing doesn't cause constant re-parsing/drawing:

var update = function () {
  // ...
};

// Update after a short delay if no further change events are fired:
var timeout = null;
var queueUpdate = function () {
  clearTimeout(timeout);
  timeout = setTimeout(update, 1500);
}

update();
content.addEventListener('change', queueUpdate);
content.addEventListener('keyup', queueUpdate);

(This used to be a bit of a problem with longer posts - especially if they had embedded images or Youtube videos!)

There are still a few things to do, but most of them are behind-the-scenes stuff, like improving the CSS for pages only logged-in users can see.

OpenMW

I've been thinking about Morrowind again recently, so a few days ago I tried installing OpenMW - a cross-platform, open-source reimplementation of the Gamebryo engine it runs on.

Installation was easy; OpenMW itself is available from the Debian package repository, so after downloading/installing the necessary .deb files I simply ran the launcher program, which copied everything from the retail CD to my hard drive before running the game.

(I did run into one minor hiccup - a message box with "ERROR: Unknown fallback name: FontColor_color_header" when starting the game for the first time - but that was easy enough to fix, thanks to the OpenMW wiki.)

I've only just started playing with a new character, so I've barely made it out of Seyda Neen at the moment, but from what I've seen so far it works flawlessly - even on my ancient 2008-era desktop machine!

If you're considering (re-)playing Morrowind, especially on a not-Windows OS, you might want to give it a whirl.

1337 preprocessor trix

After a long hiatus, I've recently decided to look at the Flash file format again. I may write more about it at some point - there's certainly plenty of interesting quirks to discuss - but today I'd like to share something I learned about C preprocessor macros.

† At time of writing, my last commit on the Vector2D project was just over three years ago.


One of the simplest uses for #define is to give a name to a common value:

#define SWF_TAG_SET_BACKGROUND_COLOR 9

You can also use it to create function-like substitutions like so:

#define swfDecodeTwips(value) ((value) / 20.0)

However, the search/replace logic only matches exact text:

#define single(x) One x
single(apple)
/* Output for `gcc -E example.c` is "One apple" */

#define plural(x) Two xs
plural(apple)
/* Output for `gcc -E example.c` is "Two xs" */

To get around this, you can use the ## operator:

#define plural(x) Two x##s
plural(apple)
/* Output for `gcc -E example.c` is "Two apples" */

With a bit of cleverness, this can be used to define "template" functions for cases where near-identical logic applies to distinct structures - something that other languages might support via inheritance, generics, or mixins:

#define swfDefineCreateFunction(PARENT, CHILD, NAME)\
  Swf##CHILD *swfCreate##CHILD(Swf##PARENT *p) {\
    void *tmp;\
    if (!p) return NULL;\
    tmp = realloc(p->NAME, (p->num_##NAME##s + 1) * sizeof(Swf##CHILD*));\
    if (!tmp) return NULL;\
    p->NAME = (Swf##CHILD**)tmp;\
    p->NAME[p->num_##NAME##s] = (Swf##CHILD*)malloc(sizeof(Swf##CHILD));\
    if (!p->NAME[p->num_##NAME##s]) return NULL;\
    memset(p->NAME[p->num_##NAME##s], 0, sizeof(Swf##CHILD));\
    ++p->num_##NAME##s;\
    return p->NAME[p->num_##NAME##s - 1];\
  }

static swfDefineCreateFunction(Shape, Path, path)

/*
static SwfPath *swfCreatePath(SwfShape *p) {
  void *tmp;
  if (!p) return NULL;
  tmp = realloc(p->path, (p->num_paths + 1) * sizeof(SwfPath*));
  if (!tmp) return NULL;
  p->path = (SwfPath**)tmp;
  p->path[p->num_paths] = (SwfPath*)malloc(sizeof(SwfPath)); 
  if (!p->path[p->num_paths]) return NULL;
  memset(p->path[p->num_paths], 0, sizeof(SwfPath));
  ++p->num_paths;
  return p->path[p->num_paths - 1];
}
*/

swfDefineCreateFunction(Movie, Shape, shape)

/*
SwfShape *swfCreateShape(SwfMovie *p) {
  void *tmp; 
  if (!p) return NULL; 
  tmp = realloc(p->shape, (p->num_shapes + 1) * sizeof(SwfShape*)); 
  if (!tmp) return NULL; 
  p->shape = (SwfShape**)tmp; 
  p->shape[p->num_shapes] = (SwfShape*)malloc(sizeof(SwfShape)); 
  if (!p->shape[p->num_shapes]) return NULL; 
  memset(p->shape[p->num_shapes], 0, sizeof(SwfShape)); 
  ++p->num_shapes; 
  return p->shape[p->num_shapes - 1];
 }
*/

I'm not sure that this idea is enough to qualify for inclusion under the articles section; perhaps I should write up a more-in-depth explanation of this newspost at some point.

there comeing arrrrrrrr............

Many years ago, The Game Creators (who, at the time, were mainly known for DarkBASIC and DarkBASIC Professional) ran a competition in partnership with Alienware. The rules were simple: Create a DarkBASIC game featuring the provided alien.3ds character model, and display the "Play it on Alienware" logo at some point. The winner would be rewarded with a free Alienware gaming PC.

I didn't enter this competition, but I did download several of the entries to see what they were like. This was in early 2004, so I would have been about 17 years old, playing them on my old Toshiba laptop running Windows XP.

I recently decided to download some of these games again. However, the website has been redesigned several times over the last decade, making them rather difficult to find. Thankfully, some digging around on archive.org led me to what I was looking for, so here are three of the games that I have re-discovered 14 years later:

Star Thing

TGC Showcase Entry | Download | Mirror

image

Star Thing was a neat little FPS in which you had to find passcodes for doors by reading notes scattered about the levels. You could only carry 4 items at a time (guns, health kits, and armour kits), so you had to carefully decide which things you took with you when travelling to the next level.

It makes me think of System Shock II or Deus Ex, and I played through several times back in 2004. It worked just fine on Windows 7 when I tried it out today, so I made Matt play though it.

Operation Genocide

TGC Showcase Entry | Download

Another cool little FPS, this one actually made it into the top 10 of the competition results.

I couldn't get this one working under Windows 7 (it complained about a missing D3D-related DLL file), but there are quite a few videos of it on Youtube: one, two, three, four.

Cold Sails

TGC Showcase Entry | Download

Okay, so this one is a bit ropey compared to the other two, but that doesn't change the fact that I played it back in 2004. Plus, it's got a wonderfully DarkBASIC-ey feel to it!

I haven't tried running this under a more-modern version of Windows yet, so I have no idea if it will work. I suppose I could always dig out my ancient Windows XP laptop, since I do still have it in a cupboard somewhere...

...and more!

You can see the full list of competition entries here: TGC Alienware Competition results

Note that quite a few of the download links on the archive.org version of the page won't work, but this is easy enough to fix:

  • If the URL starts with http://files.thegamecreators.com, then it is still available today - just paste it into your browser address bar.
    • Example: http://files.thegamecreators.com/showcase/alienware/ColdSails.zip
  • If the URL starts with http://files2.thegamecreators.com, then it will still work today if you change files2 to files.
    • Example: http://files2.thegamecreators.com/showcase/alienware/Operation Genocide.zip

This has been a public service announcement by the society of concerned citizens for DarkBASIC history preservation. Now, go and try out some of the other competition entries!

Uploads and RSpec

I implemented file-uploading yesterday evening, so now we can add images and stuff.

I'll be making everything available on my GitLab page once the code is a bit more polished; the public-facing pages look OK right now, but the behind-the-scenes stuff is pretty awful. In addition, I set up RSpec, simplecov, and FactoryBot this morning, so hopefully any further code changes will be a less likely to break things...

Further improvements

A few days later, and we now have the ability to post articles, plus a nice new CSS theme!

One cool feature is that code blocks are automatically syntax-highlighted:

/* A simple Hello World program in C. */
#include <stdio.h>

int main(int argc, char **argv)
{
  printf("Hello, World!\n");
  return 0;
}

This is done via highlight.js, something I came across a few years ago whilst writing Markdown-based documentation at CDL. I had to write some custom JS to account for Turbolinks events, but other than that it was pretty straightforward to set up.

(The font used is called VT323, and you can find it on Google Fonts.)

Adventures in ActiveStorage

I couldn't get into work today due to traffic jams caused by a sudden snowfall overnight, so I've spent the day putting together a Rails 5.2 app and getting it up-and-running here on kiniro.uk.

Rails 5.2 isn't officially released yet (I'm using the release candidate), but one of its new Killer Features™ is ActiveStorage, which makes file uploads a breeze out-of-the-box. I've previously been using the Paperclip gem for work-related projects, which works Just Fine, though it does require new database columns to be added in order to attach stuff to specific tables, whilst ActiveStorage does not.

Anyway, that's all for now - things are still very incomplete, and I have to get everything ship-shape before I tell nginx to make this available on port 80.

PS: I'll also need to set up SSL via LetsEncrypt at some point - one thing at a time...