After promising to be back, I immediately disappeared for more than a year. Classic ‘canmom liveblogs umineko’ moments right here.

I feel like I should talk a little about how the sausage is made here to explain why writing these liveblogs is a bit time consuming. And also how I am going to go about fixing it! Which is to say, as programmers are wont to do, spending dozens of hours solving a program with code to save minutes of repetitive labour later…


But before that, to address a reader criticism: about my exposition on quantum mechanics in episode 4 chapter 3, it was pointed out that the light polarisation effect I was talking about is actually a purely classical effect. You don’t need quantum mechanics to explain polarising filters. That’s already how light works in classical wave theory. An entirely fair criticism, and a bit of an unfortunate mistake on my part that I misremembered that as being a ‘quantum thing’. I’ve added a note to that effect in chapter 3.

Awkwardly, to find a genuine spooky quantum effect, you need to look somewhere that’s a bit more complicated and esoteric to explain to a non-physicist reader. Besides the double-slit experiment, I think most undergraduate expositions will use something like the Stern-Gerlach Experiment. But you have to explain quite a lot to use that one: magnetic fields, spin, etc. etc., so it’s not like there’s an intuition to violate. If anyone has ideas for what’s a better example for exposition, do let me know!


OK, let’s look at how the sausage is made.

Hacking OnScripterRU to take screenshots for me

I write these articles on my static site, as a Markdown file, which is built by Jekyll into the HTML that you’re reading now, combining it with all the templates and stuff that make up my site. That’s lovely as far as it goes—I can write in a text editor, everything is nice and compact, the server barely has to do anything to serve pages, and I have a local copy in case I ever want to change to a different host.

The tricky part is images. If I get to a page in Umineko that I want to tuck in as an illustration, I need to:

  1. screenshot the image.
  2. crop out the borders so it will embed better in the page.
  3. save the image to an appropriate place in my site’s images directory.
  4. write out the markup for an image including that new filename into my markdown file
  5. since I care about accessibility, I also write out the alt text, which generally speaking includes the text in the image. So generally speaking I need to manually transcribe the text onscreen, or else look it up in the script files and copy paste it which takes about as long.

Step 5 is the most time consuming part but the whole thing is a bit of a faff, especially compared to something like writing to an editor like Tumblr where I can directly paste an image in and it will just appear, with code taking care of all the behind-the-scenes stuff like uploading the image to Tumblr’s servers and returning its location to embed in the post. (Additionally, back when I was writing on Tumblr, it was not possible to put alt-text on images. To their credit, Tumblr have since added this option. But not having to write alt-text did save time.)

But we can do better, can’t we? Umineko Project is presented in a program called ONScripter-RU, which is helpfully open source. At any point in time, this program will have program state which will include both the image that it’s displaying to me, and the text on the screen for me to embed into the alt text. All I need to do is fork ONScripter and add the functionality I want.

So how does ONScripter work? Well, it’s a multi-platform program largely written in C++, all built on top of a widely used graphics library called SDL, which abstracts over operating system specific APIs for stuff like creating windows, playing sound or creating a graphics context. (It’s also got some Java code so it can run on Android).

Seems simple enough right? Weeelllllll. You can find the build instructions here. On Windows, which I regrettably am currently using, you need to compile with MSYS2 (that suits me, I despise visual studio) along with a whole bunch of dependencies.

OK, but before we do that, let’s see if they haven’t already implemented a screenshot command that we might be able to apply to our purposes. It does appear that pressing E might take a screenshot, but it depends on something called state.keyState.opt also being true. Here, the state is a type called EventProcessingState. Alternatively, we can press whichever button ONS_SCANCODE_SCREEN resolves to.

Could ‘opt’ be something like the Ctrl key? It turns out: no. Ctrl actually fast-forwards. So definitely not that! Let’s dig it out of the source code. EventProcessingState is defined in ONScripter.hpp. And the field keyState is of type KeyState, where does this come from? Apparently from a header file called Support/KeyState.hpp. OK, let’s look there. OK, that defines the KeyState, so we can learn it has fields called ctrl, shift and opt. We can look around for where KeyState is written, but that suggests to me that opt might refer to the Alt key..? Well, if it’s taken a screenshot, I can’t tell! Since I don’t know where it’s saving them.

