Automating fun with bash
zootr and bash
Published at
I really like The Legend of Zelda: Ocarina of Time Randomizer I used it as the base for collecting my notes on OCI artifacts1. I've watched races and show cases for years and find a lot of enjoyment in the puzzle solving aspect of the randomizer.
Installing the randomizer is a git clone of the code base, and using it is
running a Python module. No pip install
this or mkvirtualenv
that. Just
python Gui.py
or if you like to live in the terminal python
OoTRandomizer.py
2. The two just run. I wanna just point out that this is a
very, very non-trivial primarily Python project that just runs on Linux and
Windows with no dependencies. Frankly, I am beyond amazed at that alone.
Anyways, after playing a handful of seeds and settling on some settings that fit with my skill level, I was kind of tired of booting up a GUI and clicking things. However, the CLI didn't provide all the options I needed to generate patched roms, or at least all the options I wanted to create patched roms. The primary missing piece is the CLI doesn't provide a way to declare a path to a compatible z64 file. But after digging through code and asking around on the community discord I learned that an uncompressed z64 file with a specific name inside the project directory also works.
I also wanted some quality of life features. The randomizer provides them on top of the base OOT game, why not provide my own over the randomizer itself? These were things like putting the files directly into my preferred directory, providing some kind of naming mechanism, and making use of the various versions and forks the community develops. OOTR is less a randomizer and more a foundation for a constellation of randomizers that offer a variety of features, including a randomizer for the randomizer!.
So I sat down and wrote some bash, as one does and a little bit of a Dockerfile.
There's three bash scripts, the first is responsible for acquiring a version of the randomizer and creating a container image from it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
This I think is a really good example of why I unironically place bash in my
top three favorite programming languages. A comparable script in another
language3 would be much, much longer. The nastiest part is acquiring the
actual placement of the script in the file system which is important because I
may invoke this from anywhere and the script makes use a temporary directory to
hold the cloned repository using pushd
and popd
to move to and from
it4
You may ask yourself, "Where is that beautiful house Dockerfile?" Right here.
1 2 3 4 5 6 7 8 9 |
|
This is where the decompressed ROM comes into play. OOTR provides precompiled
binaries for compressing and decompressing ROMs. I also want to bring attention
to the COPY --from="base"
line. There's an accompanying --build-context
"base=${SCRIPT_DIR}"
in the script that builds this image, these are the two
halves of using multiple build contexts. Some of the container's content comes
from one place and some comes from another place. Initially I had forgotten
about this feature as I'd never used it before and the initial script
was...hairy to say the least. Multiple contexts not only shortened and
simplified the build script, it also felt very intuitive to use as well.
Let's look at the entrypoint next:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Here there's some CLI parsing, SHA256 hash comparisons to figure out if we already have a decompressed ROM already or not and finally actually invoking the randomizer, which this script instructs to always generate a settings file, primarily for my archival purposes but it also aids in automatic tagging.
So we have a build script, the entrypoint script and the Dockerfile. How the
heck do these all fit together? There's a last file I've named
linkspocket.sh
, after the Randomizer's name for the "area" we find starting
items in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
|
I hear it already. "ALEC! That's more than hundred lines of bash, what the fuck." And
my answer is (:
But honestly, it's not that much and mostly its formatting
quality of life and CLI argument parsing. Again, unironically a top three
language for me and admittedly Python probably puts up a much better argument
this time around5
There's a lot going on here but the primary points are:
- This script allows switching between versions of the randomizer6 and ensures that version is available as a container.
- Ensures the ROM is available inside the container
- Copies, rathere than uses a volume mount, to extract the generated output
Why copy instead of a volume mount? Simply I didn't really feel like fighting
file permissions when I could be playing a seed instead. Note above the
Dockerfile lazily and dangerously sets file permissions to 777
as well. Also
the container runs as root, it's a real "do as I say, not as I do" moment. :shrug:
The script also names the ultimate destination of all of the output after either the tag provided, or, if not provided, after the textual representation of the visual representation of the seed hash.
Finally, I added a symlink from this script to ~/.local/bin/linkspocket
as a
favor to myself.
What's next?
I've gotten a lot of use outta these scripts already. But I've been heavily interested in OCI artifacts recently so that seems a logical next step to me. I won't follow the examples I laid out in the previous post, those were complex to explore and illustrate what the OCI image and distribution specifications allows. Instead, I'll likely, initially, flatten the output to a single manifest with each file stored in its own blob. And eventually storing settings separately so they could be referenced from the CLI instead of providing the settings string.
The randomizer also supports quite a few cosmetic options, including models and music which would interesting to include in a similar fashion to how the ROM is included currently
-
mostly because reading SBOM and signature again and again and again makes my eyes glaze over, it's important but it isn't interesting reading to me ↩
-
It even has a hashbang if you wanna make that file executable! ↩
-
setting aside other shell languages ↩
-
These are very handy utilities to build a stack of directories to navigate, they have more functionality than this and I'd recommend reaching from them occasion ↩
-
Spoilers, that's also the next iteration of this and why I'm posting raw bash instead of linking to a repository or even some kinda paste sharing site, these are the first iterations ↩
-
I only play on the primary randomizer so this script doesn't support forks currently ↩