Home Page
Archive > Posts > Tags > Design

6 annoyances in GoLang
I still love it though

Go has been around for quite a while now and has had a lot of time to grow and mature, and it is really quite a lovely language. Watching it develop since its inception has been a delight. Its native concurrency is best in the industry, it is designed for things to be super-clean and standardized, and it compiles really fast. The support for it in IntelliJ/GoLand is top-notch and makes it really a blast to work in.

There are of course a lot of design decisions in the language that could be debated, like how setting a variable has to be a top level statement that cannot be embedded in other statements, how there is no “real” class based system, and how their specific name casing conventions are actually built into the language and enforced. However, I understand the reason for all of these decisions and feel that the designers of the language were justified in the decisions they made for the reasons they give.

There are at least 6 problems I’ve run into a lot recently though that I feel could be better. I would love to jump into the source of the language and see about making these fixes myself, but the GoLang team has a history of completely ignoring outsiders, and it would be highly unlikely that they would accept anything I submitted, especially since a number of these things would require very long and complicated proposals to even start looking at. Go may be an open source language, but it is not open development.

These first 3 would be additions to the language that would not break anything, which is in line with their promise of never breaking anything.

1) Interface with constraints elements cannot be used as variable types. Example:
type intish interface{ int64 | uint64 }
func adder[T intish](val T) T { return val + 1 } //This is allowed
var val intish //Error: cannot use type intish outside a type constraint: interface contains type constraints

IntelliJ inspection Error: Interface includes constraint elements '...', can only be used in type parameters

It wouldn’t be a big change for the compiler and runtime to be able to enforce these types of constraints when typecasting and I really wish the language had this. This is the type of thing TypeScript does beautifully since it is actually part of its native design and functioning.

2) Member functions (methods with receivers) cannot have generics
type foobar int
func (*foo) bar[T any](val T) {} //Method cannot have type parameters

3) []generic cannot be typecast to []any

There is no real reason that an array containing interfaces shouldn’t be convertable via typecast to another array of interface types as long as they are compatible.

type testAny any
startVar := make([]testAny, 3)
endVar := []any(startVar) //cannot convert startVar (variable of type []testAny) to type []any

This next one is an unfortunate consequence of the language design and would not be easily fixable.

4) Import cycle not allowed

This means there cannot be circular dependencies. If package A includes package B, then package B cannot include package A. This often wasn’t a problem with languages like C since the headers were independent of the implementations, but because packages in Go do not have headers, and their package types cannot be used as method receivers in other packages, this one is just not possible. And it can be quite limiting.

This next one is a bug in either the language or the documentation, and I consider it to be a security problem, but the go team does not seem to care about it. See https://github.com/golang/go/issues/65201

[Edit on 2024-04-10] Oh wow! they fixed it!

5) sql.RawBytes modifies the current buffers instead of setting new buffers like the documentation says.

All the documentation says about RawBytes is “RawBytes is a byte slice that holds a reference to memory owned by the database itself. After a Rows.Scan into a RawBytes, the slice is only valid until the next call to Rows.Next, Rows.Scan, or Rows.Close.”

If the value you are reading is a []byte/string then reading into a RawBytes works as expected. However, if it is anything else, like an int, it reads into the buffer that RawBytes already holds. This can lead to buffer injections and other really nasty bugs. For example:

db.Exec(`CREATE TEMPORARY TABLE goTest (i int NOT NULL, str varchar(10)) ENGINE=MEMORY`)
db.Exec(`INSERT INTO goTest VALUES (?, ?)`, 6, "foobar")
var scanIn sql.RawBytes
rows, _ := db.Query(`SELECT str FROM goTest WHERE i=?`, 6)
//Do something with scanIn
rows, _ = db.Query(`SELECT i FROM goTest WHERE i=?`, 6)
rows.Scan(&scanIn) //This corrupts the internal sql driver buffer since it reads an int into the pointer we received earlier

This final one has been an annoyance of mine since day 1 of working with Go, and it still annoys the hell out of me. There could be ways to fix it without breaking things, but I cannot think of any truly elegant solutions.