So let’s figure out what happens when we take a screenshot, then we’ll come back to seeing if we can get it to do that. The effect of pressing a screenshot key is to set a value called needs_screenshot to true. Since needs_screenshot is not defined anywhere in Event.cpp, it must be defined elsewhere as a global variable or a field on the OnScripter class (since we’re in a method)… and sure enough we find it in the header file that defines that class, ONScripter.hpp. OK, what happens when this flag is set? There is a fairly complicated looking function called flushDirect defined here, which in multiple code paths checks this flag and if it’s set, calls create_screenshot, which is found elsewhere in the same file.

create_screenshot has some stuff to handle double buffering and such, but the important part is that it saves to a location called script_h.save_path. What is script_h? Well, it’s not a field defined on OnScripter. Looking at the imports to OnScripter.hpp, I took a guess and had a look through Parser.hpp, which does indeed have a script_h field on its ScriptParser class which is of type ScriptHandler. OnScripter inherits from ScriptParser so it knows about the script_h field. (This is where I start muttering about composition over inheritance…)

Keep digging. Where is ScriptHandler defined? That turns out to be Engine/Handlers/Script.cpp, where we can indeed find save_path as a string pointer. So where does this string pointer get set? A method called setSaveDir. OK, where does that get called? One place, in a method called loadEnvData back on OnScripter, which handles loading settings from a file called ‘envdata’. OK, where does envdata come from? I’m not sure where this is defined in the source, but since I had to go digging for save data in the past, I know that the answer on Windows is C:\ProgramData\OnScripter-RU\UminekoPS3ficationEn.

It turns out that envdata is a binary file so not exactly easy to pop open in a text editor and tweak. But, silver lining: this also turns out to be where screenshots are being saved! And indeed pressing Alt-E adds a new screenshot. That’s a start. But simply taking and saving screenshots is easy, it was already a single button. I want to do a bit more than that: to read out the current dialogue text, save the image somewhere useful, crop it, and generate the text data to embed the image into my Umineko writeup.

There are two approaches I could consider here. One is to start running a hacked version of ONScripter with the screenshot functionality inserted. The other is to have an external program which watches the folder where it saves screenshots. Every time one appears, it could carry out the necessary image operations. The latter would be a lot nicer since I won’t have to use C++ or have to dig through the source code of ONScripter anymore, but it does have one big flaw: the text data for the screen wouldn’t be easily available without doing something like OCR, or something even crazier like monitoring the memory of the running ONScripter program to find the field which stores the text and extract that.

Since automating the alt-text is a big part of what I want to do here, that means we probably have to do our screenshot stuff inside ONScripter. So, step 1 is to get ONScripter to compile… getting Pacman to update properly was a pain because of some permissions shit (possibly a conflict with being installed via Scoop?) but with a bit of run I got it to run ./configure and ran make… and the compilation immediately crashed on trying to patch the makefile of bzip2 and complaining that the patch has the wrong hash.

So what’s going on? For reasons best known only to the developers, ONScripter downloads its dependencies from a second Github-hosted repo maintained by the developers which has tarballed up versions of the source of various dependencies like bzip2, FFmpeg etc. I can sort of see the logic of this: C++ long predates the idea of a centralised package manager or standardised build system for a language, so you have to use one of a variety of custom-rolled solutions. And this is a cross-platform project, so it can’t rely on a distro providing the necessary headers and development files, and of course those might not even be the right version…

(You stop and contemplate how good you have it with modern languages like Rust and Zig?)

Now, the developers also needed to modify these dependencies in various ways. The way they did this was… to write a bunch of patch files to apply after downloading the dependencies, which do things like change a makefile to swap out different CFLAGs or whatever.

Despite all the patches being provided in the repo, the .pkgbuild files specifying which patches to apply also check that these patches have the correct hash. In case the user tampered with them or something? Who knows. Anyway, now enter Git. If you are a poor benighted fool who is running on Windows for work reasons or because they haven’t gotten around to setting up that Arch partition or whatever (both, if you’re wondering), we have to deal with the different line endings on different platforms. Long story short, Windows uses two characters (CR,LF) and Linux just uses one (LF). Git will helpfully save your repo with CRLF on Windows and LF on Linux. Most text editors can figure out which one is being used so usually this is not a problem.

