December 10, 2015

Still Alive

I'm still alive.  The last six weeks have been an ordeal, but I'm still around.

I went back to work last Monday and have been working on finishing up Electric Eye since then.

I finally got Yvonne's certificate of death on Monday and am going through all the paperwork that has been waiting for that document to arrive, like her life insurance policy and some dealings with her attorney.

My granddaughter is coming out to visit on December 17 and will be here through Christmas.

Right now, I'm planning on starting posting and updating things again after the first of the year.

November 3, 2015

Yvonne Russell, 1952-2015

While I was at the office on Thursday, October 30, I got a panicked call from my wife telling me that she had lost her balance outside while putting up Halloween decorations, had fallen, had broken her wrist and that she was heading to the emergency room at Good Samaritan Hospital in Los Gatos.  I managed to get there quickly and spent six hours with her in the ER while a doctor reset an impacted radial fracture and applied a splint.

I had a rehearsal at work Friday for a tech talk I was going to be giving on November 5 and was going to be able to duck out afterwards to take care of my wife, so I made sure my wife was comfortable and went into the office.  My last words to her were, "Love you.  See you soon.  Rest."  It was the last time I saw her awake.

Shortly after my meeting, I got a call from my neighbor telling me to get home as quickly as possible.  She had found my wife collapsed on the front porch, and the fire department was there trying to revive her.  Nobody knows how long she was down for.  When I got home, two policemen met me and told me that she had been taken to El Camino Hospital in Los Gatos.

When I got to El Camino, it was a flurry of paperwork and disclosures before I was able to get back to the ER.  Everyone briefly panicked when I mentioned she was allergic to latex.  Turns out she had collapsed at home due to a heart attack, had been resuscitated at home, was rushed to the emergency room, and she had three more heart attacks in the ER.  I was escorted to the "meditation room" to wait for news, which was a small room with a box of tissue, no trash can, and a ton of religious pamphlets about prayers for loved ones you are going to lose in a variety of religions.  Hardly an optimistic moment.

They took my wife for a CT scan, then wheeled her over to the ICU.  They finally let me in around eight hours later.  They were sending her through hypothermia protocol, which has been shown to help people recover after a heart attack.

Once she completed the protocol, her heartrate was a bit high and slightly unstable, but she was holding her own blood pressure.  A respirator was helping her breathe.  There was hope that she'd be okay.  I was told to go home and get some sleep.  I needed to take care of myself.

On Sunday, I was told that the neurologist was holding off on an examination until Monday to give my wife a chance to wake on her own.  My wife looked like she was sleeping restfully for the first time in a long time.  Most nights, she woke up with horrible nightmares, or kicked me in her sleep in a desperate attempt to get away from the monsters that haunted her in her restless dreams, but she looked calm.

I work three blocks away from the hospital, so I told the doctors to call me if there was any change in her status since I could be there in fifteen minutes.  I went into work on Monday so I could work on Electric Eye since in my (less-than-restful) night's sleep the previous night, I had an insight about how to solve the last major roadblock in development.

I got a call at 11:30am from the neurologist.  I was told that my wife was still in a coma and fully reliant on the respirator for breathing.  They had tried to turn down the respirator, but her body wasn't responding or helping her breathe anymore.  Her eyes showed no response to light stimuli.  In the doctor's opinion, she suffered severe brain damage due to hypoxia.  An EEG was going to be scheduled for the same day to confirm the diagnosis.  My boss at Netflix was nice enough to let me go on leave through November to take care of what I needed to do.  I went over and waited by her side, but even though she wasn't under sedation, I didn't see REM sleep.  I didn't see nightmares.  I didn't see dreams, either.  I saw nothing.

During all of this, I was trying to keep friends and family updated via Facebook.  My son-in-law was covering for me on phones to give me some distance, which I appreciated.  It was hard enough to keep it together without dozens of family members bombarding my phone day in and day out.  I can talk to people I know about this kind of stuff easily enough, but every time Yvonne had dealt with her family, it usually ended in her getting upset and fighting over something trivial, so I never bothered trying to get close.

I was told to be at the hospital at 11am on Tuesday to meet with the neurologist.  I spent the remainder of the evening trying to keep it together by finding small projects around the house and working on them.  Fill the trash can.  Sort and start a load of laundry.  Click on this boss for 30 seconds in "Clicker Heroes."  Fold and put away the laundry.

Overnight, I kept flashing back to end-of-life discussions that I had with my wife.  Back during the Terry Schiavo debacle, we'd had a fairly lengthy discussion about her fears.  She told me that she didn't want to be kept alive by a machine.  To her, the thought of being dead but having her body remain around scared her beyond words.

In the end, both her EEG and a follow-up CT scan showed absolutely no brain activity.  Due to how organ donation works here, I won't have an official time of death for a bit, but she is gone.

So now I'm a widower.  It's an odd word.  It almost seems inappropriate.  A widow is a woman who has lost her husband.  A "widower" sounds like someone who creates widows...not someone who has lost their wife.  It's not exactly easy to say without sounding like you're butchering your pronunciation of "littler."  Regardless of my opinion of the word, it's the appropriate label at the moment.  It's a new label in my life, and it will take some time before it seems like it fits.

I may not have always gotten along with my wife.  I've lost track of the number of times when I told her that I loved her, but I hated the things that she did.  The past several years have been increasingly hard as her health regressed and her mood went with it, but even so, I loved her.  Now she's gone, and there's a twenty-year-plus void in my life that will never be filled or forgotten, but will hopefully, eventually, and mercifully be adapted to.

Farewell, Yvonne.  For someone who loved living life as if it was a roar, I'm sorry you had to go out with a whimper.

In lieu of cards or flowers, please make a donation to Doctors Without Borders.  Yvonne lived a full life due to excellent medical care by well-trained caregivers, and this is the surest way to ensure that others are able to do so as well who wouldn't be able to do so otherwise.

[Update 11/14: A Redditor pointed out that I typed 2013 instead of 2015 in the title of this post.  The title has been updated, but the old year remains in the permalink.]

October 5, 2015

No Internet At Home Makes Rom Something Something...

I just finished a weekend suffering from not only a bad case of food poisoning, but also little to no internet connectivity.  My internet connection went down Saturday afternoon, came back for a little bit Sunday evening, and it went down again this morning at around 12:20am.

It's very difficult to work at a streaming movie service and also suffer from an unstable internet connection.

Competition can't come to this area soon enough...