6) Short variable declaration can’t handle setting both new and already existing variables
This is the most common pattern you’ll see in Go by far:
var data string
if _data, err := someFunc(); err != nil {
} else {
    data = _data

You can either do it this way, or also declare err in the outer scope, thereby polluting the scope with unneeded variables. There is no way to set both the temporary error variable and also set the outer data variable in 1 line.

One non-breaking hack would be to add a symbol before variables you wanted to set in the outer scope. Example:

var data string
if +data, err := someFunc(); err != nil {
Sony eBook Readers
Why can you never find a product that has all the features you want!

To start off, Merry XMas ya’ll! (And Happy Holidays, of course! [I’m actually Jewish by heritage for those who don’t know me personally ^_^; ] )

I decided to get an eBook reader as a present for someone for the holidays, so I tried out both the Sony PRS-505 and Sony PRS-700. I decided on the Sony readers for now as they can handle most, if not all, of the main eBook formats. Here are the important things I discovered out about both.

Sony PRS-505

This is a minor upgrade to the first eBook reader that Sony released in September of 2006 (the PRS-500), and costs $300. It works as it should and is advertised, and does everything I’d really want from a basic eBook reader.

Sony PRS-700

This is a major update to Sony’s eBook line, released in September of 2008, and costs $400. The most important new feature to this is the touch screen, which has some major pros and cons.

The main comparison points that I found between the 505 and 700 are as follows.
  • I immediately noticed upon comparing the two how much lighter and more reflective the screen is on the 700, making it much harder to read. After some quick research, I found the following here:
    Sony added a touch layer on top of the e-ink display and embedded LED side-lights into the frame that surrounds the display. Clever. But this comes at the expense of contrast and glare, and the Sony Reader PRS-700 looks more like a grayscale notebook screen than an eBook reader. The glare isn’t nearly as bad as the average PDA or gloss notebook display-- it’s on par with matte finish notebook displays.
    As far as I’m concerned, very unfortunately, this makes the product completely worthless as far as an eBook reader. You might as well just use an LCD display instead of an eInk display for the quality and price!
  • The touch screen (that comes with a pointer pen too) itself is a spectacular design, and would make the device far better than the 505 if it didn’t ruin the readability of the device. The ability to navigate the device is much easier, quicker, and more intuitive due to the touch screen interface, which also allows for a lot of additional functionality including a virtual keyboard and selecting text.
  • The 700 “turns pages” about twice as fast, due to the processor being about twice as powerful.
  • The 700 also has many more zoom levels by default, which is a big plus for people who need the eBook devices specifically for bad eyesight. The “Large” zoom level on the 500 just doesn’t always satisfy what is needed in some eBooks, but the XL and XXL on the 700 definitely go that extra step. I was told by a rep at the Sony Style store that there is a way to download larger fonts to the system (possibly through the eBook files themselves), but I have not fully researched into this yet.
  • The 700 allows for searching for text now because of the virtual keyboard. I find this to be an incredibly useful feature for a book reader.
  • The 700 also allows you to takes notes and make annotations on pages due to the virtual keyboard.
  • The 700 has side lights that can be turned on, which is kind of neat, but this is really just an extra luxury.

One unfortunate annoyance of both devices is that you cannot use them while they are plugged into the computer (for charging via the USB interface or uploading new books).

After playing with both, I’d definitely recommend the 505 for now. If they could fix the contrast problem with the 700, it would be perfect and well worth the price.

I’d like to try the Amazon Kindle too, but their stock of it is so far backordered, I don’t feel like dealing with it for the time being. When I checked around the 23rd of this month, they had a 13 week wait to have the product shipped to you! The Kindle is also, unfortunately, more DRM laden with proprietary formats. This can be bypassed though.

The IPod Touch
And IPhones

So I decided to go over to the evil side recently and get an IPod Touch. I originally wanted to just try it out in The Apple Store, but I just couldn’t find out all I wanted to about it there, and was getting highly annoyed by the completely ignorant sale reps, who couldn’t answer any of my questions anyways, hovering over my shoulder. And, yes, I asked them a few questions and neither they nor their managers had a clue. >:-(

However, all the sales reps I’ve been talking to lately at different stores about the IPod Touch and other electronic products I’ve been interested in buying have been pushing me to just buy them, and return them if I’m not satisfied. This sales tactic is a bit new to me, and I don’t like buying something and returning it needlessly, but they suggest it, so I decided what the heck! I guess it’s assumed most people will buy it and either decide they like it, forget to return it, or are too lazy to return it! So I decided to go to Fry’s to grab one (IPod Touch 2G v2.2) for testing and possibly keeping if I liked it because The Apple Store were really uncool about a lot of things, and also charged a hefty restocking fee on return... jerks. The jury is still out on if I’ll be keeping it or not, but I decided to share some of my findings.

When I talk about the IPod Touch here, I am also talking about the IPhone, because they are basically the exact same product. The IPhone just has the camera and the phone features, but the rest of the software is all the same (they run on the same OS). I also have a few IPhone specific comments below, as a good friend of mine got one for XMas and I helped him out with setting it up and found out a few things about it at the same time. Whenever I refer to the IPod Touch from here on out, I am referring to both IPod Touches and IPhones.

First of all, as is advertised and highly touted, The IPod Touch has style. The design is wonderful, it has a lot of nifty features, and has lots of useful applications in the App Store, many of them free. The product itself is by far better than anything else I’ve tried on the market for music playing and general PDA (personal digital assistant) purposes.

The Blackberrys I’ve tried out at a Verizon store (the Storm and Curve IIRC) weren’t even in the same league as the IPod Touch. I also tried out a G1 (Google phone) at a TMobile store, and initial impressions were not spectacular. However, I can’t make a solid judgment on the G1 because I didn’t spend as much time with it as I could have, as I knew I couldn’t use it anyways. This is because I refuse to switch from the Verizon network because the signal quality and customer support I have received from them are worlds better than what I had ever received from Cingular (now AT&T), AT&T, and Sprint.

Now that I’ve gotten the initial information out of the way including why the IPod Touch is nice; on to all of the problems I’ve found with it.

  • Apple has horrible draconian policies regarding what can be put on an IPod Touch. Applications can only (legally) be put on the IPod Touch from the App Store, and Apple specifically regulates what is in the store, only allowing in it what is “best for their interests”. This, of course, includes denying any application in the App Store that “duplicates functionality” of an Apple product. This is bad for many reasons.
    • First and foremost, it’s not Apple’s place (though they argue that it is) to say who can develop and what can be developed for the IPod Touch, as long as it is not malicious in any way.
    • Apply very specifically blocks, quite often, products that would be excellent with great functionality because it “competes” with their generally inferior applications. Of course, one can unlock older IPod Touches, and I’m sure newer ones will be unlockable soon enough, so this problem can be bypassed. When a phone is unlocked, it can be theoretically used on a compatible network (not AT&T), and you can install any application you want to on it for free (as long as you can find it). The legality of this is questionable, but it’s not really risky.
    • This can force developers who have spent their time and effort to build a good product to not be accessible to the market, thereby completely screwing them after the fact. Apple is not specific on what can be put on the store, and is very subjective about the whole matter. Unfortunately, many developers have found themselves in this position after submitting their application to Apple for inclusion in the store.
    • Apple can decide to block a product after it has been released and people have bought it, deleting it from their phones without refund. I believe (but have no proof) that this has already happened when a product “duplicated the functionality” of a new application or feature in an application of theirs that was added after the fact.
  • The SMS (texting) interface on the IPhone is horrible. It only allows you to see part of the message that you are typing at any time (40 characters as a hazy guess). This could easily be fixed through a third party application, but Apple blocks any application that has SMS as it is “duplicating” the functionality of something they built. See the above bullet for more information.
  • The keyboard correction on the IPod touches leaves much to be desired, and there is no text prediction (suggesting words you are typing).
  • The virtual keyboard itself, while far ahead of any other virtual keyboard on a cell phone I have tried as far as usability goes, also leaves a lot to be desired, and can be quite annoying. I did get used to it pretty fast, but mistakes were very often and easily made, and I do not believe one could ever type as fast on a virtual keyboard, like the IPod Touch’s, as a physical keyboard, though I haven’t spent near enough time practicing on it to confirm this. The Google phones (at least the G1) solves this problem with its flip-out keyboard interface.
  • No multitasking. Period. The IPod Touch can do a few things at the same time (mainly play music), but 2 applications cannot run at the same time, and trying is against their developer agreement. Apple did this to control the user experience, so that a user doesn’t try running too many things at once, creating a bad user experience on the product from lag, which they would blame on Apple. Granted, the IPod Touch isn’t that powerful and it would be easy to bog down the system if too many things were running, but some things need to continue running in the background, with minimal processor time, to create a good experience.
    One of many examples of this is AIM (AOL Instant Messenger). When you start the application, it signs you on, and it keeps you online AIM until you specifically sign off (or perhaps if you turn off the phone, but I doubt it). This means that if you exit the AIM application after signing on, it shows other people that you are still online and receiving messages, even if you aren’t getting them. When you open the application back up, it retrieves all of the queued messages that were sent to you while the application was not opened. How hard and taxing would it be on the system to pop up a message informing the user a new message has come in while they are in other applications? Apparently too much, as Apple has to be black and white about the multitasking issue instead of allowing developers to petition for the right. Further, this queued AIM message system also tips one off to the fact that ALL AIM messages are sent through their servers to get to your IPod Touch, instead of your system directly connecting to the AIM servers, which is essentially an invasion on your private conversations.
  • Crashing. The IPod Touch itself has crashed on me twice within the first 2 hours I used it. When this occurred, I could not even start most all of the applications, even after turning the IPod Touch on and off (all the way, not standby mode). The only way I found to fix this was installing a new application from the App Store, or updating an application that had a new version ready. Go figure.
  • The IPhone can only take pictures, and not video. While there are products that allow taking video on the IPhone, they can only be installed by unlocking the phone, as Apple will not allow them on the App Store (see the top bullet for more information).
  • No searching for text on the current page in the web browser (Safari). This really bugs me as it is an essential feature I need in my web browser :-(.
  • I don’t trust installing Apple applications on my computers. I actually ended up using VMWare to use ITunes for this reason >:-(. ITunes likes embedding itself in your system in lots of places it shouldn’t, much like AOL since version 5.0. I do not believe it uninstalls itself completely either if you try. Also, when I tried uninstalling bonjour (an Apple communication protocol, which the program that runs it is also named, It used to be called Rendezvous) it didn’t even TRY to uninstall itself from my system. It just took the program off of a few lists and left all the files there. Even worse, I noticed that Bonjour was hooking a bunch of other processes it shouldn’t have been *sighs*.
  • I’ve saved my biggest complaint for last. All music on the IPod Touches (all IPods actually, and Zunes and Zens too) organize music by the MP3’s ID3 tags into genre/album/artist/etc, and do not allow organizing the music in folder based structures. While for most people this is not a problem, it is a big one for me. This is not a problem for people “new” to the MP3 player scene that buy their music straight from the ITunes Store, as that music is already organized for them with proper tags how they want it. My, and many other peoples collections, that have been being built for well over a decade (from CDs myself or friends have ripped ourselves for the most part), are not all tagged very well, as it never mattered. While I could go through my whole directory and tag everything properly, this would take upwards of hundreds of hours to do, and would be a waste of my time. Even so, I feel being able to organize by directory can be easier to navigate and organize then straight up genre/album/artist listings. This is a very basic functionality of all MP3 players I have had up until this point.
  • The above problem is actually solvable by playlist folder structures. Unfortunately, these are only available on some of the IPod types (for example, the Classic and Nano, IIRC) but not on IPod Touches or IPhones :-(. Further, building these nested folder playlist structures is also a minor pain. I started writing a script to do it for my music collection until I realized it didn’t work on my IPod Touch. ITunes transfers each folder to the IPod Touch as a flat playlist of all the songs in the playlists under it, but again, this is not a problem on some of the other IPod Systems. Unfortunately, if I was to spend the money on an IPod, I would like it to be a PDA too with much more functionality, which the IPod Touch satisfies, and the others do not.

As previously mentioned, I might not be keeping the IPod Touch, as I cannot justify the cost of it mainly as an MP3 player while I’ve already had other solutions that are almost as good for a number of years. I was one of the first adopters of MP3 players (of the MP3s on CD variety) back in 1998, I believe, and they still work great. However, I would probably get an IPhone were I able to use it on the Verizon network because it combines all the features I like on the IPod Touch with a phone. I would love to be able to use its excellent web browser (as far as cell phone browsers go) anywhere, not just when an accessible WiFi network was handy. The cost of an IPhone is more proportionate to what I’d like to spend since I’d be getting a phone and a music player out of it. Unfortunately, when unlocked, IPhones (and G1s) cannot work on Verizon, like it can the other networks, because Verizon uses a different kind of technology for its carrier signals (CDMA instead of GSM). Alas :-\.

Oh, yes, one more thing I wanted to mention. Apple was originally turning a blind eye to the unlocking IPhone market because most of them were going oversees to markets untapped by Apple, which is good for business for them. However, when Apple started expanding into other countries and this practice no longer served their needs, they added on a section to the AT&T contract you are forced to sign up for when buying the phone. It basically stipulates that if you cancel the AT&T contract (which incurs a fee after the first 30 days anyways) that you have to return the IPhone too. This way Apple is guaranteeing people can’t use the phone outside of AT&T.

Custom Fonts in Web Browsers
Solutions for a strict medium

A very important part of the design world is fonts, but it is an unfortunately annoying part of web browser land. There are very few fonts that come by default with OSs and even less default ones that match each other across all OSs, so your website won’t look the same across all platforms unless you use the right combinations. It’s much pretty guaranteed that if you want anything even remotely special in terms of a font somewhere on your website, you will be out of luck to match it across all platforms.

The commonplace solution for this is, of course, creating images for whenever you need special fonts displayed. While this is the most elegant solution, it is only appropriate for special circumstances, and not normal site content, as image file sizes can get ridiculous, and you lose plain text advantages like searchability and search engine recognition. Another solution is to request the user to download the font, like here. While this is a valid solution, the vast majority of users would not download the font because, mostly, they don’t care enough, and secondly, people generally know not to go download unfamiliar files on the internet when they don’t have to, for security reasons.

This has actually been a problem for me recently as I realized some of the default fonts I use for my site, which have always come with Windows, do not have default equivalents that come with most Linux distributions, as I had assumed. That’s a topic for a different day though.

So I had a customer recently request the ability to dynamically display some text in a certain font, so I told him there are 2 solutions. The first would be to use JavaScript to load translucent PNG images, the second would be to embed a Flash applet, as Flash can store font files internally for use. So here are instructions and examples of both:

JavaScript + PNG Translucency (alpha blending) Method
There are 2 ways to create the PNG translucency in Photoshop; one easier but less effective way that doesn’t maintain quality, and a slightly more complex path with better results.
  • To start off for both paths, a screenshot (ALT+PRINT SCREEN to take only the current window) will need to be taken of the font rendered in black against a white background. This can be done in your favorite word processor as long as it properly renders with translucency, or (for Windows) by just going to the font file in “c:\windows\fonts” and opening it, which uses “fontview.exe”.
  • After you have the screenshot, open a new file in Photoshop (File > New OR CTRL+N) and paste the screenshot into a new layer (Edit > Paste OR CTRL+V)
  • Delete the background layer, which requires the layer window is open (Window > Layers OR F7 to toggle its display). Right click the text portion “Background” of the background layer, and choose “Delete Layer”.
  • Select the region that contains your font’s alphabet (M for selection tool) and crop it (Image > Crop).
  • You might want to zoom in at this point for easier viewing (CTRL++ for in, CTRL+- for out).
  • The easy way from there:
    • Deselect the area (Select > Deselect OR CTRL+D).
    • Select the Magic Wand tool (W), set Tolerance to 0, check Anti-Aliased, and uncheck Contiguous
    • Select a pure white pixel and then delete the selection (DELETE)
    • You now have a translucent image that you can save and use, but the translucency isn’t that of the original font, as that is not how the magic wand tool works.
    Example using “Aeolus True Type Font” (Set against a green background via HTML for example sake)
    Translucent Aeolus True Type Font Easy Method
  • The better way:
    • Add a mask to your current layer (Layer > Add Layer Mask > Reveal All)
    • Go to the channels window (Window > Channels to toggle its display, it should be in the same window as Layers, in a separate tab) and select either the red, green, or blue layer. It doesn’t matter which as they should all hold the exact same values (grayscale [white-black colors] have the same red, green, and blue values), so red channel (CTRL+1) is fine.
    • Copy the channel (CTRL+C) (the entire workspace should still be selected after the crop)
    • Select the mask channel (CTRL+\), and you also need to make it visible (toggle the little eyeball icon besides it)
    • Paste into the mask channel (CTRL+V), invert it (Image > Adjustments > Invert OR CTRL+I), and then make it invisible again (untoggle little eyeball icon besides it)
    • Reselect the RGB contents (CTRL+~) and flood fill it with black [or your color of choice]: Paint Bucket Tool (G), 255 tolerance, no antialias
    • You now have a translucent image of the font that you can save and use that has the original font quality. You can test it by adding a white layer below it.
    Example using “Aeolus True Type Font” (Set against a green background via HTML for example sake)
    Translucent Aeolus True Type Font Good Method
From there the image file can be split up into individual images called “a.png”, “b.png”, etc, and a simple JavaScript string could be used to convert a string to display the picture text like “'MyString'.replace(/(.)/g, '<img src="$1.png">')”.
Example (this is produced by JavaScript):

Internet Explorer 6 also has the added problem of not allowing translucent images, so a hack is needed for this. Basically, an element (like a blank image) needs to have its filter style set like the following (JavaScript DirectX hack...)
style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='IMAGELOCATION', sizingMethod='scale')";

Flash Method
While this method is much quicker to complete and easier to pull off than the previous method, it is also more prone to problems and browser incompatibility. Flash and JavaScript never got along well enough in my book. Anywho, here’s the process. (Source file here)
  • In a new Flash document (v5.0+), create a text box with the following properties:
    • Type: “Dynamic Text”
    • var: MyText
    • Embed (button): Select the set of characters the dynamic text box might display. The less glyphs you select, the smaller the output file will be. I included all alpha-numeric+punctuation in the below example (24.3KB).
  • That’s all you need for the Flash file, so all that’s left now is the JavaScript. The following function will set the text for you inside the movie. Also, you should set the embed (for normal browsers) and object (for IE) tags as different “id”s. The wmode is an important parameter here too, in that it makes the background invisible and the Flash applet more a part of the web page (not a “separate window”).
    <object width="300" height="40" id="CustomFontIE" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">
    	<param name="movie" value="OtherContent/CustomFonts/CustomFont.swf">
    	<param wmode="transparent">
    	<embed src="OtherContent/CustomFonts/CustomFont.swf" wmode="transparent" width="300" height="40" id="CustomFont" type="application/x-shockwave-flash">
    <script type="text/javascript">
    	var IsIE=(navigator.appName.indexOf('Microsoft')!=-1);
    	function SetFlashText(NewText) { document.getElementById('CustomFont'+(IsIE ? 'IE' : '')).SetVariable('MyText', NewText); }
Example: (Set against a green background via HTML for example sake)
Enter text here:
Flash applet:

Online credit card misinformation
Check your gut suspicions before acting

I was just doing my accounting and I noticed I had 3 double-charges on my Capital One credit card that all happened within a 2 day period. I found this to be very odd since I have never been double-charged on any of my credit cards since I started using them 10 years ago when I was 14.

So I went ahead and submitted 2 charge disputes with Capital One, and a third with the other company I saw double-charged. I then finished my accounting, and noticed that the balance showing up on my Capital One did not include those 3 charges. I validated my suspicions by calling up their customer relations department (getting a lady in India) and confirming that the charges only show up once in my account.

I then did my emails to rescind my previous queries into having the double-charges refunded, and also included in the email to Capital One that their web system (or possibly statement system) has an error and needs to be fixed. The double-charges actually weren’t showing up on the same statements. They showed up once (for May 16th and 17th) on my last month’s statement, and then again (May 17th and 19th) on my current month’s statement. Go Figure.

[Edit on 6/13/08] A few days ago, after an annoying downtime on the Capitol One credit card site, I noticed they added a new feature that now shows your latest charges within a certain period of days (15, 30, etc) instead of just the current billing cycle. So I’m pretty sure the above problem was due to them implementing this new system without warning the user or having any indication of the system change in the interface. I do know how annoying change control is, and the problems that come along with implementing new features on websites which may temporarily confuse users, but I’d expect better from a multinational corporation like this. Then again, this isn’t the first time this kind of thing has happened on their website, so I shouldn’t be surprised.
Project About Pages
Big things come in small packages
About Window Concept

I’ve been thinking for a while that I need to add “about windows” to the executables of all my applications with GUIs. So I first made a test design [left, psd file attached]

Unfortunately, this requires at least 25KB for the background alone, and this is larger than many of my project executables themselves. This is a problem for me, as I like keeping executables small and simple.

PNG Signature I therefore decided to scratch the background and just go with normal “about windows” and my signature in a spiffy font [BlizzardD]: (white background added by web browser for visibility)
The above PNG signature is only 1.66KB, so “yay”, right? Wrong :-(, it quickly occurred to me that XP does not natively support PNG.

GIF SignatureMy next though is “what about a GIF?” (GIF is the predecessor to PNG, also lossless): (1.82KB)
I remembered that GIF files worked for VB, so I thought that native Windows’ API might support it too without adding in extra DLLs, but alas, I was wrong. This at least partially solves the problem for me in Visual Basic, but not fully, as GIF does not support translucency, but only 1 color of transparency, so the picture would look horribly aliased (pixilated).

The final solution I decided on is having a small translucency-mask and alpha-blending it and the primary signature color (RGB(6,121,6)) to the “about windows’ ” background.
GIF Signature MaskSince alpha-blending/translucency is an 8 bit value, a gray-scale (also 8 bits per pixel) image is perfect for a translucency mask format for VB: (1.82KB, GIF)
You may note that this GIF is the exact same size as the previous GIF, which makes sense as it is essentially the exact same picture, just with swapped color palettes.

The final hurdle is how to import the picture into C with as little space wasted as possible. The solution to this is to create an easily decompressable alpha-mask (alpha means translucency).
BMP Signature Mask I started with the bitmap mask: (25.6KB, BMP)
From there, I figured there would be 2 easy formats for compression that would take very little code to decompress:
  • Number of Transparent Pixels, Number of Foreground Pixels in a Row, List of Foreground Pixel Masks, REPEAT... (This is a form of “Run-length encoding”)
  • Start the whole image off as transparent, and then list each group of foreground pixels with: X Start Coordinate, Y Start Coordinate, Number of Pixels in a Row, List of Foreground Pixel Masks
It also helped that there were only 16 different alpha-masks, not including the fully transparent mask, so each alpha-mask could be fit within half a byte (4 bits). I only did the first option because I’m pretty sure the second one would be larger because it would take more bits for an x/y location than for a transparent run length number.

Other variants could be used too, like counting the background as a normal mask index and just do straight run length encoding with indexes, but I knew this would make the file much larger for 2 reasons: this would add a 17th alpha-mask which would push index sizes up to 5 bits, and background run lengths are much longer (in this case 6 bits), so all runs would need to be longer (non-background runs are only 3 bits in this case). Anyways, it ended up creating a 1,652 byte file :-).

This could also very easily be edited to input/output 8-bit indexed bitmaps, or full color bitmaps even (with a max of 256 colors, or as many as you wanted with a few more code modifications). If one wanted to use this for normal pictures with a solid background instead of an alpha-mask, just know the words “Transparent” means “Background” and “Translucent” means “Non-Background” in the code.

GIF and PNG file formats actually use similar techniques, but including the code for their decoders would cause a lot more code bloat than I wanted, especially since they [theoretically] include many more compression techniques than just run-length encoding. Programming for specific cases will [theoretically] always be smaller and faster than programming for general cases. On a side note, from past research I’ve done on the JPEG format, along with programming my NES Emulator, Hynes, they [JPEG & NES] share the same main graphical compression technique [grouping colors into blocks and only recording color variations].

The following is the code to create the compressed alpha-mask stream: [Direct link to C file with all of the following code blocks]
//** Double stars denotes changes for custom circumstance [The About Window Mask]
#include <windows.h>
#include <stdio.h>
#include <conio.h>

//Our encoding functions
int ErrorOut(char* Error, FILE* HandleToClose); //If an error occurs, output
UINT Encode(UCHAR* Input, UCHAR* Output, UINT Width, UINT Height); //Encoding process
UCHAR NumBitsRequired(UINT Num); //Tests how many bits are required to represent a number
void WriteToBits(UCHAR* StartPointer, UINT BitStart, UINT Value); //Write Value to Bit# BitStart after StartPointer - Assumes more than 8 bits are never written

//Program constants
const UCHAR BytesPerPixel=3, TranspMask=255; //24 bits per pixel, and white = transparent background color

//Encoding file header
typedef struct
	USHORT DataSize; //Data size in bits - **Should be UINT
	UCHAR Width, Height; //**Should be USHORTs
	UCHAR TranspSize, TranslSize; //Largest number of bits required for a run length for Transp[arent] and Transl[ucent]
	UCHAR NumIndexes, Indexes[0]; //Number and list of indexes
} EncodedFileHeader;

int main()
	UCHAR *InputBuffer, *OutputBuffer; //Where we will hold our input and output data
	FILE *File; //Handle to current input or output file
	UINT FileSize; //Holds input and output file sizes

	//The bitmap headers tell us about its contents

	//Read in bitmap header and confirm file type
	File=fopen("AboutWindow-Mask.bmp", "rb"); //Normally you'd read in the filename from passed arguments (argv)
	if(!File) //Confirm file open
		return ErrorOut("Cannot open file for reading", NULL);
	fread(&BitmapFileHead, sizeof(BITMAPFILEHEADER), 1, File);
	if(BitmapFileHead.bfType!=*(WORD*)"BM" || BitmapFileHead.bfReserved1 || BitmapFileHead.bfReserved2) //Confirm we are opening a bitmap
		return ErrorOut("Not a bitmap", File);

	//Read in the rest of the data
	fread(&BitmapHead, sizeof(BITMAPINFOHEADER), 1, File);
	if(BitmapHead.biPlanes!=1 || BitmapHead.biBitCount!=24 || BitmapHead.biCompression!=BI_RGB) //Confirm bitmap type - this code would probably have been simpler if I did an 8 bit indexed file instead... oh well, NBD.  **It has also been programmed for easy transition to 8 bit indexed files via the "BytesPerPixel" constant.
		return ErrorOut("Bitmap must be in 24 bit RGB format", File);
	FileSize=BitmapFileHead.bfSize-sizeof(BITMAPINFOHEADER)-sizeof(BITMAPFILEHEADER); //Size of the data portion
	fread(InputBuffer, FileSize, 1, File);

	//Run Encode
	OutputBuffer=malloc(FileSize); //We should only ever need at most FileSize space for output (output should always be smaller)
	memset(OutputBuffer, 0, FileSize); //Needs to be zeroed out due to how writing of data file is non sequential
	FileSize=Encode(InputBuffer, OutputBuffer, BitmapHead.biWidth, BitmapHead.biHeight); //Encode the file and get the output size

	//Write encoded data out
	File=fopen("Output.msk", "wb");
	fwrite(OutputBuffer, FileSize, 1, File);
	printf("File %d written with %d bytes\n", 1, FileSize);

	//Free up memory and wait for user input
	getch(); //Pause for user input
	return 0;

int ErrorOut(char* Error, FILE* HandleToClose) //If an error occurs, output
	printf("%s\n", Error);
	getch(); //Pause for user input
	return 1;

UINT Encode(UCHAR* Input, UCHAR* Output, UINT Width, UINT Height) //Encoding process
	UCHAR Indexes[256], NumIndexes, IndexSize, RowPad; //The index re-mappings, number of indexes, number of bits an index takes in output data, padding at input row ends for windows bitmaps
	USHORT TranspSize, TranslSize; //Largest number of bits required for a run length for Transp[arent] (zero) and Transl[ucent] (non zero) - should be UCHAR's, but these are used as explained under "CurTranspLen" below
	UINT BitSize, x, y, ByteOn, NumPixels; //Current output size in bits, x/y coordinate counters, current byte location in Input, number of pixels in mask

	//Calculate some stuff
	NumPixels=Width*Height; //Number of pixels in mask
	RowPad=4-(Width*BytesPerPixel%4); //Account for windows DWORD row padding - see declaration comment
	RowPad=(RowPad==4 ? 0 : RowPad);

	{ //Do a first pass to find number of different mask values, run lengths, and their encoded values
		const UCHAR UnusedIndex=255; //In our index list, unused indexes are marked with this constant
		USHORT CurTranspLen, CurTranslLen; //Keep track of the lengths of the current transparent & translucent runs - TranspSize and TranslSize are temporarily used to hold the maximum run lengths
		//Zero out all index references and counters
		memset(Indexes, UnusedIndex, 256);
		//Start gathering data
		for(y=ByteOn=0;y<Height;y++) //Column
			for(x=0;x<Width;x++,ByteOn+=BytesPerPixel) //Row
				UCHAR CurMask=Input[ByteOn]; //Curent alpha mask
				if(CurMask!=TranspMask) //Translucent value?
					//Determine if index has been used yet
					if(Indexes[CurMask]==UnusedIndex) //We only need to check 1 byte per pixel as they are all the same for gray-scale **This would need to change if using non 24-bit or non gray-scale
						((EncodedFileHeader*)Output)->Indexes[NumIndexes]=CurMask; //Save mask number in the index header
						Indexes[CurMask]=NumIndexes++; //Save index number to the mask

					//Length of current transparent run
					TranspSize=(CurTranspLen>TranspSize ? CurTranspLen : TranspSize); //Max(CurTranspLen, TranspSize)

					//Length of current translucent run
				else //Transparent value?
					//Length of current translucent run
					TranslSize=(CurTranslLen>TranslSize ? CurTranslLen : TranslSize);  //Max(CurTranslLen, TranslSize)

					//Length of current transparent run

			ByteOn+=RowPad; //Account for windows DWORD row padding
		//Determine number of required bits per value
		printf("Number of Indexes: %d\nLongest Transparent Run: %d\nLongest Translucent Run: %d\n", NumIndexes,
			TranspSize=CurTranspLen>TranspSize ? CurTranspLen : TranspSize, //Max(CurTranspLen, TranspSize)
			TranslSize=CurTranslLen>TranslSize ? CurTranslLen : TranslSize  //Max(CurTranslLen, TranslSize)
		TranspSize=NumBitsRequired(TranspSize); //**This is currently overwritten a few lines down
		TranslSize=NumBitsRequired(TranslSize); //**This is currently overwritten a few lines down
		printf("Bit Lengths of - Indexes, Trasparent Run Length, Translucent Run Length: %d, %d, %d\n", IndexSize, TranspSize, TranslSize);

	//**Modify run sizes (custom) - this function could be run multiple times with different TranspSize and TranslSize until the best values are found - the best values would always be a weighted average

	//Start processing data
	BitSize=(sizeof(EncodedFileHeader)+NumIndexes)*8; //Skip the file+bitmap headers and measure in bits
		//Transparent run
		UINT CurRun=0;
		while(Input[ByteOn]==TranspMask && x<NumPixels && CurRun<(UINT)(1<<TranspSize)-1) //Final 2 checks are for EOF and capping run size to max bit length
			if(x%Width==0) //Account for windows DWORD row padding
		WriteToBits(Output, BitSize, CurRun);

		//Translucent run
		BitSize+=TranslSize; //Prepare to start writing masks first
		while(x<NumPixels && Input[ByteOn]!=TranspMask && CurRun<(UINT)(1<<TranslSize)-1) //Final 2 checks are for EOF and and capping run size to max bit length
			WriteToBits(Output, BitSize+CurRun*IndexSize, Indexes[Input[ByteOn]]);
			if(x%Width==0) //Account for windows DWORD row padding
		WriteToBits(Output, BitSize-TranslSize, CurRun); //Write the mask before the indexes
	} while(x<NumPixels);

	{ //Output header
		EncodedFileHeader *OutputHead;
		OutputHead->DataSize=BitSize-(sizeof(EncodedFileHeader)+NumIndexes)*8; //Length of file in bits not including header
	return BitSize/8+(BitSize%8 ? 1 : 0); //Return entire length of file in bytes

UCHAR NumBitsRequired(UINT Num) //Tests how many bits are required to represent a number
	UCHAR RetNum;
	_asm //Find the most significant bit
		xor eax, eax //eax=0
		bsr eax, Num //Find most significant bit in eax
		mov RetNum, al
	return RetNum+((UCHAR)(1<<RetNum)==Num ? 0 : 1); //Test if the most significant bit is the only one set, if not, at least 1 more bit is required

void WriteToBits(UCHAR* StartPointer, UINT BitStart, UINT Value) //Write Value to Bit# BitStart after StartPointer - Assumes more than 8 bits are never written

The code to decompress the alpha mask in C is as follows: (Shares some header information with above code)
void Decode(UCHAR* Input, UCHAR* Output); //Decoding process
UCHAR ReadBits(UCHAR* StartPointer, UINT BitStart, UCHAR BitSize); //Read value from Bit# BitStart after StartPointer - Assumes more than 8 bits are never read
UCHAR NumBitsRequired(UINT Num); //Tests how many bits are required to represent a number --In Encoding Code--

int main()
	//--Encoding Code--
		UCHAR *InputBuffer, *OutputBuffer; //Where we will hold our input and output data
		FILE *File; //Handle to current input or output file
		UINT FileSize; //Holds input and output file sizes
		//The bitmap headers tell us about its contents
		//Read in bitmap header and confirm file type
		//Read in the rest of the data
		//Run Encode
		//Write encoded data out
	//--END Encoding Code--

	//Run Decode
	UCHAR* O2=(BYTE*)malloc(BitmapFileHead.bfSize);
	Decode(OutputBuffer, O2);

/*	//If writing back out to a 24 bit windows bitmap, this adds the row padding back in
	File=fopen("output.bmp", "wb");
	fwrite(&BitmapFileHead, sizeof(BITMAPFILEHEADER), 1, File);
	fwrite(&BitmapHead, sizeof(BITMAPINFOHEADER), 1, File);
	fwrite(O2, BitmapFileHead.bfSize-sizeof(BITMAPINFOHEADER)-sizeof(BITMAPFILEHEADER), 1, File);*/

	//Free up memory and wait for user input --In Encoding Code--
	return 0;

void Decode(UCHAR* Input, UCHAR* Output) //Decoding process
	EncodedFileHeader H=*(EncodedFileHeader*)Input; //Save header locally so we have quick memory lookups
	UCHAR Indexes[256], IndexSize=NumBitsRequired(H.NumIndexes); //Save indexes locally so we have quick lookups, use 256 index array so we don't have to allocate memory
	UINT BitOn=0; //Bit we are currently on in reading
	memcpy(Indexes, ((EncodedFileHeader*)Input)->Indexes, 256); //Save the indexes
	Input+=(sizeof(EncodedFileHeader)+H.NumIndexes); //Start reading input past the header

	//Unroll/unencode all the pixels
		UINT i, l; //index counter, length (transparent and then index)
		//Transparent pixels
		memset(Output, TranspMask, l=ReadBits(Input, BitOn, H.TranspSize)*BytesPerPixel);

		//Translucent pixels
		l=ReadBits(Input, BitOn+=H.TranspSize, H.TranslSize);
		for(i=0;i<l;i++) //Write the gray scale out to the 3 pixels, this should technically be done in a for loop, which would unroll itself anyways, but this way ReadBits+index lookup is only done once - ** Would need to be in a for loop if not using gray-scale or 24 bit output
			Output[i*BytesPerPixel]=Output[i*BytesPerPixel+1]=Output[i*BytesPerPixel+2]=Indexes[ReadBits(Input, BitOn+i*IndexSize, IndexSize)];
	} while(BitOn<H.DataSize);

/*	{ //If writing back out to a 24 bit windows bitmap, this adds the row padding back in
		UINT i;
		UCHAR RowPad=4-(H.Width*BytesPerPixel%4); //Account for windows DWORD row padding
		RowPad=(RowPad==4 ? 0 : RowPad);
		Output-=H.Width*H.Height*BytesPerPixel; //Restore original output pointer
		for(i=H.Height;i>0;i--) //Go backwards so data doesn't overwrite itself
			memcpy(Output+(H.Width*BytesPerPixel+RowPad)*i, Output+(H.Width*BytesPerPixel)*i, H.Width*BytesPerPixel);

UCHAR ReadBits(UCHAR* StartPointer, UINT BitStart, UCHAR BitSize) //Read value from Bit# BitStart after StartPointer - Assumes more than 8 bits are never read
	return (*(WORD*)&StartPointer[BitStart/8]>>BitStart%8)&((1<<BitSize)-1);

Of course, I added some minor assembly and optimized the decoder code to get it from 335 to 266 bytes, which is only 69 bytes less :-\, but it’s something (measured using my Small project). There is no real reason to include it here, as it’s in many of my projects and the included C file for this post.

And then some test code just for kicks...
//Confirm Decoding
BOOL CheckDecode(UCHAR* Input1, UCHAR* Input2, UINT Width, UINT Height); //Confirm Decoding

//---- Put in main function above "//Free up memory and wait for user input" ----
printf(CheckDecode(InputBuffer, O2, BitmapHead.biWidth, BitmapHead.biHeight) ? "good" : "bad");

BOOL CheckDecode(UCHAR* Input1, UCHAR* Input2, UINT Width, UINT Height) //Confirm Decoding
	UINT x,y,i;
	UCHAR RowPad=4-(Width*BytesPerPixel%4); //Account for windows DWORD row padding
	RowPad=(RowPad==4 ? 0 : RowPad);

					return FALSE;
	return TRUE;

From there, it just has to be loaded into a bit array for manipulation and set back a bitmap device context, and it’s done!
VB Code: (Add the signature GIF as a picture box where it is to show up and set its “Visible” property to “false” and “Appearance” to “flat”)
'Swap in and out bits
Private Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFOHEADER, ByVal wUsage As Long) As Long
Private Declare Function SetDIBitsToDevice Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal Scan As Long, ByVal NumScans As Long, Bits As Any, BitsInfo As BITMAPINFOHEADER, ByVal wUsage As Long) As Long
lpBits As Any, lpBitsInfo As BITMAPINFOHEADER, ByVal wUsage As Long, ByVal dwRop As Long) As Long
Private Type RGBQUAD
		b As Byte
		g As Byte
		r As Byte
		Reserved As Byte
End Type
Private Type BITMAPINFOHEADER '40 bytes
		biSize As Long
		biWidth As Long
		biHeight As Long
		biPlanes As Integer
		biBitCount As Integer
		biCompression As Long
		biSizeImage As Long
		biXPelsPerMeter As Long
		biYPelsPerMeter As Long
		biClrUsed As Long
		biClrImportant As Long
End Type
Private Const DIB_RGB_COLORS = 0 '  color table in RGBs

'Prepare colors
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetBkColor Lib "gdi32" (ByVal hdc As Long) As Long

Public Sub DisplaySignature(ByRef TheForm As Form)
    'Read in Signature
    Dim BitmapLength As Long, OutBitmap() As RGBQUAD, BitInfo As BITMAPINFOHEADER, Signature As PictureBox
    Set Signature = TheForm.Signature
    BitmapLength = Signature.Width * Signature.Height
    ReDim OutBitmap(0 To BitmapLength - 1) As RGBQUAD
    With BitInfo
            .biSize = 40
            .biWidth = Signature.Width
            .biHeight = -Signature.Height
            .biPlanes = 1
            .biBitCount = 32
            .biCompression = 0 'BI_RGB
            .biSizeImage = .biWidth * 4 * -.biHeight
    End With
    GetDIBits Signature.hdc, Signature.Image, 0, Signature.Height, OutBitmap(0), BitInfo, DIB_RGB_COLORS
    'Alpha blend signature
    Dim i As Long, Alpha As Double, BackColor As RGBQUAD, ForeColor As RGBQUAD, OBC As Long, OFC As Long
    OFC = &H67906
    OBC = GetBkColor(TheForm.hdc)
    CopyMemory BackColor, OBC, 4
    CopyMemory ForeColor, OFC, 4
    For i = 0 To BitmapLength - 1
        Alpha = 1 - (CDbl(OutBitmap(i).r) / 255)
        OutBitmap(i).r = ForeColor.r * Alpha + BackColor.r * (1 - Alpha)
        OutBitmap(i).g = ForeColor.g * Alpha + BackColor.g * (1 - Alpha)
        OutBitmap(i).b = ForeColor.b * Alpha + BackColor.b * (1 - Alpha)
    Next i
    SetDIBitsToDevice TheForm.hdc, Signature.Left, Signature.Top, Signature.Width, Signature.Height, 0, 0, 0, Signature.Height, OutBitmap(0), BitInfo, DIB_RGB_COLORS
End Sub

C Code
//Prepare to decode signature
	//const UCHAR BytesPerPixel=4, TranspMask=255; //32 bits per pixel (for quicker copies and such - variable not used due to changing BYTE*s to DWORD*s), and white=transparent background color - also not used anymore since we directly write in the background color
	//Load data from executable
	HGLOBAL GetData=LoadResource(NULL, FindResource(NULL, "DakSig", "Sig")); //Load the resource from the executable
	BYTE *Input=(BYTE*)LockResource(GetData); //Get the resource data

	//Prepare header and decoding data
	UINT BitOn=0; //Bit we are currently on in reading
	EncodedFileHeader H=*(EncodedFileHeader*)Input; //Save header locally so we have quick memory lookups
	DWORD *Output=Signature=new DWORD[H.Width*H.Height]; //Allocate signature memory

	//Prepare the index colors
	DWORD Indexes[17], IndexSize=NumBitsRequired(H.NumIndexes); //Save full color indexes locally so we have quick lookups, use 17 index array so we don't have to allocate memory (since we already know how many there will be), #16=transparent color
	DWORD BackgroundColor=GetSysColor(COLOR_BTNFACE), FontColor=0x067906;
	BYTE *BGC=(BYTE*)&BackgroundColor, *FC=(BYTE*)&FontColor;
	for(UINT i=0;i<16;i++) //Alpha blend the indexes
		float Alpha=((EncodedFileHeader*)Input)->Indexes[i] / 255.0f;
		BYTE IndexColor[4];
		for(int n=0;n<3;n++)
			IndexColor[n]=(BYTE)(BGC[n]*Alpha + FC[n]*(1-Alpha));
		//IndexColor[3]=0; //Don't really need to worry about the last byte as it is unused
	Indexes[16]=BackgroundColor; //Translucent background = window background color

//Unroll/unencode all the pixels
Input+=(sizeof(EncodedFileHeader)+H.NumIndexes); //Start reading input past the header
	UINT l; //Length (transparent and then index)
	//Transparent pixels
	memsetd(Output, Indexes[16], l=ReadBits(Input, BitOn, H.TranspSize));

	//Translucent pixels
	l=ReadBits(Input, BitOn+=H.TranspSize, H.TranslSize);
	for(i=0;i<l;i++) //Write the gray scale out to the 3 pixels, this should technically be done in a for loop, which would unroll itself anyways, but this way ReadBits+index lookup is only done once - ** Would need to be in a for loop if not using gray-scale or 24 bit output
		Output[i]=Indexes[ReadBits(Input, BitOn+i*IndexSize, IndexSize)];
} while(BitOn<H.DataSize);

//Output the signature
const BITMAPINFOHEADER MyBitmapInfo={sizeof(BITMAPINFOHEADER), 207, 42, 1, 32, BI_RGB, 0, 0, 0, 0, 0};
SetDIBitsToDevice(MyDC, x, y, MyBitmapInfo.biWidth, MyBitmapInfo.biHeight, 0, 0, 0, MyBitmapInfo.biHeight, Signature, (BITMAPINFO*)&MyBitmapInfo, DIB_RGB_COLORS);

This all adds ~3.5KB to each VB project, and ~2KB to each C/CPP project. Some other recent additions to all project executables include the Hyrulean Productions icon (~1KB) and file version information (1-2KB). I know that a few KB doesn’t seem like much, but when executables are often around 10KB, it can almost double their size.

While I’m on the topic of project sizes, I should note that I always compress their executables with UPX, a very nifty executable compressor. It would often be more prudent to use my Small project, but I don’t want to complicate my open-source code.

One other possible solution I did not pursue would be to take the original font and create a subset font of it with only the letters (and font size?) I need, and see if that file is smaller. I doubt it would have worked well though.
Zelda Treasure Flaws
The only time when having too much money is a problem

I had meant to write this post back when I beat “Zelda: Twilight Princess” a few days after it and the Nintendo Wii came out in 2006, but never got around to it, and the idea of writing about a game that came out long ago seemed rather antiquated. The initiative to write this post popped up again though as I just finished replaying “Zelda: Ocarina of Time” (N64).

I have been a really big Zelda fan for a very long time, and have played most of the series. I got to a GameStop ~8 hours, IIRC, before they started preordering the Wii to make sure I could play Twilight Princess as soon as it came out, as I was very anxious to play it. It was a good thing I did too, because when the Wii actually came out, they were next to impossible to acquire. I knew of many people having to wait in lines well over 15 hours to get one soon after the release, and they were still rarities to attain well over a year later.

While I really enjoyed Twilight Princess, I was very frustrated by a rupee and treasure problem. “Zelda” (NES) and “Link to the Past” (SNES) had it right. Whenever you found a secret in those games it was something really worth it, namely, a heart piece (increased your life meter), or often even a new item. Rupees (in game money) were hard earned through slaying enemies, only rarely given in bulk as prizes, and you almost always needed more. As I played through Twilight Princess, I was very frustrated in that almost every secret I found, while hoping for something worth it like a heart pieces, was almost always a mass of rupees. There were at least 50 chests I knew of by the end of the game filled with rupees that I couldn’t acquire because I was almost always maxed out on the amount I could carry. What’s even worse is that the game provided you a means to pretty much directly pinpoint where all heart pieces were. These problems pretty much ruined the enjoyment of the search for secret treasures in the game. You could easily be pointed directly to where all hearts were, new game items were only acquirable as primary dungeon treasures, and the plethora of rupees was next to worthless.

So, as I was replaying Ocarina of Time, I realized how unnecessary rupees were in that game too. There are really only 2 places in the whole game you need rupees to buy important items; one of which is during your very first task within the first few minutes of the game. The only other use for rupees is for a side quest to buy magic beans which takes up a small chunk of your pocket change through the game, but besides that, there is no point to the money system in the game as you never really need it for anything. What’s even more a slap in the face is that one of the primary side quests in the game just rewards you with larger coin purses to carry more rupees, which again, you will never even need to use.

While these games are extremely fun, this game design flaw just irks me. Things like this will never stop me from playing new Zelda games however, or even replaying the old ones from time to time, especially my by far favorite, Link to the Past, as they are all excellent works. I would even call them pieces of art. Miyamoto forever :-).