However, the patches are text files. The hashes were calculated assuming lines end in LF. So if anyone is also in the ridiculous position of building ONScripter on Windows, you first need to set Git to download the repo with LF instead of CRLF. There are Git commands to update an already downloaded repo, but I couldn’t get them to work, so I deleted the repo, enabled LF mode everywhere, and downloaded it again.

Presto! Now the hashes resolve. I could start building ONScripter… up until it failed to build FFmpeg, due to this error. Luckily, there are solutions in the thread: downgrade binutils, or patch FFmpeg (more patches! what could possibly go wrong!)… or, I suppose, simply switch to a more up to date FFmpeg with the patch already applied.

In the end I went for the latter strategy (after some time trying to figure out how to downgrade to an older binutils, which turns out not to be available on MSYS2). I found the (freshly released) FFmpeg version 8.0 from github’s handy auto-archive thing and modified the .pkgbuild file to download that version. After some shenanigans in which MSYS2 forgot about GCC for a minute (probably due to the fucking around with binutils), and installing an extra package called nasm, I managed to get FFmpeg to configure… but not build, since unfortunately it links a DirectX header file in libavutil, and whichever version of the DirectX header file it links tries to use an undefined type called DXGI_RGBA, which is presumably supposed to be defined in another DirectX header file, and presumably I have the wrong version of that.

My first thought was to have MSYS2 install the DirectX headers package. Unfortunately that did not seem to be sufficient. So, I learned about the g++ -M option (or -H if you want to read a whole novel) which tells you where include paths resolve to. It turns out you also have to make sure to run this from the correct folder (in this case, the folder with config.h in it) to find the right files because yeah of course it would be path dependent, that’s just how C compilation rolls. Anyway, the answer to my question is that I’m getting the header file from C:/ProgramData/Scoop/apps/msys2/2024-01-13/mingw32/include/dxgi1_2.h… maybe I just need to make msys2 update itself? Or rather, have Scoop update MSYS2 for me. And install all the dependencies again. And then update MSYS2 from inside itself with pacman -Syu just to be sure.

Finally, the moment of truth: finding out if FFmpeg would compile… it did not. Updated MSYS2, same damn problem!!

So, the problem seems to be that the version of dxgi1_2.h distributed with MSYS2 is wrong somehow; not the fault of FFmpeg, although someone must have figured out a workaround or you wouldn’t have FFmpeg available on mingw. Alas, I have yet to find good information on how to fix that, so I will have to leave it alone for now.

Before trying that, another idea occurred: I could check the forks of OnScripter to see if anyone was maintaining an up-to-date version. It turns out that someone is: there is a mod in development called Umineko Concerto, designed to bring some additional Umineko material to the Project experience and improve various quality of life things, and they have an actively developed fork of OnScripterRU. Yay! I pulled the repo and set it building with mingw32. All went well up until the crucial FFmpeg step… and once again it barfed, some part of the FFmpeg code involved an invalid CUDA pointer operation. Haha, wow, we’re really in the depths of it now.

Unfortunately, we are still on an ancient version of FFmpeg that’s being hotpatched, and if I go through the same procedure to update to the latest FFmpeg, I will run into the same problem described above.

I could keep digging but I think the conclusion to draw is that nobody really cared to keep the MinGW32 build path up to date, on any fork of the project, and the sensible thing to do is to cross-compile from Linux. Of course. Otherwise I probably have to work my way through the entire build system and replace every single component that doesn’t work, encountering who knows what problems along the way.

(I did also try building with MinGW64, but this also fails—one of the dependencies tells you to use MinGW32 instead. Also, just to be sure, I went ahead and reinstalled MSYS2 with the official installer rather than via Scoop. I encountered all the same errors.)

So the next thing I intend to do is install Arch Linux on the spare SSD I’ve been procrastinating installing Linux on since I got this computer two years ago. (I had to use Windows for work, forgive me for my sins.) Then I can compile it for Linux, and never have to worry about Windows again, hooray. Since that’s what we call a ‘big job’, we’ll set this project aside for now until I have a working Linux boot with all the things I’m accustomed to.

