Menu

The locked image

December 2, 2010 - .NET, C#, Programming

As with most of my projects, progress is usually slow because it’s something I do “when I feel like it” rather then on a schedule. Because of this I often have a lot of “design-time” where I don’t actually write code but idly wonder about what I could do to make it better.

One consideration I recently decided on was supporting the packaging of the various files — images, sounds, etc — into a zip file. This came about as a result of my experience (easy enough) of using gzipStream to make the size of the serialized LevelSet data a tad smaller (the default levelset, which is 5 levels, still weighs in at about 145KB; most of this is because it stores a lot of PointF structures from the PathedMovingBlock that I recently added, but I won’t get into that. In any case, I considered at the same time- why not allow the loading of files from a zip?

Of course, this is hardly a new idea. I never thought it was. placing the application/game data into one monolithic (and easier managed file-system wise) file is ancient; games have been doing it for ages. In either case, since I had decided to use ZIP (rather then, say, define my own package file like I did with my Visual Basic BCFile project) I had to be able to work with zip files.

Thankfully, there are a lot of free solutions to read,extract, and otherwise deal with zip files from .NET. after some searching, I decided on dotnetzip

. The reasons I decided on it were simple: it’s easy to use, and it supports both extracting as well as creating zips. I’d rather learn one library rather then several to perform the various tasks in the future. The first step was to decide how it was all going to be done.

A little overview of how I had it arranged in the code may help. The Sounds and Images are managed by classes called cNewSoundManager and ImageManager respectively. recent changes have caused me to rewrite the SoundManager to create the “New” SoundManager, which supports different adapter classes for using various sound libraries (of which BASS.NET has become my top choice).

Thankfully, during the creation of Both ImageManager and SoundManager, I made it able to accept a number of paths in their constructors via a overload accepting an array of Strings. In fact, it was the “low-level” implementation for loading; the other overrides simply transformed their data into an array and called it, so I knew that worked. Although my ImageManager could probably be modified to load files from a stream (the ZipFile class can retrieve a inputstream for the files in a zip) the SoundManager could not feasibly do so; many sound libraries will only load from files, and since most of them are really wrappers around even lower level libraries, I couldn’t optimistically assume that I would always be able to convert a filestream into something I could use; I realized that the “Driver” classes could always take the passed in stream and create a file, but that sort of redirects the issue. Instead, I decided to leave it as-is (loading from files) and instead make it so that during the bootstrap phase (where it loads images and sounds) it also extracts the contents of any zip files that are in the application data folder as well.

I chose to change the extension I would use to bbp (Base Block Package) so that the game won’t get confused or screwed up if a standard ZIP happens to be in the same folder. The first question however is where I would extract these files too; obviously it was a temporary operation, so I opted to choose the system Temp folder, which is easily obtained in .NET via Directory.GetTempPath(). I then decided that as I enumerated each zip, they would be extracted not to this temp folder but to another folder that would be created beneath it; this way, the files in each ZIP file can have conflicting names, and still extract properly. (although at this time that will cause both ImageManager and SoundManager to throw a fit, I decided it best to not add a third class that didn’t understand that files in different folders can have different names). The next problem was easy; I simply took all the various folder names and added them to the arrays I passed to the SoundManager and ImageManager constructor. Easy as pie.

Now I needed to make sure that when the program terminated, the files were deleted. During startup it detected if the special temp folder existed and deleted it, but it would be ideal if that folder could also be deleted when the program is closed normally. the problem here is that I was initializing all of this in a static constructor (well, not really, since apparently Mono didn’t like me using static constructors for this, but it was still a static routine). There is, however, no Static “Destructor” that I could use. So I opted to create a small private class implementing IDisposable that I could create as a static variable and initialize with the name of the temporary folder to delete; the Dispose() method would then delete it, easy as pie.

However, upon testing, I encountered an error; apparently, the handle was still open in the dispose routine. After a little digging, it was clear that the problem was at least partially as a result of the Image.FromFile() method, which apparently caches that it was taken from a file as well as the filename and will keep it open as long as the image is around; since I couldn’t always be sure that the temporary class would be disposed after the ImageManager (and therefore the various Images it holds) it was difficult to make sure they were closed.

However, I decided to change my use of the FromFile() method to something that won’t pass the Image class any filename data at all; that way the Image class couldn’t possibly hold the file open, as long as I close it properly myself.

To do so, I replaced the code:

With:

And so far, it’s worked a treat.

Have something to say about this post? Comment!