iStatistician 2.7 is out

by acha11 16. May 2009 06:24

If you race on iRacing.com, then you can use iStatistician to chart your performance over time.

Until the release of v2.7, iStatistician was horribly broken for a couple of weeks. Sorry if you were one of the affectees. Briefly, iStatistician reads your driving record straight from the iRacing.com website, and since iRacing.com was re-organised fairly extensively a couple of weeks ago in the inter-season break, my app was looking for all sorts of information in all the wrong places.

Unfortunately, there’s not a lot I can do to protect against this kind of occasional breakage, without talking to the iRacing.com team to get access to some kind of test environment so that I can upgrade iStatistician ahead of any major breaking releases. Obviously, there’s no guarantee they’d be up for that, and also, I’m not sure having two weeks warning (say) would actually mean I’d find the time to make the necessary modifications in time for a website upgrade :-).

While iStatistician has its fair share of horrible, crufty, brittle web scraping code, there are also a few nice elements I should write up at some point. It’s a WPF app running on the user’s machine, using NHibernate over a SQL Server Compact Edition database for persistent storage. There’s some use of LINQ over the domain model, and a nice charting API (with bitmap export, zooming scrolling, etc.) that’s quite LINQ-friendly too, and moderately reusable.

All that’s below the surface though, I guess. The whole point is that you can generate charts like the one below, which tells me that while I have been improving just a little over time, Sunday at 11:15pm is not the best time for me to race. Apparently.

image

A trick to extend the un-extendable

by acha11 8. May 2009 01:29

Update 2009-10-29:

iRacing have implemented a check to restrict use of this technique... more here.

 

This post’s about a nice trick I came across to inject your own code into a third party app that’s not designed to be extensible.

The app in question is iRacing, a racing simulator. The extension is Stephane’s Telemetry HUD Plugin, which adds extra timing and telemetry info as a HUD on top of the standard 3d view that iRacing renders.

iRacing without HUD

iRacing old-school

 

iRacingSim with HUD

iRacing + Telemetry HUD (note the lap times in the top-left and RPMs on the top right)

The Telemetry HUD Plugin ships as a single DLL, d3d9.dll, which you copy into your main iRacing program folder (the folder which contains iRacing.exe).

When I first saw this arrangement, I assumed the trick worked like this:

  1. iRacing asks to load the “real” Direct3D d3d9.dll.
  2. Windows applies its defined search orderfor DLLs, and decides to load Stephane’s d3d9.dll (since it’s in the local app folder), instead of the “real” dll that’s off in %windir%\system32.
  3. Stephane’s d3d9.dll somehow (maybe by calling LoadLibrary? Hey, I’m not Stephane, alright?) causes the real d3d9.dll to be loaded as well so that iRacing can still get at the Direct3D functions it needs. (But what about naming collisions? Is it even possible to have two DLLs with the same name loaded? Answers below…)

I wanted to find out whether my hunch was on the right track, and how the details really worked. So first I fired up Dependency Walker and loaded iRacingSim.exe (the main iRacing executable) while the plugin was installed:

image

The top-left panel lists the DLLs that iRacingSim.exe depends on. I’ve highlighted D3D9.DLL – notice that it’s loaded from the iRacing folder, since I’ve got the Telemetry HUD Plugin installed, and Dependency Walker is smart enough to apply the same DLL resolution rules as Windows.

Because I’ve highlighted D3D9.DLL, the second-from-the-top-on-the-right panel is listing all the functions it exports, and the top-right panel is listing just those exported functions which are actually called by iRacingSim.exe. We can see that D3D9.DLL only exports Direct3DCreate9(), and that’s the only function which iRacingSim.exe actually calls. Contrast that with a view of the “real” D3D9.dll:

image

Here you can see that the real D3D9.DLL exports a whole bunch of functions (in the panel second from the top on the right), but Dependency Walker still says that only one of them is actually called by iRacingSim.exe.