[Update: 10/5, 6:48pm] After a total of five hours of being on hold and being hung up on seven times, I finally got through to a customer service rep who had things fixed in a few minutes.  She proceeded to blame the modem that they assigned me for the problem.

Joy.

September 22, 2015

Electric Eye - The Post Postmortem

If you haven't read the Netflix tech blog yet today, you might want to wander over there before reading this post.  Yep, the project I created and am acting as lead on at Netflix is called Electric Eye.

The project started ~6 months ago as a one-off experiment and has exploded beyond anything I thought could happen because the damned thing kept working even when I thought it wouldn't.  It has repeatedly triggered my Impostor Syndrome, making it ramp up to 11 from time to time, because while I had to test computer vision projects before, and had even written a few, I felt that I was at sub-novice level for most of what I've been doing.

I don't want to talk about the project in this post, though.  I would like to talk about why this post was written the way that it was.

First, you may be asking why there isn't any code yet.  Well, we've been working with legal on the code release.  Almost all of the code is going to be open sourced except perhaps one item that I can't talk about yet and some code to hook this into our backend for test reporting.  However, that will include my highly optimized frame transformation system, all of the CV and audio DSP helpers I've developed, and possibly ~25 full CV test cases.  There's still some refactoring and code cleanup to be done, but I'd expect a code release either late next spring or summer.

Second, you may be wondering why I link to libraries and, specifically, certain functions in the libraries.  This is actually a direct result of interactions with a former employer.  No, they didn't make me not release code.  They showed me that not every employer will let their test teams use open source unless the code is released under a BSD license or something even more lenient.  I intentionally wrote the post so that a tester in a restricted environment could find the information necessary to implement a variation on what I'm doing on their own.  After all, if I can tell you how to detect a pattern and transform the image to get you a testable frame, that's most of the challenge right there and if you do it on your own, your legal department will have no issues outside of vetting your library licenses.  If I give you the code to do it at the same time, then your legal department may freak out.

Finally, you may be wondering why the blog post is out before the code.  We're trying to hire some great SDETs and every candidate we've spoken with has been extremely excited by this project.  Showing off some cool testing tech is a great way to grow awareness of QA inside Netflix and does wonders to raise interest levels.  The sooner we get some great SDETs in here, the better.

We got the final logo (the blue eye) yesterday morning, but I've been pulling screenshots all through the development process.  The screenshots in the article range from 1-4 months old.

Well, back to work.  Who knows...we might end up doing an AMA somewhere about it.

September 20, 2015

First Episode of Terraria Inverted Live


This series uses RomTerraria 4 v0.9.

Preview of RomTerraria 4 v0.10

Right now, I've got about 2/3rds of the UI properly respecting these widgets (everything in the new UI added post-1.2 and everything right/bottom docked) so I might miss some, but get ready...


September 19, 2015

RomTerraria 4 v0.9 Released

Main post here.

Two updates in less than two weeks.  What the hell is going on here?