You got any overly hirstute Himalayan cattle? Because boy am I good at yakshaving. Anyway, this episode will have to take its screenshots the old-fashioned way for now. Let’s get on with it!

Back on the island of Rokkenjima, where whatever problems they may be having, at least they don’t have to worry about compiling C programs for Windows. Unless… huh, turns out Windows 1.0 was actually released in 1985, and of course the C programming language goes back to 1972, so I guess it isn’t entirely out of the question. If that tuns out to be a plot point later, I’m saying now that I called it!

Let’s begin

Speaking of Ange… the story picks up with her point in the timeline. We find ourselves in a new location: a doctor’s surgery in Niijima owned by Nanjo’s son (inherited posthumously, of course.) And that means a new talksprite, for Nanjo Masyuki…

Nanjo Masayuki, thinner than his father, in a drab medical room. He is saying 'I am truly sorry to say this after you have come all this way, but I have nothing in particular to tellyou.

He’s not best pleased about the media circus around the Rokkenjima jiken, and tries to dismiss Ange’s inquiries… but the fact that we have a scene here suggests that there will probably be something to learn here.

He wants nothing more to do with the whole thing, and is rather condescending about it—the inverse of Ange’s feelings. Ange fends him off with a precision deployment of tragic backstory. The doctor is, notably, a pretty old-looking guy, with grey hairs; his face model is kind of round, taking after his dad. He merits a character entry, though they don’t give us his age…

Nanjo Masayuki (南條 雅行)

Nanjo’s son. Took over the Nanjo Clinic. Unlike Nanjo, he gives a slightly dispassionate, indifferent impression.

After the commotion surrounding Rokkenjima, he grew to completely despise the pres, and never again did he attempt to speak of what had happened at the time.

He used to have a daughter who was afflicted with an intractable disease, but unfortunately, she wasn’t able to live out her natural lifespan.

(What is a natural lifespan, anyway? Hm, that sounds like a philosophical can of worms that can stay shut for now.)

Nanjo Jr. is kind of formal and frosty, even when Ange shames him into opening up. Still, he says he’ll tell her ‘everything’. Which means the whole story of Nanjo and Kinzo’s ‘chess connection’ (waggle eyebrows?). Not in so many words, he basically observes that Kinzo was a huge asshole and Nanjo was the only person ‘magnanimous’ (大らか) enough to put up with him.

Ange asks why Nanjo Sr. should be trusted to attend the family conferences, and learns that (as we know) Kinzo was in a pretty bad way at the time. But, Masayuki says, his medical charts have been lost (all of them!).

Having clicked on the side branches of the conversation tree, Ange advances to the main subject: was there anything weird? Masayuki hems and haws but finally admits that he got a letter, sent in his own name, addressed to Nanjo Sr…. not at Rokkenjima, but at Rebun Island in the far north of Hokkaido. (Ainu land!)

The music turns spooky. We learn that Nanjo Sr.’s corpse was not found in the aftermath of the Rokkenjima incident. So of course, they discuss the question of whether he faked his death. But the address on the envelope simply does not exist. And the listed house number is 1-2-34-567. So, seems fake.

Prediction: when they read the letter it will be in the same hand as the author of the diaries…

So, we establish the letter was sent on October 3rd, and the address shenanigans were a way to ensure there would be a delay until after the incident.

The contents of the letter are: a written letter, a small key with the number A112, and a swipe card. (Good luck using that after 12 years…)

Hmm, A112, have we seen that one before? I grepped all my previous writeups and it doesn’t appear to be. The card says “MEMBERS” in English. The letter, meanwhile, has the string “07151129”, which we have seen. We get the Goosebumps drippy font this time too.

The number 07151129 in red blood like text with drips, and the same number in the description box.

At the time it was written alongside the Seventh Pentacle of the Sun. This time it’s written with the name of a major bank. So, an account number? No, a safe deposit box, which Masayuki has helpfully already gone to retrieve. A special high-security one, and yet also one which could only be opened with these three credentials (card, pin and key).