So at this point, it looks to me as though Stephane either:

  1. chose to fake a DLL that iRacingSim.exe called very few exports from (saving time and effort); or
  2. chose D3D9.DLL because a call to Direct3DCreate9() was a convenient time to hook in and do his own initialisation (since by that point, he knows that the graphic subsystem is being initialised).

One question remains: When iRacing calls Direct3DCreate9() in what it thinks is Direct3D but is actually Stephane’s fake DLL, how does Direct3D actually get initialised? Does Stephane just pass the call through to the real D3D9.DLL? Fire up Dependency Walker and load the Telemetry HUD Plugin d3d9.dll:

image

Well, we can tell from the tree at the top left that Dependency Walker can’t see a dependency from the fake D3D9.DLL to the real one. Maybe Dependency Walker can only see static, declared (declarative) inter-DLL dependencies, though – perhaps if Stephane’s code imperatively sets up the dependency by calling LoadLibrary, passing a path to the real D3D9.dll, he can then call to the real thing? Maybe we could check that by checking the string table in his DLL?

Fire up devenv.exe and open the fake D3D9.dll using the binary editor. Ctrl-F to look for occurrences of “d3d9” – maybe we can find some clue as to how the real D3D9.dll gets loaded?

image

That’s a pretty interesting string – “\d3d9.dll”. And immediately before and after it, there’s talk of something called PROXYDLL, which looks a bit library-ish. Google is our friend, and finds us “Intercept Calls to DirectX with a Proxy DLL”. That’s an article on CodeGuru, including sample code, which uses a pretty familiar-sounding technique to inject code into a Direct3D 8 or 9 app and render extra stuff on the screen.

After downloading the sample source, I run into the following chunks of code…

