Extracting Visual Novel Resources

A guide to ripping the files from ヤンデレな彼女に死ぬほど尽くされる.

Maria is your childhood friend, a well-behaved proper young lady.
She dreams of marrying you, waking up to you, cooking your meals with love.
But in reality she's an insane princess with insane emotions!
--Game Description

Overview
This paper is a practical tutorial to reverse engineering a Visual Novel. Reverse engineering video games is usually done to cheat, create mods, or defeat copy protections. Visual Novels are a type of story driven game with light animation. If you haven’t play one before, just imagine the dialog in an RPG, but it goes on for the entire game with little or zero actual gameplay. Visual Novels are unique in that they attempt to prevent the player from seeing all images, sounds, and scripts ahead of time. Players must unlock the art assets by playing the game, and then later they can view the assets in a gallery. Also, the visual novel in question is very much NSFW; you have been warned.

Unpacking
Unpacking the data from the game was pretty easy, since the game used a well known engine, KiriKiri, and so had a standard resource file, XP3. I just ran arc_unpacker, with the following command.

arc_unpacker --dec=kirikiri/xp3 \
             --plugin=fsn \
             --out=C:\Users\Me\Desktop\dump data.xp3
$ ls | head -5
back_base.png
back_base_over.png
back_extra.png
back_gallery.png
back_load.png

Too bad none of the files could be opened.

Breaking Simple Crypto

Since the files weren't opening in any viewer, I tried using the 'file' command to see what was in them. It couldn't recognize anything either. The file names weren't mangled, so I knew what the files were supposed to be. For example, I knew 'nc001a.bmp' was supposed to be a BMP file and 'back_title.png' was supposed to be a PNG. Below is part of my command history. The ‘back_load.png’ file is obviously supposed to be a PNG, since it has the PNG file extension, but the ‘file’ command can’t recognize the contents.

$ file back_load.png
back_load.png: data

Certain file types start with magic bytes to identify what type they are. I knew that PNG, Ogg, and BMP files had unique magic bytes, so I tried comparing the expected bytes with provided bytes. I used a '.ogg' file for my first test.

Every '.ogg' file starts with the ASCII characters 'OggS'. The first four bytes of 'bgm001.ogg' were instead 'yQQe'. This was encouraging, since the same characters were repeated. 'OggS' has the same two middle characters and so does 'yQQe'. This suggested that whatever change was being made was per byte. I tried displaying the characters in binary to see if I could find a pattern.

Here's the character 'y' and the character 'O' compared.
>>> "{0:08b}".format(0x79)
'01111001'
>>> "{0:08b}".format(ord('O'))
'01001111'

I've marked below where the bits differ.
'01111001'
 __XX_XX_
'01001111'

Here's the character 'Q' and the character 'g' compared.
>>> "{0:08b}".format(0x51)
'01010001'
>>> "{0:08b}".format(ord('g'))
'01100111'

I've marked below where the bits differ. You'll notice the bits differ in the same place.
'01010001'
 __XX_XX_
'01100111'

Here's the character 'e' and the character 'S' compared.
>>> "{0:08b}".format(0x65)
'01100101'
>>> "{0:08b}".format(ord('S'))
'01010011'

Once again, the same bits have been flipped.
'01100101'
'01010011'
 __XX_XX_

Flipping select bits would be possible by XOR'ing a certain value. Since the changed bits are the same every time, and the file seems to be encrypted byte by byte, I should be able to just XOR every byte in the file with the same number. XOR'ing a byte with the same number twice gives back the original value.

I mapped this function onto each byte of a file.
CONSTANT = int('00110110', 2)
def transform(byte):
    'Transform a byte back to what it originally was.'
    return byte ^ CONSTANT

Now the Ogg file was starting to look correct. Not only were the magic bytes better, I could see other valid strings in the hexdump. The file still wouldn't open though. After playing with it for a while I realized I was in over my head and attempted the same process with a PNG file.

Getting a valid PNG

I attempted to map the same transform function onto one of the PNG files. This fixed the magic bytes and also made the rest of the data in the hexdump look reasonable. I didn't even have to change the number I was XOR'ing with.

I could open the PNG, but it didn't look right. At the top was a single horizontal line of color, and then the rest of the image was black with a trippy skewed outline of the title screen. I was only able to include a screenshot of the broken image in this document. The actual broken image would not paste correctly.

I thought that maybe something in the header had been changed to mess up the image. After poking at the image a bit, I noticed the dimensions were sort of weird. The file was 801x600. Because 800x600 is a typical resolution, I tried decrementing the width. It worked.

The same technique works on many other images as well.

Future Work

There’s still much to be done. The Ogg files I started with are still scrambled and the tricks I’ve used are only useful for fixing some images. There’s still a lot of sex scenes that are so jumbled they look like smashed fetuses. 

Feel free to replicate this work with your own legal copy of the game. Maybe you’ll be the one to fix the sound files!