Before it’s revealed, let’s speculate. What could it be? One obvious thought is the gold, though since we know that is on Rokkenjima, probably not that. (Also, Masayuki would probably not be be operating a small doctor’s surgery out in the sticks if he had the gold!) Another speculation is something to do with the diaries. But nah, it’s gotta be something specifically related to Kinzo, right? Only he would have an arrangement like this.

Masayuki says it led him to a gigantic room full of safes. A112 was one of many associated with the card/PIN combo. There is a hell of a lot of pageantry: he retrieves a duralumin case and is left alone to open it. In there is… not the gold, but a lot of money. Around a hundred million yen, which would be about $600k in 1986 dollars, which translates to about $1.7 million today.

Masayuki, however, refused to take it because he quickly realised there was something suspect about the whole arrangement.

Ange has the same thought as me, and compares the handwriting with the mysterious Rokkenjima author calling herself ‘Beatrice’, and yep, it’s a match. We cut to an as-yet unnamed character in a blue shirt.

An amiable man in a blue shirt saying '.........Y‐......yeah. I'm pretty sure I got something like that in the mail.'

This turns out to be Kumasawa’s son, Kumasawa Sabakichi. Character info:

Kumasawa Sabakichi (熊沢 鯖吉)

Kumasawa’s son. Works at the Niijima Fishing Harbor.
He must have gotten his carefree, big-hearted personality from Kumasawa.

Kumasawa had many sons, and a number them were away from the land as fishermen.
It was purely a coincidence that it was possible to come into contact with him, as he happened to be involved with the fishing harbor.

Hopes are on him that he might have heard chatty Kumasawa let something important slip, but…

Yes, that is ‘saba’ as in ‘mackerel’ and ‘kichi’ as in ‘good fortune’, and he’s also got the same ‘mackerel’ written on his shirt. What a legend. Also, ‘many sons’… most important revelation of this chapter: Kumasawa had a lot of sex? (Nah, honestly, that’s not really a surprise.)

Anyway, Kumasawa Jr. got an envelope for Safe A113, this time in Okinawa, with the same 1-2-341-5167 address, in the far West of Japan. Anyway, unlike Masayuki, Sabakichi did not bother tracking down the bank.

Sakutarō and Mammon appear on the ‘inside Ange’s head’ level of reality to discuss with her a sudden recollection that… yep, she got such a letter too. Ange’s was addressed to Rudolf, with presumably the same procedure.

So presumably the surviving relatives all got a portion of this money. Which raises a whole lot of questions. Assuming this ‘Beatrice’ is not the murderer, but knows something about what really happened, which they can’t safely reveal… this might be some convoluted means to try and make up for the atrocity they witnessed?

Ange meanwhile is absolutely convinced that ‘Beatrice’ is the culprit and set the letters as an insult. A curse, even, says Mammon. (Though Masayuki and Sabakichi seem to have been able to get on just fine with the ‘curse’.)

Thinking hat (and thinking music) on, Ange and Sakutarō observe that this roundabout method to delay the post is less reliable than sending the letters with an explicit delivery date. (Is that a thing?) We perhaps can guess at the answer: Kinzo’s magical practice revolves around chance, so perhaps the sender intended to run the risk of a letter arriving ‘early’ as way to perform some occult effect?

Sakutarō claims to have spoken with Beatrice a little, and deems it in-character for her to take a risk. Ange recalls an anecdote from Maria’s diary, in which Beatrice explained that it’s more fun to take a risk. And also makes you harder to predict as an opponent.

Ange is given another clue: Kumasawa had a photo of a ‘western-style house’, which is one we’ve not seen before, with a statue of a lion outside. Apparently on the arch is inscribed, in English,

This door will only open with a probability of 1/1,000,000,000,000,000. You will only be blessed with a probability of 1/1,000,000,000,000,000.

That’s 1E-15 in scientific notation, or if you prefer, one in a quadrillion. Surprisingly, it seems that Kumasawa once made a serious attempt to solve the epitaph, which Ange explicitly notes has two interpretations: either it bestows the succession of the Ushiromiya family, or the succession of the title Beatrice.

In passing, Ange mentions the ‘La Plata’ (ラプラタ) theory and the ‘Enoura’ (江之) theory, which I assume are probably references to fans from the Witch Hunt group or something else? I don’t wanna search because I might accidentally get spoiled on the real solution.