void InitInstance(HANDLE hModule)
{
    OutputDebugString("PROXYDLL: InitInstance called.\r\n");

Well, given that “PROXYDLL: InitInstance called.” shows up in the binary screenshot above character for character, I’m pretty sure that Stephane’s work’s based off this sample, so we can be confident the D3D9.dll technique used by the sample’s also used by Stephane’s fake D3D9.dll.

// Exported function (faking d3d9.dll's one-and-only export)
IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion)
{
    if (!gl_hOriginalDll) LoadOriginalDll(); // looking for the "right d3d9.dll"

Here we go, how does LoadOriginalDll() work?

void LoadOriginalDll(void)
{
    char buffer[MAX_PATH];
    // Getting path to system dir and to d3d8.dll
    ::GetSystemDirectory(buffer,MAX_PATH);

    // Append dll name
    strcat(buffer,"\\d3d9.dll");
    // try to load the system's d3d9.dll, if pointer empty
    if (!gl_hOriginalDll) gl_hOriginalDll = ::LoadLibrary(buffer);

    // Debug
    if (!gl_hOriginalDll)
    {
        OutputDebugString("PROXYDLL: Original d3d9.dll not loaded ERROR ****\r\n");
        ::ExitProcess(0); // exit the hard way
    }
}

And there’s the resolution (see what I did there?): a quick call to GetSystemDirectory tells us where system32 is, and we ask LoadLibrary to load d3d9.dll from that folder.

None of this was insanely complicated, but it’s something I wouldn’t have ever tried, I think.

For starters, I would have been sceptical that it’d be so easy to subvert the DLL resolution rules for what is effectively a system library. Having said that, I see how there’s no sensible measure to prevent a user running whatever code they want on their machine, of course.

Second, I’m really surprised that iRacing, an online multiplayer game (where you’d expect some cheat/hack countermeasures on the part of the developers), doesn’t have parasite detection code that picks up either unrecognised or suspicious-looking DLLs, or at least funky stuff like “hey, wait a sec, I have TWO d3d9.dlls loaded in my process space! uncool!”.

It’ll be interesting to see how iRacing respond to this plug-in. Traditionally, iRacing have patched their software only in the one week break between quarterly seasons; only the most egregious bug exploits (for example, cuttable corners on the track) have been patched “out-of-band”. Given that the inter-season break was last week, and that Stephane released his tool a few months back, it looks like they’ve chosen to tolerate his hack-let for now.

This tool is grey-hat. It surfaces information to the driver which otherwise isn’t available: extremely detailed and precise, real-time feedback on whether you’ve fallen behind or pulled ahead of your best lap ever up to this point on the current lap – incredibly useful, with overtones of mild cheatiness.

My concern is that it’s not that far from here to a genuine black-hat tool which observes your acceleration and accelerator levels, reasons that you must be spinning the rear wheels, and eases off the accelerator by filtering the values fed from your wheel and pedals to iRacing (using a similar technique to that found above).

That class of hacks would basically ruin the key characteristic of iRacing that makes it the greatest ever online racing league: a competitive and even playing field populated by (almost universally) non-cheating (mostly) non-idiots.

Page boundary crossings on the 6502

by acha11 24. August 2008 01:45

I'm implementing a 6502 emulator in C# as part of a project to emulate the Nintendo Entertainment System.

I've implemented all the 6502 opcodes, and now I'm going back through and implementing cycle counting, so that each instruction consumes the correct number of clock cycles.

On the 6502, some instructions take one extra cycle if a "page boundary is crossed". Different references use slightly different wording, but none which I've found so far really clearly specify exactly what constitutes a page boundary crossing. Take Post-indexed indirect addressing:

The CPU takes the byte following the opcode. The CPU goes to the specified zeropage address and reads a word. It adds the value of the Y register to that. It reads a byte from the location specified by the resulting 16 bit value.

  1. There could be a page boundary between the opcode and the operand.
  2. I could cross a page boundary going to the zero page from the operand (or the opcode, or the start of the next instruction).
  3. The word I read there could span a page boundary by starting at address 0x**FF.
  4. Adding the Y register to that word could cause a page boundary crossing.

Of those, only 3 and 4 seem likely candidates for the "page boundary crossing" we're interested in.

Reading 64doc,  the section "Absolute indexed addressing" describes what the CPU's up to at each cycle during one of the absolute indexed addressing read instructions. It implies that the "type 4" page boundary crossing in the list above is the type that causes the CPU to take an extra cycle for the instruction: if adding an offset to an address causes a page boundary crossing, then the instruction takes 1 extra cycle.

This model has good explanatory power too. The LDA (load accumulator) instruction has 6 opcodes, each for a different addressing mode. Their cycle timings are 3, 4, 6, 5+,4, 4+ (where '+' means an optional extra cycle if a page boundary is crossed). The linked document above says that the 6502 optimistically fetches from the calculated address assuming there was no page boundary crossing, and then spends the extra cycle, if necessary, re-fetching from the correct page if it guessed wrong. Look now at the STA (store accumulator) instruction. Its cycle timings for the same addressing modes are 3,4,6,6,4,5. Notice that the timings are identical, except that we always get charged the extra cycle. Because STA writes to memory, the CPU can't optimistically write to its best guess of the desired address, and then write again to the correct address if it turns out it guessed wrong; very sensibly, the store instructions always wait for the address to be calculated with certainty before going ahead and writing to the bus.

Tags: ,

Twilight princess

by acha11 21. January 2007 01:50

first up: spoilers. of course.

i just completed the legend of zelda: twilight princess. it... uh... took me more than 52 hours according to the in-game clock, which would make it the longest adventure game i've ever played through. actually, twilight princess is the first zelda game i've completed; for however many years, i've had a link to the past, ocarina of time and wind waker gathering dust on a shelf, each somewhere around two-thirds complete.

i think the reason that i've finished twilight princess is fairly simple: someone involved in the design process made a conscious decision that they wanted players to finish the game. midna, for example, gives advice during boss battles at the slightest sign of trouble. even towards the end of the game, when you'd normally expect the difficulty curve to rise a little, she'll literally call the next action the player must take - not the weakness to be exploited, mind, but the action needed to exploit it. the result is that you get a feeling of being led by the hand through the game.

i want to say that if anything the designers have erred on the side of over-friendliness in this and other cases - that by smoothing (almost) every sharp edge, they've made the experience less memorable. after all, the best gaming moments are those where you overcome ridiculous adversity single-handed through sheer bloody-minded force of will, dexterity, strategic planning, preparation, speed of reflex, etc.

but it turns out i don't believe that. on the one hand, link to the past and ocarina of time would both be on my list of top 10 or 20 games. on the other, most of my memories of those games are of frustration. where's the next dungeon? (hour of random wandering later:) i've found it, now how do i get in? why do i have to clear this room of the same six monsters for the ninth time? how do i damage this boss? where are all the pieces of heart? if i'd had just one more, i might have been able to kill that invulnerable boss, right?

the reason i finished twilight princess it that there's effectively none of that frustration. your path is largely signposted (literally in many cases with flashing beacons on the map); there are very few unique techniques to be mastered to, for example, get access to a dungeon, and those that are necessary are explained clearly; where rooms full of monsters do regenerate, they're generally of the one-hit-kill variety; midna calls out with boss strategy tips at the slightest sign of need; a fortune teller will show you an in-game shot of the neighbourhood of each piece of heart.

and, yeah, sure, that makes the whole thing a lot easier. the sensation of being "stuck" is basically absent. so, what purpose did that sensation really serve? why were designers evoking that response, and is missing out on that sensation going to curve my spine and stop us from winning the war (ref: george carlin)?

purpose one: prolonging the game. back when games sold fewer copies (but did they really?), budgets were smaller, so less content could be produced. also, cartridge sizes (SNES and N64) constrained the amount of content that could be delivered. so slowing the player down with frustation was necessary historically to ensure players felt like they were getting their money's worth.

now, i don't think that was the intention during either OOT or LTTP's design; they're both games that were innovative and/or revolutionary for their time, and the majority of their innovations improved the player's lot by smoothing out the types of jagged edge i talked about earlier.

however, i do think that one of the big arcs in the history of gaming is a move away from evoking feelings of frustration in the player, and that LTTP, OOT and TP are each points on a curve of increasing player-friendliness.

purpose two: gotta be cruel to be kind. gotta feel the terrifying lows to really experience the giddy highs (not forgetting, of course, the creamy middles). gotta be frustrated to later feel satisfied. hunger is the best sauce. sweet anticipation.

this is where, for me, the jury's still out. the sense of satisfaction i have right now having completed TP just isn't that great. it's certainly nothing compared to the day i got under 0 seconds on the Grand Prix Legends handicap, which set a "par" time for a hot lap around each of the 1967-era grand prix circuits in the game according to the standard set by the game's developers. gpl is a brutal, uncompromising driving simulator; while it's possible to criticise its realism (something even its lead developer has done, saying it feels to him like driving on ice), the world ice-driving circuit it does simulate is one in which you can invest a huge amount of effort improving your skills to achieve a certain standard. the difference between GPL and TP is that back when i would happily spend two hours polishing my line around a single track, i knew that there was a risk that at the end of two hours i wouldn't actually be any faster; that i might tweak some aspect of my style, find that it wasn't an improvement, and then somehow be unable to find my way back. in TP, i didn't have much feeling of risk that i wouldn't progress - x hours invested translated pretty consistently to y completion percentage points, with a very small standard deviation.

a related point is the difficulty curve. TP's level of difficulty (in terms of dungeons, combat, and boss battles) really didn't seem to ramp up very much on the way through the game. the challenges i was overcoming later in the game were only feasible because link's capabilities had improved (due to new items), not because my skill in deploying those capabilities had improved.

the whole thing's kind of depressing. is TLOZ turning into WoW? a noob with a freshly purchased level 60 character will destroy the world's best WoWer playing a level 30 character, right?

anyway, all that aside, i still think that TP's the best game i've played for a couple of years at least, and i definitely believe it's the best game in the Zelda series. definitely recommended.

Tags:

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

RecentComments

Comment RSS