In this build:
  • Most common complaint about Terraria from left-handed players is that Re-Logic didn't give them the ability to swap mouse buttons.  I've given you that ability now.

    Toilet Oddity

    I was at the airport earlier today and needed to use the restroom.  There was a single stall open, and when I went in, I noticed that the seat was soaked.  "Damn," I thought, "I hate it when people piss on the seat."

    I grabbed a lot of toilet paper and cleaned off the seat as best I could.  I did my business, stood up, and noticed that when the auto-flush triggered, it caused a ton of water to splash up out of the bowl onto the seat.

    I simultaneously felt three things.  First, I felt guilty for blaming the previous occupant for pissing on the seat.  Second, I felt disgusted because it was obviously a lot worse on the seat.  Third, I felt astonished that a toilet could fail that spectacularly at doing what it was supposed to do: dispose of waste.

    To keep this from happening to you and your fellow man in the future, lift the seat before flushing.

    There's your moment of WTFery for the day.

    September 17, 2015

    No Posts Until Saturday

    You'll hear very little from me until sometime in the afternoon of September 19.

    Oh, and I'm going to have a post go up on the Netflix Tech Blog sometime on September 22.

    September 14, 2015

    Hosting Issues

    Of course...I upload a new version of RomTerraria, and my hosting provider takes a shit.

    Things should be corrected by morning.  If they aren't, I'll set up a mirror on a different server.

    Sorry for the inconvenience.

    (Update: Hosting is back up.)

    September 12, 2015

    RomTerraria 4 v0.8 released.

    Main post here.

    Exactly two months since the last update, here's an update.

    In this build:
    •  If you were having problems with RomTerraria finding your copy of Terraria, guess what?  You're in luck.  If I can't find it, I'll pop up a file browser now.
    • Like the Gravity Globe, but don't want to have it taking up an accessory slot?  You can turn the Gravity Globe on all the time now.  Thanks to GamingWithRyan for the idea.

      A Little Sneak Peek

      If you like Gravity Globes but don't like losing the accessory slot, the next RomTerraria release will interest you.

      September 11, 2015

      End in Sight

      Today is the final demo of my project at work.

      I've been working on a computer vision project at Netflix where we're now able to point a standard, consumer grade webcam at an off-the-shelf TV and with less than 10 seconds of calibration, I can convert the image in real-time into something that you can run bog-standard, computer vision algorithms against, with the camera off by up to 45 degrees in any direction..  In other words, you can point your camera at a TV from above and to the right, and I'll still be able to perspective-correct it back to normal and give you something to work with.

      That was fun enough, but the really fun part was getting it so that I could do the same with curved televisions regardless of curvature without being told that the television has a curve.







      A ton of extra work has been done to ensure the returned frames are of good quality.  Emissive screens are a beast, and LED-lit screens have a flicker that you don't notice otherwise.

      I'm working on a blog post for the Netflix tech blog that'll have a lot more information and we'll be open-sourcing it sometime next year, but I'm feeling awesome about this and can't wait to tell people more information.

      I've got the afternoon of September 12 set aside for RomTerraria.  Can't wait to be finish it.

      August 27, 2015

      Busy

      Sorry for my absence.  I've been preparing for a big demo tomorrow at work, and if that goes well, an even bigger demo on September 11.

      Lots of C++ code, which has been difficult because I'm out of practice.
      Lots of OpenCV work, which has been difficult because I'm out of practice.
      Lots of really psychotic 3D math, which has been difficult because I'm out of practice.

      As you can tell, I really need to practice more.

      To quickly reply to lots of comments I've been seeing that I haven't published yet...

      1) If you are using RomTerraria 3 with the Steam version right now, you're doing it wrong.
      2) I still haven't published RomTerraria 4 to Github.  I know.  I'm a dick.  I'm trying to get Mac/Linux support in for 1.3.0.8 before it goes up.
      3) There are four different Elexis Sinclairs in "SiN Episodes: Emergence," and not all of them share the same goals.

      July 25, 2015

      RomTerraria v4 and Mac/Linux FAQ

      (updated 9/21)

      Will RomTerraria work with the Mac/Linux versions of Terraria introduced with 1.3.0.7?

      Not at this time.  Soon.

      Are you planning on making RomTerraria work with the Mac/Linux versions?

      I'm currently working on it.  When it's done, you'll need to copy your Mac/Linux executable over to a Windows machine and patch it.

      Do you have an ETA?

      Not at the moment.

      July 19, 2015

      RomTerraria 4 v0.8 Feature List

      Still working on the next update while my ankle recovers.  As I get things working, will add them to the list here so you'll know what's in the next update.
      • In windowed mode, you'll no longer be restricted to the size of your primary desktop.  However, if you stretch your window so that it is larger than 8192 pixels in any direction, the game may crash.

      July 13, 2015

      Update on RomTerraria Ultra-High Resolution Support

      If you are still crashing with resolutions higher than 4096 pixels in any one direction (for example, 5760x1080) after updating to 1.3.0.5, I'm sorry.  I've put in as much troubleshooting as possible without being able to reproduce the issue in-house...which is my next step.

      Right now, I'm running on a 2560x1440 monitor with a GeForce GTX 760.  I'm going to see if I can get another two monitors attached to this guy this week in hopes of breaking the ultra-wide resolution barrier and I'll see if I can reproduce the issue under a debugger then.

      It's currently looking like a write-after-free bug with a render target.

      If anyone can attach a debugger to their Terraria.exe process patched to 1.3.0.5 with v0.7 before then and can get me any detail, I can work on patching it, but until then, I'm stuck.

      Sorry for the delay.

      July 12, 2015

      RomTerraria 4 v0.7 Released

      Taking care of a sick friend this weekend has pushed a lot of stuff back.

      Download link on this page.

      In this release:
      • Option to force Terraria to catch CorruptedStateExceptions.  This option adds the HandleCorruptedStateException and SecurityCritical attributes to Terraria.Program.LaunchGame.
      • Added tooltips to currently enabled options.

      July 10, 2015

      Trip This Weekend; Updates Sunday Evening

      Almost done getting RomTerraria's code cleaned up for release on GitHub, but may not have it done tonight.  The only issues that I'm aware of right now are that people who are using RomTerraria on certain multimonitor configurations are crashing out without a stack trace.  Enabling the legacy corrupted state exception mechanism should have worked to let me diagnose that, but it turns out that it didn't.  I'm now looking into injecting exception handlers into methods to try to help with diagnosing.

      I've got an unexpected trip down to Yosemite tomorrow morning, but I'll be back on Sunday.

      July 9, 2015

      RomTerraria 4 v0.6 Released

      Main post here.

      In this build:
      • Updated patcher to work with Terraria 1.3.0.4.  They renamed InternalMain to LaunchGame and it broke the patcher.
      • Added a new flag to Terraria.config to allow Terraria to catch CorruptedStateExceptions and report the stack trace.  This will not fix the weird multimonitor crashes that some of you are getting, but should allow you to get the stack trace so I know where I need to patch stuff up.
      If you get a stack trace, you can email it to romterraria -at- romsteady.net.

      July 8, 2015

      RomTerraria 4 v0.6 Coming Thursday Evening

      I don't have time to test this release and get it out tonight, so it will come out tomorrow.

      This release will not fix the "crash without stack trace, aka NullPointerException, aka access violation" issues, but will allow me to get a stack trace from them.

      If you are wondering how I'll get the stack trace, it's going to be a bit tricky, but here's the article I'm using to figure it out.

      Update: Home, and working on updating RomTerraria to work with 1.3.0.4 first.

      July 6, 2015

      Calculate OpenCV warpPerspective Map For Reuse (C++)

      At work, I was working on an OpenCV project that utilized warpPerspective quite extensively for real-time perspective correction.  It worked great, but it was slow.  When I profiled my solution, warpPerspective was taking ~20% of the CPU on my MacBook Pro.

      Now, if you look at the code for warpPerspective, it's essentially doing quite a bit of matrix multiplication for each pixel.  Since I'm assuming that my camera won't change position during a run, that's a lot of wasted effort.

      I use the following code to calculate the map that is generated by warpPerspective up front so I can use it in a remap call later to get the same results for less than half the CPU cost.  I store the final map in cv::Mats named transformation_x and transformation_y.

      transformationMatrix = cv::getPerspectiveTransform(originalCorners, destinationCorners);

      // Since the camera won't be moving, let's pregenerate the remap LUT
      cv::Mat inverseTransMatrix;
      cv::invert(transformationMatrix, inverseTransMatrix);

      // Generate the warp matrix
      cv::Mat map_x, map_y, srcTM;
      srcTM = inverseTransMatrix.clone(); // If WARP_INVERSE, set srcTM to transformationMatrix

      map_x.create(sourceFrame.size(), CV_32FC1);
      map_y.create(sourceFrame.size(), CV_32FC1);

      double M11, M12, M13, M21, M22, M23, M31, M32, M33;
      M11 = srcTM.at<double>(0,0);
      M12 = srcTM.at<double>(0,1);
      M13 = srcTM.at<double>(0,2);
      M21 = srcTM.at<double>(1,0);
      M22 = srcTM.at<double>(1,1);
      M23 = srcTM.at<double>(1,2);
      M31 = srcTM.at<double>(2,0);
      M32 = srcTM.at<double>(2,1);
      M33 = srcTM.at<double>(2,2);

      for (int y = 0; y < sourceFrame.rows; y++) {
        double fy = (double)y;
        for (int x = 0; x < sourceFrame.cols; x++) {
          double fx = (double)x;
          double w = ((M31 * fx) + (M32 * fy) + M33);
          w = w != 0.0f ? 1.f / w : 0.0f;
          float new_x = (float)((M11 * fx) + (M12 * fy) + M13) * w;
          float new_y = (float)((M21 * fx) + (M22 * fy) + M23) * w;
          map_x.at<float>(y,x) = new_x;
          map_y.at<float>(y,x) = new_y;
        }
      }

      // This creates a fixed-point representation of the mapping resulting in ~4% CPU savings
      transformation_x.create(sourceFrame.size(), CV_16SC2);
      transformation_y.create(sourceFrame.size(), CV_16UC1);
      cv::convertMaps(map_x, map_y, transformation_x, transformation_y, false);

      // If the fixed-point representation causes issues, replace it with this code
      //transformation_x = map_x.clone();
      //transformation_y = map_y.clone();


      I then apply the map using a remap call:

      cv::remap(sourceImage, destinationImage, transformation_x, transformation_y, CV_INTER_LINEAR); 

      Ended up dropping CPU usage for the call down to ~8%.  I'll take it.

      July 4, 2015

      Sick

      Woke up with a hell of a cold and can't concentrate on much of anything.

      Taking the day off from everything, including side projects.

      Update: Still sick.  RomTerraria FAQ updated.

      Update part deux:  Getting better, but still enjoying NyQuil-induced comas every evening.  Will try to get back on RomTerraria tonight.

      Update the third:  Feeling a lot better.  Should be back on RomTerraria Thursday evening.

      July 2, 2015

      RomTerraria 4 v0.5 Released

      In this build:

      • If you are crashing after an alt-tab or changing from windowed mode to full screen or back, RomTerraria may spit out some crash information into a text file in your save game folder called CrashInInitTargets.txt.
      • General logging support into RomTerrariaDebug.txt in your save game folder.
      • If you have been experiencing flickering or missing textures, I've got a potential fix in to work around a race condition in Terraria.Graphics.TextureManager.Load.

      July 1, 2015

      RomTerraria 4 v0.4 Released

      Link back to main blog post.

      Rendering issues with missing transparent surfaces, missing items, and some rendering crashes should be fixed now.

      Last update for the night.  I have work in the morning.

      RomTerraria 4 v0.3 Released

      Fixed a crash issue that would happen if you patched Terraria more than once.

      Get it here.

      RomTerraria 4 v0.2 Released

      Wow...3,200 downloads in less than 24 hours.  Nuts.
      • Found the issue with the crashes in single- and multiplayer on x64 boxes.  Turns out there is a knowledge base article about it.  The Terraria.exe.config file now included should take care of it, whether you use RomTerraria or not.
      • Added "Disable Achievements" per request.
      Get it now.

      June 30, 2015

      Productive Evening

      By the time this post goes live, I'll have had over 500 downloads of RomTerraria 4 in just under three hours.  Not too bad.

      That said, I've got a full day at the office tomorrow and need at least eight hours of sleep.  Have to keep at the full-time job because making mods doesn't pay bills.  I'll try to answer questions every evening (Pacific time) for the next bit, and if I have to release a patch, will do so as quickly as possible.

      RomTerraria 4 Released

      (5/17/2020 4:23pm: If you're looking to fix the zoom stuff in "Journey's End," just go here.)

      (6/11/2017 6:51pm: While the high resolution core of RomTerraria is now embedded into Terraria proper, they added in a forced minimum zoom level for higher resolutions.  New project created to take care of that.)

      Note: This was tested on 1.3.3.  This does not work with 1.3.4 yet due to a change in visibility of a RenderTarget2D and some bugs in the new water code that I have to work around at higher resolutions.  A test build that supports v1.3.4.3 is available for download on this post, but has not yet been fully tested.

      (9/11/2016 11:36am) Download RomTerraria 4 v1.2 here.  Updated for 1.3.3, and the instructions below have changed.

      The v1.2 release fixes the "Retro" lighting issue introduced with v1.1. 

      (6/28/2016 10:12am) Forgot to update this post with a link to the code on GitHub.

      This release allows you to do the following:
      • Increase the maximum resolution of the Terraria client beyond 1920x1200.
      • Enable cooperative fullscreen mode.
      • Swap the left and right mouse buttons for our left-handed friends out there.  (new for v0.9)
      • If RomTerraria can't find your Steam folder, will now pop up a folder browser to let you tell it where your Steam or GOG version of Terraria is.  (new for v0.8)
      • Allow you to have the effects of the Gravity Globe without having the item equipped in an accessory slot.  Thanks to GamingWithRyan for the idea.  (new for v0.8)
      • Disable achievements (if you want).  (new for v0.2)

      Terraria 1.3.0.2 at 2560x1440

      To use:
      1. Unzip to somewhere other than where Terraria is installed.
      2. Right-click on Terraria in Steam, go to Properties, go to Local Files, and then Verify Integrity of Game Cache.  Every crash I've looked at so far has been caused either by the game itself or a bad file somewhere.  This also resets your Terraria installation to the latest version of the game.
      3. Run Terraria once.  If you don't do this, you can run into situations where RomTerraria can't find or patch your version of Terraria.
      4. Run RomTerraria 4.0 (filename is RTRewriter.exe).
      5. Only select "Enable Cooperative Fullscreen" if you are running a multimonitor solution or know what this is.
      6. Click "Open Terraria Save Game Folder."
      7. Click "Update Terraria." A Terraria.exe file and an RTHooks.dll file will appear in your save game folder.
      8. Open your Terraria folder on Steam.
      9. Rename Terraria.exe to Terraria.Original.exe.
      10. Copy Terraria.exe, Terraria.exe.config, and RTHooks.dll from your Terraria save game folder to your Terraria folder.
      11. Launch the game from Steam.  The game may crash once.  If it does, run one more time.
      12. Starting with v1.3.3, Terraria won't let me default to the maximum resolution of your default monitor, so you will have to go into Settings > Video > Resolution to change your resolution.
      If you select a higher resolution and it won't go above 1920x1200, go into windowed mode, change the resolution, then pick "Go Fullscreen" from the options menu.

      Please note that if the game is updated, you'll have to repeat the above steps.

      One note: the best thing that could ever happen to this mod would be for its main reason for existing (resolutions greater than 1920x1200) to cease to exist.  If Re-Logic would like the code to enable high resolutions, just let me know.  It's yours for free, and would take very little time to implement.

      If you like it, and want to tip in Dogecoin, here's someplace you can send them:
      DMVNZDrDxqygKg92v7KjbgJPrDz6cseWF3

      FAQ:

      I'm not able to go above 1920x1200.
      I'm looking into each of these reports.  If you can't go higher than 1920x1200, enable cooperative fullscreen.  When cooperative fullscreen is enabled, I set the screen size to the size of the default display adapter (monitor 1), set full screen to false, and set the window style to "none," meaning it hides the border and widgets.

      The game is crashing with RomTerraria installed.  (Note that this usually happens at resolutions higher than 4096 in any one direction.)
      Try running v0.7.  You should now get a stack trace.  Post that stack trace in the comments.  If you can't post it to the comments, you can email it to romterraria -at- romsteady.net.  If you don't get a stack trace, it's okay.  I'm working on setting up a 3-monitor setup at home so I can troubleshoot it.

      After installing RomTerraria, I can't open the map.
      Go into Settings > General, and make sure "Map Enabled" is set.  It's been randomly disabling for some users.

      After installing RomTerraria, I seem to be capped at 60fps.
      If you are in cooperative full-screen mode, you'll be capped to your desktop refresh rate.
      If you are in regular full-screen mode, I'm investigating that.

      I can't get the game to run after this, I'm running Windows Vista/7/8/8.1, and my copy of Terraria is installed under C:\Program Files.
      Please install the game to a different Steam install location (like C:\Steam) or run Windows Explorer as an administrator.  Windows Vista and above created a great security mechanism called VirtualStore which can make it look like you are mucking around inside C:\Program Files, but are actually working against a folder in your User folder.  The way Steam works, it won't like having the replacement Terraria.exe or RTHooks.dll in the VirtualStore folder.

      How does this find my copy of Terraria?
      I use .NET's Process.GetProcesses() function to get a list of all processes on your machine that have "steam" in the name.
      If I find one named "steam.exe," I look for Terraria in [that file's folder]/SteamApps/Common/Terraria.
      If I don't find it there, I assume that it's in a different Steam install folder, so I open your config/config.vdf file and look for the path in InstallConfigStore/Software/Valve/Steam/apps/105600/installdir.
      If I don't find it there, or if I don't find Steam.exe, I look for the GOG version by checking the registry key HKEY_LOCAL_MACHINE\SOFTWARE\re-logic\terraria\install_path.
      If it still can't find it after that, it pops up a browse dialog.

      Old versions of RomTerraria patched out the need for Steam.  Will you do that in RT4?
      No.  It works fine under Steam now due to how they handled Steamworks this time.

      There are one or two new files in my save game folder: RomTerrariaDebug.txt and/or CrashInInitTargets.txt.  What gives?
      RomTerrariaDebug.txt is a general purpose place where I can log out debug text.  Right now, I'm putting texture load debug info there if you've hooked the texture loader.
      CrashInInitTargets.txt gives me all the info I need to troubleshoot hardware acceleration issues.

      How do I uninstall this mod?
      Right-click on Terraria in Steam, go to Properties, go to Local Files, and then Verify Integrity of Game Cache.  This will "unpatch" Terraria.

      When I use this on the GOG version, I'm told to run this through Steam.  What gives?
      This isn't happening for everyone, but I'm investigating the cause.

      When can I expect a response to my questions?
      I'll respond as quickly as I can, but the most likely time periods are between 7-8am Pacific and 7-10pm Pacific daily.

      I'm technically advanced and think I can help out.
      Code's on GitHub.  Have fun.

      Older Version Archive:

      v0.8 Download (works for 1.3.0.8)
      v0.7 Download (works for 1.3.0.4, 1.3.0.5, 1.3.0.6, 1.3.0.7)  
      v0.6 Download (works for 1.3.0.4, 1.3.0.5) 
      v0.5 Download (works for 1.3.0.3)
      v0.4 Download (works for 1.3.0.3)
      v0.3 Download (works for 1.3.0.2)
      v0.2 Download (works for 1.3.0.2)
      v0.1 Download (works for 1.3.0.2)

      June 28, 2015

      RomTerraria 4 Release Date

      I'm targeting a July 1 release date for RomTerraria 4.

      This version will be updated to work with Terraria 1.3, which will be released at approximately 10am Pacific on June 30.

      This release will ONLY include support for modifying resolutions and enabling cooperative full-screen. The patch notes make it look like 1920x1200 will still be the largest supported resolution, so RomTerraria will still be needed.

      Why release the next day?  Well, I have to be at work between 8:30a and 9a on June 30.  I won't be home until almost 6p that day.  I have to eat dinner, investigate the code, and figure out how to do the following:
      • Disable achievements if it's the Steam version.  Steam achievements have been added to this game, and since I don't want anyone to get a Steam ban for using RomTerraria, this has to be done.
      • Add the resolution to the UI.  Each version has had its own weird way that this has had to be done.
      • Redirect file loads so that the patched executable doesn't have to be in your Terraria folder.  This was one RomTerraria 3 feature that people liked, so I'm going to try to keep it.
      • Build the whole thing into a single patcher.
      Once all that is done, I'll have to test it.  I've got a nice 2560x1440 monitor right in front of me so I can test the living daylights out of the release.

      Finally, I have to upload it and update all the download links everywhere, but I'm probably going to create a RomTerraria 4 download page and just point everything there.

      See you later this week.

      Update 6/30, 10:15am: Terraria 1.3 released.  Looks like the code will be easier to port than I thought.  I'll need to change from searching for Main to InternalMain, ensure that SocialMode is changed from Steam to None, and then test my code.  May even be able to get rid of some of my code completely.  I like simplification.

      Can't do any of this until I get home tonight, but shouldn't be too much of a problem.

      Update 6/30, 5:30pm: Still not home, but I'm already getting crash reports from people running the old mod.  Don't run the old stuff.

      Update 6/30, 6:10pm: Home now, but it looks like they did another update since this morning.  Give me a few minutes to eat dinner, please, then I'll start on updating the mod.

      Update 6/30, 6:33pm: In menu in Steam at 2560x1440. Needs additional testing before I can release it.

      Update 6/30, 6:42pm: In game in Steam at 2560x1440 in 1.3.0.2.  However, I'm getting this weird strobe effect in dark areas.  Won't release until I get that solved.

      Update 6/30, 6:54pm: Visual glitch fixed.  A new RenderTarget2D was added in 1.3 that I wasn't accounting for.  Let me package this up.

      Update 6:30, 7:08pm:  Released.

      June 21, 2015

      Recovering from Injuries

      I'm not doing a lot of coding right now.  Instead, I'm pulling a Peter Griffin, grasping my knee, and going "ow" a lot.

      Yesterday, I decided to go to Raging Waters in San Jose.  It was a warm day, and the air conditioning in my leased home doesn't work very well, so rather than risk heat damage to my PC (a very real possible side effect of the computer vision coding I've been doing for the last couple of weeks), I decided to spend a day going down waterslides.

      It was my first time going, so after spending 45 minutes in line to get my photo ID season pass, I started meandering around the park.  There were several slides that I could not go on because of my shoulder, but I finally found a set of slides I could enjoy called "The Serpentine Slides."

      I went down the yellow slide first, and had a good time until I got to the bottom.  My tube was underinflated, so when it hit the water at the bottom, it stopped and I skipped off that tube right under the water.  Still, had fun.

      Then, went up to the pink slide.  This time, my tube had a reasonable inflation level.  After turn three, there's a dip.  When I hit that dip, I caught air, hit my head hard on the slide, got turned upside down, and was going down the slide face down.  I caught air twice more with the tube on top of me, but was able to shield my head from getting hit any more with my arm.  When I got to the bottom of the slide, I essentially pile-drived my knees into the concrete on the bottom of the ride.

      When I got up, I was woozy and bloody.  Kudos to the first-aid team there for a prompt, polite response.

      Today, I've got a massive goose-egg where my head hit the slide and my right knee hurts like crazy and is still leaking fluids into bandages, but I'm mostly okay,

      Expect progress on posts to resume soon.

      June 4, 2015

      Back from Vegas

      14 poker tournaments.  6 final tables.  Only one cash.

      I've done a lot better, but I've also done a lot worse.

      I need a couple of days to catch up and recover.  Please forgive any delay in responding to questions/comments/bitches/complaints/gripes.

      May 27, 2015

      In Vegas For WSOP

      I'm currently sitting in my hotel room in Las Vegas, Nevada, prepping for a good night's sleep knowing I've got a reliable house sitter, and getting mentally ready for the World Series of Poker.

      This year, I'm not participating in the Main Event.  Instead, I'm going to be in the new Colossus event that will be starting on Friday.  If you're going to be in the area, I start at table 113 in Pavilion White and I'll be wearing a Jurassic Park cap the entire event.

      Expect delays in responses between now and June 4.

      May 14, 2015

      Enabling Nashorn Autocomplete/Intellisense In Eclipse

      (Update 5/14/2015, 1:00pm: Discovered that for this to work, you should be running Eclipse Luna SR2 for Java EE Developers at a minimum.)

      At Netflix, we have a system called Netflix Test Studio.  Under the hood, it uses Nashorn to execute test cases that are written in JavaScript.  Several of the objects that get instantiated within the Nashorn context are native Java objects that inherit from a couple of base classes or implement a specific interface.

      One of the biggest downsides to the way our system was set up was that we didn't have autocompletion for our test cases.  I decided to spend a day to try to figure out what we needed to do, and here are the results of my investigation.

      Please note that I won't be sharing my exact code here.  The code I wrote relies on some inner assumptions about our code base.  I'm here to talk you through what I did so that you can replicate the behavior in your own code base.  Also, I'm using Eclipse Luna SR2, so instructions may differ from version to version.  Finally, this is only a day's worth of work.  A final version will be significantly more robust than what I describe here.

      The first step is enabling the JavaScript facet in your Java project.  To do this, right-click on your project in Project Explorer, go to Project Facets, select "JavaScript" and hit "Apply" and "OK."  This will also give you syntax error checking for JavaScript files inside your Java application, which is always a nice thing.

      Second, you need to create a JsDoc file.  JsDoc is the JavaScript equivalent of JavaDoc.  What I did was I added a new main class to my project that I could run that would use the Reflections library to find all the classes in my project that inherited from our test step base class and generate a JsDoc stub like this:

      /** @external com.netflix.nts.steps.tocap.NrdpCompareFriendlyName

      Constructors:
      NrdpCompareFriendlyName()

      @class NrdpCompareFriendlyName
       */
      function NrdpCompareFriendlyName() {}


      /** Add com.netflix.nts.steps.tocap.NrdpCompareFriendlyName to JavaImporter */ NrdpCompareFriendlyName.prototype._ = function() {}
      /** getTargetId()
      */
      NrdpCompareFriendlyName.prototype.getTargetId = function() {};
      /** promptUserForResponse(UserPrompt arg0)
      */
      NrdpCompareFriendlyName.prototype.promptUserForResponse = function() {};
      /** wait(long arg0, int arg1)
      wait(long arg0)
      wait()
      */

      NrdpCompareFriendlyName.prototype.wait = function() {};
      ...

      Let me walk you through each of these items.

      NrdpCompareFriendlyName is a Java class.  /** */ is how you delimit a JsDoc comment block in JavaScript, so I put some metadata there.  Eventually, I'll be pulling in some data from our JavaDoc, but for now, I really only needed to add the @class attribute to help Eclipse know that this is a valid item to show up after a "new" statement.  I then declare an empty function with the same name.

      Now, there's a bit of a bug in Eclipse Luna SR2 where if you hover over a class name, rather than pull up the JsDoc information that came immediately before the function definition, it will pull up the JsDoc for the method immediately following it.  So, I emit a dummy underscore so I can have the class name that needs to be added to the JavaImporter class immediately after.  This way, I can hover over the class name, then copy the full class name and easily paste it into my JavaImporter statement.

      To associate the method with the class, I add it to the class prototype.

      For each method in the class, I then emit a simple JsDoc block with all the overloads of the methods and their expected parameter types.

      Finally, we add the JsDoc to the project references.  Right-click on JavaScript Resources in your project and pick "Properties" and add your JsDoc file there.  You should then get this data to show up as part of your Eclipse autocomplete/content assist.

      A couple of additional recommendations before I leave.  In Preferences > JavaScript > Editor > Content Assist, check "Enable Auto Activation."  Also, if your JsDoc file is massive (ours is 4.6MB right now), you might need to increase the timeout under Preferences > JavaScript > Editors > Content Assist > Advanced.

      Have fun, and let me know what kind of enhancements you guys can make with this.

      April 5, 2015

      Handling Quake 1 .map Files (Part 1)

      Now, let's say you want to handle Quake 1 .map files.  Perhaps you want to do the project I'm doing: porting Quake to other engines.  Perhaps you want to use Quake 1-compatible level editors for your MonoGame or three.js projects.  Who knows?  Code for this series will be provided in C# for easy integration into tools and content pipelines.

      First, let's look at what an entry in a .map file looks like.

      {
       {
        ( x1 y1 z1 ) ( x2 y2 z2 ) ( x3 y3 z3 ) TEXTURENAME xofs yofs rotation xscale yscale
        ...
       }
       "key" "value" // Optional comment
      }
      

      Each entry in a .map file is enclosed in curly braces to encompass an entity.

      Each entity can contain zero to many brushes.  Each brush is enclosed in curly braces.

      Each brush must be convex can contain four to many faces.  (Why four?  Think of a three-sided pyramid and the base.)

      Each face includes three vertices to define the plane, shown above as (x1,y1,z1) through (x3,y3,z3), a texture name, and all the information needed to calculate the texture coordinates for each final vertex.  Vertices are in clockwise order.  Most of the time, these vertices are the first three vertices of that face, but not always.  They're just there to define the plane.

      Each entity can also contain one to many key/value pairs.  Each entity will have one key named "classname" which tells you what other key/value pairs you should expect.

      If you find a classname of "worldspawn" in the entity, this will be all of your static geometry.  You can use this information to heavily optimize this information for rendering or other work: do texture batching, visibility calculations, collision calculations, etc.

      While you can have comments in your .map files, you probably won't see them.

      If you think about it, this gives you a good way to handle save game separation as well.  When you are saving your game, iterate through your world and serialize out the information for any entity that isn't worldspawn.  When you are loading the saved game, you can load only the worldspawn entity, then overlay your other entity information from your save game.

      Now that we have that information, we know what we need to parse the .map file.  Here's a functioning map parser.  It fills a Map object with Entities that can contain key/value pairs and Brush objects containing Faces.

      http://www.romsteady.net/quake1/Quake1Map-ParserOnly.zip

      Next time, we'll turn this information to a polygon soup.

      March 30, 2015

      The Failures Of Binary Formats In Development

      One thing I've wanted to do for the last few weeks is dive back into game development.  While I was back at Amazon, I could do game dev work that I had grandfathered in, but I couldn't technically do any new game dev work.  Fortunately, Netflix lets me do pretty much whatever I want outside the office, provided it isn't creating a new video streaming service.

      With that said, I needed a solid project to drive me forward.  I had licenses for Unreal Engine 4, Unity 4/5, and Leadwerks 3.x, tons of assets I had been collecting and licensing over the last few years, and some larger projects in mind, but I wanted something small and contained...something where I could spend a week or two working on the project and have something done.  I wanted something completable.

      While I was browsing through my assets looking for a little inspiration, I found quake_map_source.zip.  Basically, John Romero had released the original .map files for Quake v1.06 back in 2006.  I started remembering back when Tenebrae was released, people were going in and manually placing lights to try to get the effects, and here I am with the source files.  Why not see if I could get Quake maps functioning in these other engines?  And thus, a project was born.  I wouldn't be able to release a full project because that would virally taint the engines with the GPL, but I could release code and tools to let people recreate it themselves.

      I decided to start with Leadwerks.  I had backed the v3 engine on Kickstarter, and Josh had been nice enough to release the runtime code to the Map class loader at my request.

      The Leadwerks map class is a binary chunked format.  The first 32 bytes of the file include a magic number to identify it as a Leadwerks scene file (SCEN), a version number, and then offsets to where you will find each chunk.  Dealing with a chunked file is fairly easy to do:

      1. Serialize each chunk to an internal buffer
      2. Write out an empty directory chunk up front
      3. For each buffer that you want to write out...
        1. Store the current file location
        2. Write out the chunk
      4. Once you're done writing out all the chunks, seek back to the beginning and write the offsets for each chunk into the file

      The first item that needed to be done was to write out an empty map file.  If I can't write out an empty file, I don't stand a chance of getting the rest done.

      There are two chunks that are critical to working with a Leadwerks file: the String Table and the Map Info chunks.  The String Table is where strings are supposed to be stored, and they'll be referred to by IDs within the Leadwerks file.  The Map Info table has general information about the map.

      Let's start with the Map Info chunk.  If you have a Leadwerks license, you can download the file in the above-linked post and follow along.  Please be aware that after a lot of headaches, I rolled back to map version 22 for what I was going to try to export.

      Starting on line 352, it wants the number and resolution of light maps.  That's easy...I don't have any.  Two ints out.

      Line 408 wants ambient lighting.  Four 1.0f values.

      Line 413 wants a map size.  Inspection of the UI shows valid values of 1024, 2048, and 4096.  1024 it is.

      Line 414 wants gravity.  The UI shows a positive value, but the value is stored in the map file as a negative value, so -25 is serialized.

      Line 424 looks like wasted space, and I just serialized out 0 and 0f depending on the field, but we'll come back to this.

      Now for the comment on 438:
      // This just uses the regular string commands, not the fancy string index system
      If you look at an actual file, it means that it's going to try to read in an actual string here, terminated by a Chr(13)/Chr(10) line break.  Okay...ReadLine() is used for integrated text, ReadString() is used for string table data...got it.

      The String Table chunk starts with a string count, and is then just a dump of null-terminated ASCII strings.

      I also serialized out empty object tables, navigation mesh tables, and terrain info chunks.  The end result is a fairly simple, empty file.  If I load the empty file in the engine itself, it works fine.   However, if I load the empty file in the editor, the editor crashes with an access violation.

      So what happened here?  Let's go back and look at that line 424 item.  The engine is throwing away this data, but the editor requires some specific stuff there.  While I'm not going to guess what the first two int values are, I think the next four groupings are data about the four viewports.

      So in this case, we have editor data intermingled with runtime data.  That's a bit of a problem.  I'll have to think about the editor as well as the engine representation.

      I understand the appeal of keeping a binary file for your worlds.  You can keep all of your dependencies in a single file or location.  You can have things in a ready-to-run format for your engine.  Speed of iteration (time from edit to gameplay) seems to drop dramatically.  There are major benefits.  However, there are large drawbacks.

      First, maintenance. This map shows other issues with maintaining binary formats over a long time.  Look at line 443:
      if (version > 22 && version < 25)
      In other words, for only those versions, read in an integrated string, split it out, and apply the post f/x shaders there.  If this had been a text file, the key/value pair could have been easily ignored or converted out to a new format on save.

      Second, corruption.  Let's say I've got a hefty map and as part of a save, a small portion gets corrupted.  If I have a text file, I can generally go in, find the problem area, and either figure out how to fix it or how to excise it so I can rebuild.  If a binary file gets corrupted...oh, well.

      Some of you are thinking that any game developer worth his or her salt would be using source control, which leads to number three, source control.  Source control systems work best with text files.  Some, like Perforce, do a good job with handling binary diffing.  Others, like git, really dislike binary files.  Hell, even Unity has started offering a text format for its levels because of this.  It's in YAML, which is a pain to work with, but it works.

      Fourth, extensibility.  There's nothing wrong with using a binary package or a bake for modules that you want to share out in order to keep all the dependencies in one place or to make it easy to handle module licensing, but when it comes to working with the core engine itself, allowing full import of scenes from text can be crucial.  When I initially asked for the map class to be released, I was working on some procedural content stuff.  I wanted to be able to create a single file that I could import that had lights, geometry, triggers, script hookups, etc.  You can import your geometry easily enough in most of these engines, provided you can export into a proper format (FBX, COLLADA, etc.), but can you import gameplay primitives just as easily?

      When I get home tonight, I'm going to try to bring in the 424 chunk, and I'm going to try to get this Leadwerks file writer out as code for people, but this has really reinforced why any game dev work I do going forward is going to try to keep plain text files for my non-runtime representations of game primitives where appropriate.  I'd rather spend a little extra time baking for runtime and keep the flexibility it gives me.

      Minor update, 6:13pm: After extracting the values from another map, it does look like those values are used for the editor.  After adding those values in, the editor no longer aborts out with an access violation.  It just quits silently.

      March 12, 2015

      MacBook Air Soundflower Issue

      I've got a 2011 MacBook Air that I use for travelling.

      Since I updated to Yosemite, I've been having a weird issue where it will occasionally lose all audio.

      The weird part is that if I go into Settings > Audio, and go to the Output tab, I see that the sound has changed from "Internal Speakers" to "Soundflower (2ch)."  This is odd, because "Soundflower" is meant to be used to capture audio.  It's an input pathway.

      Any idea how to disable "Soundflower" for output?

      WSOP Los Angeles 2015 Update Post (Done)

      Update 1:

      I played in the 8:30pm $230 satellite against 67 other players.  Top 6 got seats, 7th got $1500.  I busted out in 11th.  Not too bad given that I haven't played poker in person since last year's WSOP in Las Vegas.

      Today, I'm going to be playing in event #6, the $365 no-limit hold-em tournament.  I paid my registration fee last night, and I was the first entrant.  This tourney can go two days.  I may also play another $230 satellite tonight to see if I can avoid paying the $1,675 for the main event on Saturday.

      I'll be taking Sunday off to take care of some items for work, as well as to do some game development in complete isolation.

      Anyway, time for a quick shower, then a run to IHOP for a ham and cheese omelette. 

      Update 2:

      Took the table lead early, but was rivered three times in a row that crippled me.  I pushed with 11 big blinds left and got outkicked to be out in 121st place.

      Next tournament is a $230 satellite tomorrow at 3pm.  Going to do some game development for the rest of the night.

      Update 3:

      Decided to try another $230 satellite again tonight.  Rivers made sure it was the wrong choice.  Back to game dev.  Will do event #7 tomorrow at noon instead of the $230 satellite and will just buy into the event on Saturday.

      Update 4:

      Busted out in 97th in event #7.  Went to the tables and managed to claw back $565, bought in for event #8 day 1a, and called it an early night.  I've got plenty of time for a quick shower, a leisurely breakfast and lunch, and a bit of relaxing music before my last tourney.

      If I cash, great.  If not, this was still a good experience.  I busted out 18 places away from the money last year in Vegas, and this has done a good job of exposing the weaknesses in my game.  Hopefully I've learned enough to help me out today.

      Update 5:

      Today was flight A of the main event. Took a dominating lead on my table and kept it through dinner time, eliminating four players.

      However, at level 12, things got insane. The slow bleed took almost 40% of my chips, and then two hands knocked me out.

      For the first one, the short stack shoved. I had a pair of 9's, and this guy had been shoving with crap all night, so I called. Everyone else folded, and he showed AK. It was a race that he won on the river.

      Next hand, two dudes I had good reads on ahead of me were raising. I could tell they didn't have shit, and when I looked down at queens, I pushed. One dude behind me called, and the dudes with nothing folded. He had AK, and won the race on the river.

      It's been a fun trip down here to the WSOP Circuit. While I didn't cash in any of the events, I'm up about $565 from my cash games.

      Tomorrow will be spent working remotely, and the rest of this trip will be spent working in isolation on some game dev stuff.

      January 24, 2015

      Helper - Convert FPS Creator Models for Leadwerks Using Ultimate Unwrap 3D Pro

      Just a quick post tonight.

      I found this wonderful tutorial by Jesse B. Anderson on how to convert FPS Creator Classic models to work with Leadwerks Engine 3.x.

      I have a copy of Ultimate Unwrap3D Pro 32-bit, so I thought I'd try to automate the process since UU3D doesn't have a batch mode.

      This is an extremely early copy of the code for my automation.  YOU WILL NEED TO MODIFY THIS CODE FOR YOUR OWN ENVIRONMENT. THIS CODE IS NOT VERY STABLE.  THERE'S STILL A LOT OF WORK TO BE DONE.

      That said, this code is managing to successfully convert FPSC meshes and animated models over, as well as create animation key files for import into Leadwerks 3.3.  To use it, modify the paths that are going to be converted/converted to in the source, launch UU3D, and let it go.  If you run it through the debugger, you can usually hit "Continue" when Windows Automation freaks out and it will continue.

      A later version is going to import the models into Leadwerks and properly scale the models, but this may be useful for some people as is.

      January 18, 2015

      Status Update; New Version of RomTerraria Incoming

      It's been a few weeks, so let's do a quick infodump.

      I've managed to purge all of my personal code that I wrote while I was at Amazon that wasn't grandfathered in, so all code going forward should be clean, unencumbered, and most importantly, mine to do with as I please.

      Work is going well.  I'm about two weeks away from being (mostly) done with my first major project at Netflix.

      I'll be in Los Angeles this March participating in the World Series of Poker Circuit at the Bicycle Casino.  Specifically, I'll be playing in event #6 or #7 and event #8.

      I'm rewriting the code I had written for Leadwerks to take advantage of some 3.3 features, as well as ensure that my early code is consistent with the code I wrote later on.

      Oh, and I'm almost done with the new version of RomTerraria.  New features that will be out this week are:
      • Admin rights no longer required if you installed the game in the Program Files folder on your machine.  The RomTerraria executable and supporting DLLs will be put in a subfolder of your Terraria folder in your Documents folder.
      • Support for the GOG.com version as well as the Steam version.
      • Code cleanup to prepare for the coming 1.3 version of Terraria.
      (Update: New version out.)