Ange’s final trip takes her to the residence of the ferryboat captain, who until this point has not received a talksprite. But now he gets one! The artists have been busy.

Captain Kawabata, a middle-aged man in a cap, saying 'I remember you well...! You were only about yea high. Look at you now...! You must have been absolutely devastated. I understand how you feel, believe me...!'

The guy has an impressive memory, and we get a callback to the Battler Seasickness incident right at the start. Ange is hoping he’ll take her over to the island. Apparently very few people are willing to visit the island anymore, and they’ve taken to calling it by its own name, Akujikijima (悪食島), literally ‘Bad Food Island’. Quite convenient since it means Rokkenjima will largely be untouched…

One thing I’m noticing here is the way class factors into how these minor characters are depicted. If Masayuki the doctor is kind of stuck-up, fishermen like Sabakichi and Captain Kawabata are depicted as real cheery salt of the earth type of guys.

So, twelve years after the Rokkenjima incident, and in the real world, more than eight years after I began this liveblog, it’s time for Ange to go to Rokkenjima and get some answers.

Ange is not planning to do a full investigation of the island—just to stay there for a few hours in the twilight and get some closure. (‘Closure’ here appears to be ‘心の整理’, something like ‘put one’s heart/soul in order’).

Interestingly, Kawabata actually has positive things to say about Kinzo, saying he was paid well back in the day and he’s glad to be able to finish his final job. Here’s what the character screen has to say about him…

川畑船長

An old man who used to captain the high-speed boat that went to Rokkenjima and back.

It is is beyond doubt that of the existing survivors, he is one of those who know the most about Rokkenjima.

From various comments by the servants who used to go to and from the island, the media hoped that he might be quite knowledgeable about the internal affairs of the Ushiromiya family.

However, he would say nothing, and so they eventually began to forget about this old man…

Surprisingly, we get two whole extra backgrounds just for Ange exiting the futon shop where Kawabata lives. He offers to let her and Juuza stay the night. Suspect there might be an action scene coming up here?

The background of the futon shop, with lots of different patterns and colours of futon stacked on shelves.

The captain divines that Ange had been considering suicide on the island and indirectly tells her to not to, i.e. to let him take her home again even if he couldn’t bring her family back. Ange promises she will. But as she goes…

It’s time for the twist. Ange sees something that only she can see—not even her headmates. A butterfly? Whatever it is, it provokes a little speech about fate. We get several shots of all four visible and invisible characters reacting in confusion and a track of gradually building arpeggios… and then the chapter ends. FINE. Keep us in suspense, Ryuukishi. Gives me motivation not to take another huge hiatus…

Maybe she just saw a really nice futon.


OK, so, about the screenshots issue. As interesting as it would be to hack OnScripter-RU (and I’m not saying I won’t get involved in the Umineko Concerto project down the line, since it seems like they have need of graphics programmers), the built-in screenshot cropping tools combined with copy-pasting text from the English script is making taking screenshots considerably less painful. Liveblogging this chapter took only an hour or so (not counting the part where I wrote up the whole ‘trying to compile OnScripter’ thing). So I think, shelve that project and see if I can press on to the end of this chapter.

Any major new observations from this chapter? Well, we got an answer to the mysterious eight-digit string 07151129 from the previous episode. It’s… a PIN for a safe. I feel like, given the specific number has been shown on screen multiple times now, that can’t be all it is.

Why would it be painted during the ritual on Rokkenjima in Episode 3? Well, if we are going with the theory that the murders on Rokkenjima are a fiction concocted by someone to cope with the trauma of witnessing the Rokkenjima murders, and it really is just a bank account PIN, then the answer is actually that the author inserted this number in one of the stories they wrote, so the intended recipient of the number is not the characters on Rokkenjima, but the reader of the story. Perhaps they were trying to make sure someone got to access the money in those safes. But without the associated card and key it’s kind of useless, so this isn’t a great theory.

So maybe this choice of PIN has some further significance to Kinzo and his affairs…

Other than that, well, I guess we’ll have to wait a few chapters to find out what Ange saw that convinced her this was all fate. See you… dare I say soon? Haha, well, it’s no fun if it’s certain, right?

Comments

Add a comment
[?]