ScummVM logo ScummVM website - Forums - BuildBot - Doxygen
Contact us - Buy Supported Games: GOG.comDotEmu 
curved edge

PlanetPlanet

Welcome to the ScummVM planet - This aggregates the personal blogs of developers, teams members and active participants from all around the ScummVM community.
If you wish to subscribe to updates to the planet or individual blogs please use the links on the right hand side.
To add your blog to the planet contact DJWillis.

April 24, 2014

Joseph Jezak (JoseJX) - GSoC

Look at what arrived in the Mail today!

Pirates! Adventure! Monkeys! Exile!
I haven't had a chance to finish my work on Quaternions, due to my end of semester responsibilities, but I did get a copy of Myst 3 and EMI for PS2. Awesome! Now I have all of the supported games in ResidualVM, for testing after large changes to the engine.

by Joe Jezak (noreply@blogger.com) at April 24, 2014 11:36 PM

Lukasz Watka (lukaslw) - GSoC

Acceptance for GSoC 2014


I got my e-mail from Google this week and I can’t even describe how amazing it was. I’m able to participate in Google Summer of Code project for ScummVM organization this year. I had so different fellings before and after the outcome of accepted students. I still can’t belive that I got accepted, it’s so amazing.
  

My project allows to implement ‘The Prince and the Coward’ game in ScummVM and translate it into English language. You can check out my draft calendar of work here: Prince and Coward Google Calendar and my repository here: prince-lukaslw.

I recently came back to Gdańsk from my home town after Easter break and I’m going to start working as soon as possible.

This is my first blog post so I think I’ll need a little practise in writing it. I hope it will be readable for you and I'll try to keep it up-to-date during the time of my project.

I’m sure it will be a great summer ;)

by lukaslw (noreply@blogger.com) at April 24, 2014 11:28 PM

April 23, 2014

Sven Hesse (DrMcCoy)

Interview for the Neverwinter Nights Podcast

(Cross-posted from the xoreos website)

This past Saturday, I was being interviewed for the Neverwinter Nights Podcast about the xoreos project. I talked a bit how this project got started, what I’m focusing on and about the current situation.

Not being used to being interviewed, or speaking English for that matter, my nervousness is quite audible. But I hope I still got my points across. :)

So, if you’re interested, give it a listen.

flattr this!

by DrMcCoy at April 23, 2014 08:31 AM

ScummVM News Headlines

The students are known for GSoC 2014!

GSoC 2014 Banner

The accepted Google Summer of Code proposals have been announced yesterday, and we are pleased to say that ScummVM will be mentoring two students, for projects with a Polish theme.

  • Peter Bozsó (a.k.a. uruk), who you may remember from last year, will work on an engine (and translation to English) of the Polish game Sfinx.
  • Lukasz Watka (a.k.a. lukaslw) will work on an engine (and translation to English) of the Polish game The Prince and the Coward.

Additionally, ScummVM is acting as an umbrella organisation during GSoC for our sister project ResidualVM. They will be mentoring three students:

  • Stefano Musumeci (a.k.a. subr3v) will be working on refactoring and optimizing the TinyGL software renderer.
  • Joni Vähämäki (a.k.a. Akz) and Joseph Jezak (a.k.a. JoseJX) will both be working on improving the Escape from Monkey Island engine, tackling bugs and missing features from two different angles.

We expect it will be a productive and interesting summer!

by wjp (nospam@scummvm.org) at April 23, 2014 12:00 AM

April 22, 2014

Joseph Jezak (JoseJX) - GSoC

Fixing Attaching and Detaching For Good!

Continued from the previous entry.

In the previous entry, we identified a problem with attaching and detaching actors in a scene. In this post, we'll look at the solution to the problem and then adapt the solution to the ResidualVM code.

In graphics, we usually think of posing an object as three rotations around the three axes, X, Y, and Z. These rotations are referred to as Euler Angles. Unfortunately, this isn't always the best approach, because while this rotation is easy to conceptualize, the result is sometimes ambiguous depending on how those angles are interpreted. So, while the EMI engine uses Euler Angles when interacting with the user, internally, the engine uses Quaternions. While I won't go into a discussion of Quaternions vs. Euler Angles, or the math behind them, understand that the output generated by the engine is going to involve the conversion of Euler Angles to Quaternions, some computation, then back into Euler Angles.

After examining the disassembled EMI source code and confirming that the engine did use Quaternions internally, I set up some test code using the Irrlicht Quaternion implementation because it was easy to use and easy to test different configurations with. The result of my testing can be found here. This implementation correctly computes the angles as reported by the EMI retail engine, as recorded in this spreadsheet.

Essentially, after some testing, I found that the EMI retail engine computes the new rotation angle by finding the Quaternion of the actor we're attached to, finding the inverse, then multiplying that by the Quaternion of the actor that's being attached. For detaching, the operation is reversed, where we take the attached actor's Quaternion and multiply that by the inverse of the attached actor's Quaternion.

So, what do we need to add to ResidualVM? In ResidualVM, the Quaternion implementation requires two new methods:
  • getInverse() - Finds the Inverse of a Quaternion
  • toEuler() - Returns the Euler Angles from a Quaternion
With these new methods, we can now implement the correct code for attaching and detaching actors in actor.cpp. However, I discovered that the ResidualVM Quaternion code wasn't making much sense and the result was still incorrect. After discussing it with Botje, wjp, somaen and klusark on IRC, I decided to make the Quaternion implementation and usage consistent. This work will be described in the next entry.

by Joe Jezak (noreply@blogger.com) at April 22, 2014 08:58 PM

Peter Bozsó (uruk) - GSoC

Acceptance again

Yes!!! Another year of Google Summer of Code in the embrace of ScummVM! I just can't express my happiness and my gratitude towards the team that they accepted me again! :)

My project this year will be CGE2 engine. As you might have guessed, in some way it's an improvement to our already existing CGE engine. The truth is that they will probably share a lot of code, since Soltys (the game which uses CGE) and Sfinx (the game which will use CGE2) are very similar in a lot of major parts (for example: management of graphics, sounds, scripts, texts), but they also have a lot of significant differences (for example: Soltys has only one controllable character, while Sfinx has two, and a more complicated inventory system to support them.)

To get a better overview of my project, you can read my proposal, and as the work will go on, there will certainly be a ScummVM Wiki page for the engine, where you will be able to track my progress. Also I won't forget about this blog neither, I'll keep it up-to-date with at least one post per week.
So the system is the same as the year before. I hope I'll have as much fun this year as I did in 2013. :)

If everything goes fine, I'll start working on my project this Thursday, so watch out! ;)

by uruk (noreply@blogger.com) at April 22, 2014 05:48 PM

Merging

Hi everybody!

After another bigger break I proudly announce you the pull request of Avalanche engine on GitHub!!!
In the last few days Strangerke and me worked a lot to make the engine more coherent and readable, and we were even able to implement most of the sounds and the harp-playing mini-game alongside it.
There are still lot to do with the game, so my work clearly won't stop by the end of this year's GSoC. If you want to be kept well informed, I suggest you to check the wiki page of the engine regularly, as well as keep on reading this blog, because I won't stop writing posts about my progress neither. (Even if not weekly, thanks to school... but as frequently as it is possible.)
At last I want to say thank you to everyone, especially to my mentors: Strangerke and fuzzie. These two, and practically the whole ScummVM team helped me so much developing my engine that I can't even describe the gratitude I feel toward them! It's a great team to work with and being part of it. I am also very thankful to Google as well since they made my whole project possible with their financial support. Thank you guys, you are all awesome! :)

by uruk (noreply@blogger.com) at April 22, 2014 05:47 PM

Membership

Yesterday, I became a full member of the ScummVM team! I just can't describe how grateful and proud I am right now. It's really like a dream came true for me. :)
This isn't strictly connected to my project, but it's so important for me, I knew I must share it in this blog as well. Many thanks again to everyone who supported me during this long journey! And I am still so far away from the end... :)

by uruk (noreply@blogger.com) at April 22, 2014 05:47 PM

First screenshot

Yes, you are not dreaming! It's true, it's fresh, it's happening RIGHT NOW! Ladies and gentlemen, I proudly present you the very first screenshot of Avalot d' Argent in the holy embrace of SvummVM!
Okay, the colors really need some adjustments, but apart from that you can see that the background image is successfully loaded and shown accurately. (Of course here will come the GUI in the top and the bottom, but that's not my first priority at the moment.)

Lucerna
Let me tell you a little story about my fight with the bits! Everything started after my latest post: I began to mess around with graphics, and I had some mailing with dreammaster, who helped me a lot and gave me an awesome "Graphics in ScummVM for dummies" guide. By following that, I successfully set up the basics of the screen handling in my engine, what you can find here and here. Graph's initial purpose was to replace the Graph unit from Pascal with a C++ class, so it can work in synergy with Lucerna (the class/unit which is responsible for the screen, keyboard and mouse handling) but as time passes I am slowly getting convinced that it will eventually become a replacement for Lucerna. I don't see it clear yet, time will tell...
At the moment Graph is not much more than a wrapper around a Graphics::Surface object.
I got the resolution of the game (what you can find in the header file of Graph) from this function call in the original code: 
 gd:=3; gm:=0; initgraph(gd,gm,'');
Where 3 stands for EGA graphic driver and 0 means EGALo mode, which is equal to 640x200 screen resolution, 16 colors and 4 screen pages. (I'll bother with these "pages" later.)
I also had some difficulties setting up the color palette to mimic the EGA display, but dreammaster told me a lot about it, and by "borrowing" a little bit of code from Strangerke's Mortvielle engine, I get over with it quite fast too. As you may see it in the screenshot at the beginning of the post, there is still a couple of things to do with it, since the colors in the original game are much more livid, but I'll leave it's fix for later.

The next big deal came right after that. I had a long conversation with fuzzie (who is "substituting" Strangerke, my mentor, while he is on holiday) about the topic and she told me a great deal about EGA displays and how did they handle data.
The function which is responsible for loading the data of each screen is Lucerna::load(). The tricky part was here to notice the loop in the original code (which is fuzzie's merit), and recognize that we have to face the "four plane style". After that, recreating it in C++ was quite easy, and fuzzie helped me a lot with that as well.
Here's the result of it:

f.seek(177);

/*for (bit = 0; bit <= 3; bit++) {
port[0x3c4] = 2;
port[0x3ce] = 4;
port[0x3c5] = 1 << bit;
port[0x3cf] = bit;
blockread(f, a0, 12080);
move(a0, a1, 12080);
}*/

Graphics::Surface background;
background.create(_vm->_graph._screenWidth, _vm->_graph._screenHeight, Graphics::PixelFormat::createFormatCLUT8());

byte backgroundHeight = 8 * 12080 / _vm->_graph._screenWidth; // With 640 width it's 151
// The 8 = number of bits in a byte, and 12080 comes from the original code (see above)

for (byte plane = 0; plane < 4; plane++)
for (uint16 y = 0; y < backgroundHeight; y++)
for (uint16 x = 0; x < _vm->_graph._screenWidth; x += 8) {
byte pixel = f.readByte();
for (byte i = 0; i < 8; i++) {
byte pixelBit = (pixel >> i) & 1;
*(byte *)background.getBasePtr(x + 7 - i, y) += (pixelBit << plane);
}
}

_vm->_graph.copySurface(background);

background.free();

As you can see I left the automatically converted loop in a comment. Under that, there's my C++ interpretation of it. You can find the idea behind that loop's core on this page. We guessed it's using four planes because the original loop goes from 0 to 3. So my code does the very same. It first reads the blue component of every pixel: since every bit represent a pixel, we jump in the x loop 8 times every time, and read a byte only after every 8th processed pixel. After that, we loop through the given byte, get the blue component of every pixel, and put them in their place. We do that four times, so every plane (blue, green, red, intensity) is read by the end of the big, plane loop, and finally we got the background image!
The interesting part with this is the following: we read an image with the size of 640x151. That's ok, but after playing the game a couple of times using DOSBox, I clearly saw that it shows the background image (and obviously the whole game too) in a much bigger resolution. I found that the window's size the emulator uses is 640x400. It's very strange since the original Pascal code (as I mentioned above) uses half of that height. To mimic DOSBox's output, I put a little supplement into Graph::copySurface() which copies every line of the background 2 times. After that, my graphical output became almost the same with the one DOSBox produced, but I am still not sure if it's the right way of operation and I am still searching for traces of this "stretching" in the original code.

(P.s.: Thomas: if you read this post and have any clue or suggestion about this resolution-problem, I'd be very glad if you could share it with me in a comment or by mail! :) Is the original output of the game really in 640x400, and if it is, where the "trick" is located in the original code which makes it possible?)

by uruk (noreply@blogger.com) at April 22, 2014 05:47 PM

April 15, 2014

Arnaud Boutonné (Strangerke)

Misc progress...

Since my last post, Dreammaster and I achieved the following feats:
- Add support to Mortevielle DOS, without speech but with a unique English translation and an improved German translation. I thought that a student could work on adding a speech to text library during the Google Summer of Code, but my idea didn't attract any student, so I guess it'll stay like it is currently for the moment.

- Add support for Return to Ringworld. The game is currently in good shape. Eriktorbjorn and L0ngcat found a couple of glitches, several of which being engine bugs, and the card game still has a known broken case, but it's more or less complete. In short, we need more testers.

- Complete the Voyeur engine, which currently only supports the DOS version. I'd love to look deeper in the CDi version (and if I'm completely desperate at the MAC version) but I'm currently spending all my free time on the hardcoded logic of Rex Nebular. At this point we need testers for Voyeur, as only Dreammaster and I played through the game so far.

- Make fuliginous progress on MADS engine and more precisely on Rex Nebular. Without a doubt, I'll finish to reimplement the hardcoded logic of the first group of scenes within the next 24 hours. Based on what we currently see, it would mean that 15-20% of the game logic will be done at this point. Dreammaster on his side is using this hardcoded logic to fix the core engine, and it's working pretty well that way!

I keep my fingers crossed so that Dreammaster and I manage to keep the same pace for the next months, it would mean we would have something robust before the end of the year... It's exciting and addictive! :)

And of course, we need testers!



by Arnaud Boutonné (noreply@blogger.com) at April 15, 2014 08:53 AM

April 12, 2014

Joseph Jezak (JoseJX) - GSoC

The Attached are Attached

After the Manatee's Ride, I hoped that all of the attachment issues were fixed. Unfortunately, that's not the case. Another, more complicated attachment situation presents itself when Guybrush is arriving at Pegnose Pete's house. In this scene, Guybrush puts down the raft pole. Here, the pole is attached to Guybrush, who is in turn connected to the raft. When the pole is detached, the detached location doesn't match the retail version, resulting in the scene below:
How did the Pole get up there?
So, what's the problem in ResidualVM? A series of scripts generating scenarios indicated that the current code for detaching actors was incorrect. Reversing the procedure that was worked out in the Manatee's Ride resulted in the correct detached position for actors connected to another actor.

Great, now the above situation worked correctly right? Nope. It turns out that the situation is more complex when you've got a situation like the scene from the Mysts O' Time with three actors attached. We'll start by checking the values when everything is attached after Guybrush gets on the raft in the first setup of the scene "mot":
swampraftRetailResidualVM
getpos(-0.109778, -0.12, -0.437341)(-0.109778, -0.12, -0.437341)
getworldpos(-0.109778, -0.12, -0.437341)(-0.109778, -0.12, -0.437341)
getrot(0,733,0)(0,13,0)
guybrushRetailResidualVM
getpos(0.4, 0.25, 0.4)(0.4, 0.25, 0.4)
getworldpos(0.359951, 0.13, -0.137574)(0.36995, 0.13, -0.137573)
getrot(0,0.252186,0)(0,0,0)
mot.poleRetailResidualVM
getpos(-0.0222, 0.0287, 0.147)(-0.0222, 0.0287, 0.147)
getworldpos(0.513002, 1.36408, 0.350046)(0.510769, 1.36408, 0.350696)
getrot(-13.8, -87.5, 0)(-13.8, -87.5, 0)

While there are some differences, these results match up pretty well. Let's detach the pole from guybrush and check the coordinates again:

mot.poleRetailResidualVM
getpos(0.513002, 1.36408, 0.350046)(0.411437, 0.2787, 0.548226)
getworldpos(0.513002, 1.36408, 0.350046)(0.411437, 0.2787, 0.548226)
getrot(129.963, -25.8091, 0.0515321)(-13.8, -87.5, 0)

There's a quick fix for the world position, using the result from getworldpos gives us our new coordinates. This makes sense as we don't want to move when detached, so the world coordinates should become the new position. The rotation is more problematic, how did we get there? The resulting rotation seems to be the result of converting from providing the actor's pose in the local reference basis to the world reference basis.

Interestingly, detaching all three actors, then re-attaching them results in an incorrect placement of the pole object, indicating that there's probably some bugs in the original implementation. This appears to have been worked around by constantly setting the position and rotation in the game scripts.

To figure out exactly what was going on, I tested attaching three actors in different poses and recorded the final attached values. Then, I detached the third actor and recorded those values as well. The result of this experiment is recorded in this spreadsheet. Of interest, I found that the position of the actor does not change the resulting orientation, so the only values recorded are the rotation angles retrieved with the :getrot() method. In the next blog post, we'll interpret these values and figure out how EMI actually calculated the attached and detached position angle.

by Joe Jezak (noreply@blogger.com) at April 12, 2014 05:53 PM

April 08, 2014

Joseph Jezak (JoseJX) - GSoC

Fixing the Positions

One of the requirements for the 2014 GSoC is to write a patch and complete a pull request. While we've finished a small fix with the segfault, I wanted to take on something a bit bigger for this pull request. Unfortunately, I didn't have enough time to figure out all of the Actor offsets, embedded functions and structures to complete the SetActorLocalAlpha function for this deadline. Instead, I decided to try and figure out why the chandelier lights were being rendered in the wrong place inside the Governor's Mansion. Unfortunately, this ended up being a much bigger problem than anticipated. Here's what I've found, along with more information about what the original engine does.
Guybrush burns, burns, burns, in a ring of fire
To start with, it was tedious to replay the same part of the game over and over again to get into the mansion. To avoid this in ResidualVM, we can use the debug console to run a script to change the current setup. To do this, we must first load the file that contains this script. Bring up the console with CTRL-D and type:
  • lua_do dofile("_jumpscripts.lua")
Press ESC, to close the console, and this code will be executed. Getting back into the console again, we run the jump_script function:
  •  lua_do jump_script("mansion interior")
Press ESC, and we'll be warped into the Governor's Mansion setup, cool! The jump_script  takes care of setting all of the game states as well, you can consider them to be like bookmarks in story. From one of these bookmarks, we can also jump to specific setups using the switch_to_set("setname"), where setname is the name of the set you'd like to switch to.

Unfortunately, this trick doesn't work in the retail build because we don't have a console to type Lua into. Thanks to klusark, who recently fixed mklab, we can now unpack and repack the .m4b file and insert our own code into the game's data. I'm using two scripts, one which extracts a Lua script, and another that pushes in the new script and rebuilds the .m4b file.

I used these scripts to unpack the _control.lua script and wrote this patch (containing only my own code, so it should be okay to post) to add a dialog box for typing in Lua to be executed directly. This mimics the console in ResidualVM, but works in the retail build as well. You enter this console by pressing the z key. We can  also print variables as the message line for the dialog box by assigning a value to the dd variable.

Now, let's take a look at what's going wrong with the chandelier lights. Examining gmi.lua, the script that contains the setup for the Governor's mansion, we see that the main chandelier actor is located in the variable gmi.chandelier. There's another actor called gmi.chandelier2 that is attached to the first chandelier variable that actually has all of the candle actors attached to it. So, let's explore these variables and see if we can figure out what the problem is.

Using the new console tool, we can press z to bring up our debug console, switch into the Governor's Mansion setup and print out the position for the chandeliers:
  • dofile("_jumpscripts.lua")
  • jump_script("mansion interior")
  • dd = gmi.chandelier:getpos()
  • dd = gmi.chandelier2:getpos() 
Here's the output from the retail version for gmi.chandelier2:
Debug output showing the position vector for chandelier2
Here's the output from ResidualVM:
Debug output showing the position vector for chandelier2
So, the chandelier2 variable is in the wrong position, with the y axis being -1 instead of 1 as in the retail version. Let's see if just moving chandelier2 to the right place fixes the problem:
  • gmi.chandelier2:setpos(0.0, 1.0, 0.0)
The chandelier lights are in the right place!
Indeed it does! So, what broke it? After inserting some more debugging print outs, I found that the actor.attach method was the culprit that was adjusting the position. Specifically, the call to the AttachActor lua method was causing the problem, which leads us to the method:Actor::attachToActor. In this function, the attached actor's position is modified by subtracting the base object's position from the attached actor's position. Checking against the retail version, it seems that the subtraction should be reversed, with the attached actor's position subtracted from the object it's being attached to.

I injected a new lua script with a series of attachments and compared those results with the retail engine and things looked perfect! Or did they...

by Joe Jezak (noreply@blogger.com) at April 08, 2014 09:10 PM

April 01, 2014

ScummVM News Headlines

Edward Snowden joins ScummVM

Happy April Fools' Day! Unfortunately, we haven't really got a new developer... though pull requests are welcome!

We do not normally announce new developers here, but we have an unusual and talented developer recently join the team, Edward Snowden.

When we first got the e-mail from a dot-ru address which we will not be disclosing, we were skeptical.

However upon verifying his PGP key, we are convinced that this is not some kind of April Fools joke and are happy to welcome a talented software developer with enthusiasm to the team.

He is apparently a big fan of point and click adventures, especially Beneath a Steel Sky,, Dreamweb, Woodruff and the Schnibble of Azimuth, and I Have No Mouth, and I Must Scream. His collection allowed him to pass a long stopover at an airport last year and he decided then, once he had sorted some personal issues to get around to contributing some code to ScummVM.

As an avid fan of Larry Niven, he will be initially working on the TSaGE support for Return to Ringworld along with Strangerke, and will be using the nickname "crazyeddie"

Please can we ask the other developers and users not to bother him about any issues outside of ScummVM and not to send any irrelevant e-mails to his ScummVM address at "crazyeddie@scummvm.org" Thanks!

by Digitall (nospam@scummvm.org) at April 01, 2014 12:00 AM

A Landmark Achievement

Happy April Fools' Day! Much thanks to LordHoto for the mock achievement dialog code and for the Hand of Fate and Zak McKracken quotes. Thanks to ST, DrMcCoy, and Strangerke for all their input.

Due to high demand, ScummVM is extremely proud and thrilled to announce the introduction of achievements to various games. Games really have no replay value without achievements, so we hope that our loyal users will enjoy finally being able to play them again the way they've always meant to be played. They're finally ready for the 21st century!

The ScummVM team managed to put together a huge list of achievements that we wanted to see in various games, including some not-yet-supported ones. From there, we just plugged in our simple dialog and achievement tracking code, and were able to add it to all engines easily. Soon integration with the forums will be complete and you will be able to share all your achievements there.

LordHoto was really excited to work on this feature saying, "This is the coolest feature I've added to The Legend of Kyrandia." "First Steam's achievements came to Linux and now this? Linux gamers should be thrilled for all the achievements they can now earn!" added DrMcCoy.

We've gathered up some screenshots of some of our favorite ones:

Full Throttle Screenshot

The Legend of Kyrandia: The Hand of Fate Screenshot

The Curse of Monkey Island Screenshot

Geisha Screenshot

Gabriel Knight: Sins of the Fathers Screenshot 1

Gabriel Knight: Sins of the Fathers Screenshot 2

Gabriel Knight: Sins of the Fathers Screenshot 3

Zak McKracken and the Alien Mindbenders Screenshot

by clone2727 (nospam@scummvm.org) at April 01, 2014 12:00 AM

March 25, 2014

Paul Gilbert (Dreammaster)

Rex-tacular

What a difference a mere week can make in terms of progress. At the beginning of last week only the basic background from one scene was showing, although there was more code implemented in the engine that wasn't yet working. Now, a week later, behold the progress of Rex Nebular on ScummVM:



That's right, in a rush of effort this week, the Rex Nebular engine has already passed several milestones in development:
  • The user interface is now partly implemented. As you can see from the above picture, the user interface background is showing, with the sections for the actions, items, and the animated inventory item showing.
  • The scene background is now displaying, along with basic sprite animations within the scene. The fires are burning, and the hamster "auxiliary power" :) is running.
  • Basic player display. Rex is in his default position for the scene, facing the door.
  • Behind the scenes, we've already completed the logic for a couple of the game scenes, although we're obviously not able to test them properly yet.
The focus for the upcoming week will now be on the beginnings of user interaction and action handling. This means getting walking working, selections in the user interface, and actually being able to execute actions. A lot of the needed code is already in place, but will require some heavy duty debugging and comparison against the original to figure out where things are going wrong. We're somewhat hampered by the complexity of the core engine when doing debugging, but we'll get there eventually. :)

by Dreammaster (noreply@blogger.com) at March 25, 2014 02:44 AM

March 14, 2014

Paul Gilbert (Dreammaster)

Rexing it Up

Work has been progressing furiously with the MADS engine, and Rex Nebular support. Whilst I was able to us some code from the previously engine version, a lot of it turned out to be more M4 specific than I'd anticipated, and had to be freshly disassembled and/or rewritten. Additionally, with my greater experience writing engines, I've been able to lay out a cleaner separation of methods into classes than previously, particularly keeping in mind extensibility for having separate game logic for the other MADS games. As a result, the new MADS engine is already significantly different from the old M4 engine.

After a great deal of implementation, not to mention debugging of my code and comparison against the original running in DOSBox, I finally have the background of my test scene showing! See below for the first 'new engine' view of Rex Nebular in all it's glory:


Particularly auspicious considering I see that Rex Nebular has just been released for sale on GOG. So presuming that the engine gets finished this time, people will easily be able to obtain a copy. :)

What's next? In order to reach my original milestone of having an animation sequence playing correctly, it meant that I needed to properly implement the entire frame step and rendering logic from the game, which had a great deal of code, and many different secondary methods that I've encapsulated into a multitude of classes for sprite sets, pending sprites, active sequences, text display, and lots of other things. Not to mention a pretty complicated precursor to M4's RGBList, where resources loaded in for both the scene and for sprites are allocated chunks of the palette space.

I'm currently focusing on debugging the standard sprite drawing, which is used by the animation class. I'm hoping with a bit more work, I can it to properly show the animation sequence. Doing so will also help ensure that all the sprite display logic for the scene also works correctly. This will make it easier to start work later on for full blown game scenes, since all the necessary sprite display will already be done, and I can concentrate on things like player movement, action handling, etc.


by Dreammaster (noreply@blogger.com) at March 14, 2014 01:00 AM

March 09, 2014

ResidualVM News Headlines

Welcome to Google Summer of Code 2014

GSoC 2014 Banner

We are happy to announce that we are participating in this year's Google Summer of Code under the umbrella of our sister project ScummVM.

In case you don't know it, the Google Summer of Code program allows students all over the world to participate in open source projects like ResidualVM and get paid for it.

If this sounds like something you would be interested in, please take a look at the merged list of ideas, the ResidualVM ideas are at the bottom.

You can also propose projects of your own; we'll be happy to help you shape your idea into a concrete project. If you want to participate or have questions about GSoC, come talk to us on IRC irc.freenode.net; #residualvm) or ask on the forums.

by aquadran (nospam@residualvm.org) at March 09, 2014 12:00 AM

March 05, 2014

ScummVM News Headlines

Summertime and the codin' is easy

GSoC 2014 Banner

We are very excited to announce that we are going to be doing things a little differently this year. In addition to our own participation in Google Summer of Code, ScummVM will also be acting as an umbrella organization for our sister project, ResidualVM.

Both projects will be working hand in hand with students and mentors over the coming months. The first obvious sign of this collaboration can be found in our merged GSoC ideas page.

So if you're interested in being a GSoC student this year, please take a look at the ideas page and come and talk to us. We love to welcome potential students on IRC and the ScummVM and ResidualVM mailing lists are also splendid places to start! We have a lot of exciting things to suit a range of skillsets and we also warmly welcome task proposals other than those on the list if you feel so inspired!

by Strangerke & DJWillis (nospam@scummvm.org) at March 05, 2014 12:00 AM

February 19, 2014

Paul Gilbert (Dreammaster)

Explosive decompression

Looking back, despite my earlier promises of posting more regularly, it seems I've been a bit tardy in posting updates, and quite a bit has happened. So lets get up to date with what's been happening with me. Firstly, we finally got some proper bug reports for Return to Ringworld, and have been working to fix them. Well, moreso Strangerke than me, so thanks go to him for his work in fixing bugs when he has the time.  As of right now, the card mini-game still isn't fully functional, but at least the main game is that much more polished now than it was.

Secondly, as of Monday, I finally merged the next game I was working on, Voyeur, into the project's master branch. This is a weird little game that Strangerke put me onto, because the original DOS executable had debug information embedded in it, which made it somewhat easier to implement. Still a lot of work to understand the contents of methods, reimplement them, and get the game in a stable state. But it made a nice change of pace from all the TsAGE work, where we had to disassemble everything from scratch. As of right now, the Voyeur engine only supports the DOS version. Apart from DOS, there were also CDi and Macintosh versions released. The Mac version at least may be supported at some point in the future, although there is some extra complexities we'd have to worry about due to the Mac-specific data in it, such as rasterized fonts that the PC version doesn't have. Adding support for it might make a nice future mini-project for a Mac enthusiast.

So what's next? Apart from further bugfixes for both Return to Ringworld and Voyeur, I'm already onto my next project.. once again tackling the white elephant of Nex Nebular, for the 3rd time. This time, though, things will hopefully be different. Using my experience with disassembling the TsAGE sound system, I tackled the sound system first, which was the major previous remaining stumbling block. And as of last Sunday, I finally got it working! That's right, my new embryonic MADS engine was able to play back the explosive decompression sound you get when you fail the copy protection check. It was a wonderful feeling, after spending a week reverse engineering and then implementing the code, then having spent an afternoon trying to debug my code and compare bytes being written to the ScummVM FM_OPL driver against the port writes in the original running under DosBox, to finally get everything right, and hear the sound coming out of my speakers.

 It turns out that despite some funky assembly tricks in the player that I had to work around, the player itself is actually a fairly compact, simple implementation. I'm no sound expert (as my frequent prior complaints make clear :) ), but I understood enough to give the various methods in my code halfway decent names, and name at least some of the fields. Hopefully a further analysis, now that I have cleaner C++ code, will help me better understand what's going on, and give the rest of the fields more proper names.

Now that the issue of sound support is taken care of, how am I going to proceed from here? I have a lot of code back from the earlier combined m4 engine which had code from both Rex Nebular and Orion Burger. Part of the trouble with the earlier attempts, I think, was that we were overcomplicating matters. As such, I'll only gradually put in code as I need it, and not worry so much about M4. That'll help me keep the focus more on getting Rex Nebular to work. Not that I'm going to just chuck out all M4 code from the engine. I've already started work on making the core classes, such as the previously named 'M4Surface' class, having a cleaner structure more suited for multiple games. For example, it'll now have it's own class factory, which will abstract having a base MSurface class that will have different descendants that implement the various game specific load/display logic. That seems cleaner than littering the code with "if isM4()" checks, and having methods like "rexLoadBackground". As far as a caller is concerned, they'll just initialise a surface, and the factory will take care of giving them the right kind for their game.

With a cleaner separation of game logic into descendent classes, I'll be able to, for example, keep in place special cases we'd already figured out for Riddle of Master Lu, and make it that much easier to implement support for it later. So that should keep a certain team member who shall remain nameless happy.  :)

At the moment, I'm currently aiming for getting all the initial engine setup sorted out, and playing back the animation sequence for when you get the copy protection answer wrong. As I mentioned above, this will involve pulling some code from the old project to save time, but I'm also already doing a fair amount of refactoring and cleanup, such as changing char *'s to Common::String, using Common::Point's, properly commenting the code, and so on. Given the amount of previous work done on the old M4 engine and the mostly complete disassembly I had of Rex Nebular, we can hopefully expect rapid progress in functionality, and eventually enjoy Rex's escapdes as he quests for the vase he's been sent to find.

by Dreammaster (noreply@blogger.com) at February 19, 2014 03:03 PM

February 02, 2014

James Woodcock

Beneath A Steel Sky Enhanced Soundtrack Version 1.2.1 Released – Missing Track

A missing track in the *.flac version of my enhanced soundtrack for Beneath A Steel Sky has been fixed in version 1.2.1.

by James Woodcock at February 02, 2014 10:15 AM

January 23, 2014

Adrian Astley (RichieSams) - GSoC

Getting Started with Git

We're using Git in my Elements of Databases class this semester, so I though I would put together a crash course for Git. So here goes!

What is Git?

TL;DR explanation of what Git is:
Git was designed to allow multiple users to work on the same project as the same time.
It also serves as a way to save and display your work history.

First things first

There are various ways you can use git (command line, SourceTree, GitHub client, TortoiseGit, or some combination). My personal preference is SourceTree for mostly everything, TortoiseGit for merge conflicts, and command line only when necessary.

So the first step is to download and install the software that you would like to use. I am going to be showing SourceTree, but it should be a similar process for other programs.

Go to this link: http://www.sourcetreeapp.com/download/
The download should start within a couple seconds.

Run the exe and follow the directions.

Setting up SourceTree


  1. When you first start SourceTree, it will ask you where git is installed. 
  2. If it's not installed, then it can do it for you if you click "Download an embedded version of Git"
  3. Next it will ask you about Mercurial. You can just say "I don't want to use Mercurial"
  4. You will then be presented with this: 
  5. Fill our your name and email. This is the information that will show up when you commit.
  6. Leave the two checkboxes checked.  
    • The first allows SourceTree to automatically update git configurations when you change options within SourceTree. 
    • The second makes sure all your line endings are the same, so there are no conflicts if you move from Windows to Mac, Linux to Windows, etc.
  7. Accept SourceTree's Licence Agreement and Click "Next" 
  8. This next dialog box is for if you use SSH. This can be set up later if you choose to use it. In the meantime, just press "Next" and then "No"
  9. The last dialog box gives you the opportunity to sign into any repository sites you use. This makes cloning repositories much easier and faster. 
  10. Click "Finish" and you should be in SourceTree proper: 

Creating a Repository

So now you have everything installed, let's actually get into usage. The first thing you'll want to do is create a repository.  You can think of this as a giant box to hold all your code and changes. So let's head over to GitHub. Once you've logged in, you should see something similar to this:


  1. Click the green, "New repository" button on the right-hand side of the web page. The page should look something like this: 
  2. Name the repository and, if you would like, add a description.
  3. Click the radio button next to "Private", since all our class repos need to be private
  4. Click on the combobox labelled "Add git ignore" and select Python. Github will then automatically create a .gitignore files for us.
    • A '.gitignore' file tells git what type of files or directories we don't want to store in our repository.
  5. Finally, click "Create repository"

Cloning a Repository

Now that we've created the repository, we want a copy of it on our local machine.
  1. Open up SourceTree
  2. Click the button in the top left corner of the program called "Clone/New" 
  3. You should get something that looks like this: 
  4. If you logged in with your GitHub account earlier, you can press the Globe-looking button to list all your repositories. 
    1. Just select the one you want to clone and press OK. 
    2. Otherwise, go to the repository on GitHub and copy the url labelled "HTTPS clone url" 
      • (You can use SSH if you want, but that's beyond the scope of this tutorial)
    3. Paste the url into SourceTree 
  5. Click on the ellipses button next to "Destination path" and select an ***EMPTY*** folder where you want your local copy to reside. 
  6. Click "Clone" 

Basic Git Usage

Now let's get into the basic usage of git

Let's add a python file with some basic code. So browse to the folder that you just created and create a file called hello.py. Open it with your favorite editor and write a basic Hello World.

Ok now that we've created this file, let's add it to our repository. So let's go over to SourceTree.

  1. Make sure you're in the "File Status" tab 
    • This tab lists all the changes that you've done since your last commit with a preview window on the right
  2. Click on hello.py
  3. Add the file to the "Stage" by clicking "Stage file" or by using the arrows in the left column.


  4. Just what is the stage? Think of it as a temporary storage area where you prepare a set of changes before committing. Only the items that are on the stage will be committed. This comes in handy when you want to break changes into multiple commits. We'll see an example of that later.

  5. Press the "Commit" button in the top left of SourceTree.
     You should get something like this: 
  6. Add a message to your commit and click the "Commit" button at the bottom right-hand corner. I'll explain message formatting later. 
  7. Now if you go to the "Log/History" tab, you will see your new commit:
     

You might notice that SourceTree tells you that "master is 1 ahead". What does this mean?

When you commit, everything is local. Nothing is transmitted to GitHub. Therefore, SourceTree is telling you that your Master branch is 1 commit ahead of GitHub.

So let's fix that! 
  1. Click the "Push" button. 
  2. And press "Ok"
Now everything is synced to GitHub.

Commit Style and Commit Message Formatting

Before I go any further I want to make a few comments on commit style and commit message formatting.

Commits should be treated as small logical changes. A stranger should be able to look at your history and know roughly what your thought process was. Also, they should be able to look at each commit and know exactly what you changed. Some examples would be "Fixed a typo on the output message" "Added an iteration counter for debug purposes"

With that in mind, Git has a standard commit message format:

<SYSTEM_NAME_IN_ALL_CAPS>: <Commit message>

[Commit body / Any additional information]

So an example would be:
COLLATZ: Added an iteration counter for debug purposes

I wanted to know how many times the function was being called
in each loop.

SYSTEM_NAME refers to whatever part of the project the commit affects. IE. SOUND_SYSTEM, GRAPHICS_MANAGER, CORE. For our class projects, we probably won't have subsystems, so we can just use the project name, ie. for this first project COLLATZ.
The commit message should be short and to the point. Any details should be put in the body of the commit.
If you have a commit body, there should be a blank line between it and the commit message.


More Git Usage Examples

Let's do another example commit

  1. Modify your hello.py file to add these lines: 
  2. Save


Now, let's commit

  1. Go back to the "File Status" tab in SourceTree 
  2. If you look at the preview pane, you'll see the lines we added highlighted in green 
However, it would make sense to split the changes into two commits. How do we do that?
  1. Click on the first line you would like to add to the Stage. Holding down shift, click on the last line you want to add to the stage. 
  2. Now click, "Stage Selected Lines" 
  3. The changes moved to the Stage! 
  4. Commit the changes using the same instructions as before
  5. Now let's stage and commit the remaining changes. You can once again select the lines you want and use "Stage Selected Lines", or you can stage the entire chunk. 
    • A chunk is just a group of changes that happen to be near each other.
  6. Now there's an extra space that I accidentally added. 
  7. Rather than going to my editor to delete it, I can let git do the work.
  8. Select the lines you want to discard and press "Discard Selected lines"
************ WARNING *************
Once you discard changes, they are gone forever. As in, no getting them back. So be VERY VERY careful using discard.
************ WARNING *************

Pulling

So far, we've been the only ones on our repository. However, the whole point of using a repository is so that multiple people can work at the same time.

This is a portion of the commit history for an open source project I'm part of called ScummVM:
As you can see, there are many changes going on all the same time.

Let's imagine a scenario:

You and your partner Joe are working on some code at the same time. You make some changes and commit them. However, in the meantime, Joe also made some changes, commited them, and pushed them to the repository. If you try and push, git will complain, and rightfully so. You don't have the most up-to-date version of the repository. Therefore, in order to push your changes to the repository, you first need to pull Joe's changes and merge any conflicts.

How do you pull?
Just click the "Pull" button in SourceTree. Click ok and wait for git to do its work. Once it finishes, you'll notice Joe's new commit have shown up in your history. *Now* you can push.

Therefore, it's common practice to always pull before you push. Nothing will go wrong if you don't, since git will catch the error, but it's a good habit to get in.

Tips and Tricks

Stashing

So say you have a group of changes that you're working on, but you want to try a different way to fix the problem. One way to approach that is by "Stashing". Stashing stores all your current changes and then reverts your code back to your last commit. Then at a later time you can restore the stash back onto your code.


  1. To stash changes, just press the stash button in SourceTree 
  2. To bring your changes back, right click on the stash you want and click "Apply" 
  3. It will bring up a dialog box like this: 
  4. If you leave the "Delete after applying" checkbox unchecked, the stash will stay, even after it's been restored. I usually delete a stash after applying, but it can be useful to keep it if you want to apply it somewhere else.

Stashing can also be done on the command line with:

  • git stash
  • git stash pop

The first command stashes changes and the second restores the last stash and then deletes it

Going back in history

Say you want to go back to a certain state in your history, perhaps because that was the last time your code worked, or maybe to see if a certain stage also had a certain bug.

  1. First, stash or commit all your current changes. If you don't, you could lose some or all of your work.
  2. Then, in the Log/History tab of SourceTree, double click on the commit you would like to move to. You should get a dialog box like this: 
  3. That's to confirm that you want to move. Click yes.
  4. Now your code should have changed to reflect the state of the commit you clicked.
  5. If you want to make any changes here, first create a branch. That's covered in the next section.
  6. To move back to the end, just double click the last commit you were on.


Branching

Consider that you and Joe are both trying to come up with a solution to a bug. Rather than both working in 'master' and potentially messing up each other's code, it would make more sense if you each had a separate instance of the code. This can be solved with branching.

So for example, you could work in a branch called, 'solution1' and Joe could work in a branch called 'solution2'. Then when everything is finished, you choose the branch you like best and use git to merge that branch back into 'master'.

So to start, let's create a branch.

  1. Easy enough. Just click the "Branch" button http://i.imgur.com/BAmPmg2.png
  2. Name the branch and press "Create Branch". Branch names can not contain spaces and are case sensitive
  3. You should now be in your new branch. Any commits you do will commit to this branch.

To move to another branch, or "checkout" a branch, simply double click the branch in your commit history or double click the branch in the branch list in the left column

Now that you've committed some changes to another branch, let's merge it back into master

  1. Double click on master to check it out
  2. Right click on the last commit of the branch you would like to merge in and select "Merge..."
  3. Click "Ok"
  4. If there are no conflicts, the merge will be successful and master will contain all the changes from the other branch
  5. Remember to push!


Well, that's pretty much all the basics. There are many many many more things you can do with Git, but you can worry about that when you the situation arises. 

You are more than welcome to leave a comment if you have any questions or if you have any suggestions for improving what I've written or the structure of how it's organized. Also, please let me know if you find any errors.

Have fun coding!
-RichieSams

by RichieSams (noreply@blogger.com) at January 23, 2014 04:03 AM

Obligatory "Hello world!"

Hello world!

Welcome to my new blog: 'RichieSam's Adventures in Code-ville'. This will be the place I share the coding experiences have and learn while working on my various projects. With that said, who am I and what am I working on?

I'm a 21 year old, fourth year student studying at The University of Texas at Austin. I'm majoring in Mechanical Engineering with a minor in Computer Science. I thoroughly enjoy programming, both the thrill of gettting something to work and the science/math of algorithms and data structures. The majority of my programming projects have revolved around games. The first major project I did was creating an application that tracked guild currency for my guild. My latest project is a suite of tools to let users install, modify, and create game asset modifications of the game League of Legends. It required reverse engineering quite a few file formats and learning how to hook the game process in order to allow run-time asset swapping.

The two big projects I'm working on right now are The Dargon Project and Z-engine for ScummVM. The Dargon Project is the aforementioned suite of applications. Z-engine is my project for Google Summer of Code.

Z-engine:
The Z-Engine is used in the games Zork Nemesis and Zork Grand Inquisitor. Marisa Chan created a C implementation of the engine, but it is only for desktop and requires configuration files. The project aims to create a ScummVM engine using Marisa Chan’s implementation code as a guide into the Zork file structure and engine design. That is, it will not simply adapt the current implementation to the ScummVM engine structure. Rather, it will create a new engine, using the file structures and event implementations in Marisa Chan’s code as a reference. ScummVM will allow these games to be played on a variety of platforms and a redesign will remove the need for configuration files. Lastly, it will mean that ScummVM will support all of the Zork point'n'click adventure games.

I'm absolutely thrilled be one of the lucky people to be a part of Google Summer of Code. ScummVM is an amazing group of developers and I'm really looking forward to being a part of that.

Well, I guess that's it for now. My next post will most likely be about that start of GSoC.

Until then,
-RichieSams

by RichieSams (noreply@blogger.com) at January 23, 2014 02:46 AM

The making of psychedelic pictures (AKA, the panorama system)


In the game, the backgrounds are very long 'circular' images. By circular, I mean that if you were to put two copies of the same image end-to-end, they would be continuous. So, when the user moves around in the game, we just scroll the image accordingly. However, being that the images are flat, this movement isn't very realistic; it would seem like you are continually moving sideways through an endless room. (Endless staircase memories anyone?)


To counter this, the makers of ZEngine created 'ZVision': they used trigonometry to warp the images on the screen so, to the user, it looked like you were truly spinning 360 degrees. So let's dive into how exactly they did that.

The basic premise is mapping an image onto a cylinder and then mapping it back onto a flat plane. The math is all done once and stored into an offset lookup table. Then the table is referenced to warp the images.
Without warping

With warping

You'll notice that the images are pre-processed as though they were captured with a panorama camera.

Video example:


Here is the function for creating the panorama lookup table:
void RenderTable::generatePanoramaLookupTable() {
memset(_internalBuffer, 0, _numRows * _numColumns * sizeof(uint16));

float halfWidth = (float)_numColumns / 2.0f;
float halfHeight = (float)_numRows / 2.0f;

float fovRadians = (_panoramaOptions.fieldOfView * M_PI / 180.0f);
float halfHeightOverTan = halfHeight / tan(fovRadians);
float tanOverHalfHeight = tan(fovRadians) / halfHeight;

for (uint x = 0; x < _numColumns; x++) {
// Add an offset of 0.01 to overcome zero tan/atan issue (vertical line on half of screen)
float temp = atan(tanOverHalfHeight * ((float)x - halfWidth + 0.01f));

int32 newX = int32(floor((halfHeightOverTan * _panoramaOptions.linearScale * temp) + halfWidth));
float cosX = cos(temp);

for (uint y = 0; y < _numRows; y++) {
int32 newY = int32(floor(halfHeight + ((float)y - halfHeight) * cosX));

uint32 index = y * _numColumns + x;

// Only store the x,y offsets instead of the absolute positions
_internalBuffer[index].x = newX - x;
_internalBuffer[index].y = newY - y;
}
}
}

I don't quite understand all the math here, so at the moment it is just a cleaned-up version of what Marisa Chan had. If any of you would like to help me understand/clean up some of the math here I would be extremely grateful!

Putting aside the math for the time being, the function creates an (dx, dy) offset at each (x,y) coordinate. Or in other words, if we want the pixel located at (x,y), we should instead look at pixel (x + dx, y + dy). So to blit an image to the screen, we do this:
  1. Iterate though each pixel
  2. Use the (x,y) coordinates to look up a (dx, dy) offset in the lookup table
  3. Look up that pixel color in the source image at (x + dx, y + dy)
  4. Set that pixel in the destination image at (x,y)
  5. Blit the destination image to the screen using OSystem::copyRectToScreen()

Steps 1 - 4 are done in mutateImage()
void RenderTable::mutateImage(uint16 *sourceBuffer, uint16* destBuffer, uint32 imageWidth, uint32 imageHeight, Common::Rect subRectangle, Common::Rect destRectangle) {
bool isTransposed = _renderState == RenderTable::PANORAMA

for (int y = subRectangle.top; y < subRectangle.bottom; y++) {
uint normalizedY = y - subRectangle.top;

for (int x = subRectangle.left; x < subRectangle.right; x++) {
uint normalizedX = x - subRectangle.left;

uint32 index = (normalizedY + destRectangle.top) * _numColumns + (normalizedX + destRectangle.left);

// RenderTable only stores offsets from the original coordinates
uint32 sourceYIndex = y + _internalBuffer[index].y;
uint32 sourceXIndex = x + _internalBuffer[index].x;

// Clamp the yIndex to the size of the image
sourceYIndex = CLIP<uint32>(sourceYIndex, 0, imageHeight - 1);

// Clamp the xIndex to the size of the image
sourceXIndex = CLIP<uint32>(sourceXIndex, 0, imageWidth - 1);

if (isTransposed) {
destBuffer[normalizedY * destRectangle.width() + normalizedX] = sourceBuffer[sourceXIndex * imageHeight + sourceYIndex];
} else {
destBuffer[normalizedY * destRectangle.width() + normalizedX] = sourceBuffer[sourceYIndex * imageWidth + sourceXIndex];
}
}
}
}

  • Since the whole image can't fit on the screen, we iterate over a subRectangle of the image instead of the whole width/height.
  • destRectangle refers to where the image will be placed on the screen. It is in screen space, so we use it to offset the image coordinates in the lookup table (line 10).
  • We clip the coordinates to the height/width of the image to ensure no "index out of range" exceptions.


You may have noticed the last bit of code hinted at panoramas being transposed. For some reason, the developers chose to store panorama image data transposed. (Perhaps it made their math easier?) By transposed, I mean a pixel (x,y) in the true image would instead be stored at (y, x). Also the image height and width would be swapped. So an image that is truly 1440x320 would instead be 320x1440. If you have any insights into this, I'm all ears. Swapping x and y in code was trivial enough though. I would like to note that prior to calling mutateImage, I check if the image is a panorama, and if so, swap the width and height. So the imageWidth and imageHeight in the function are the width/height of the true image, not of the actual source image. This code that does the swap can be found in the function RenderManager::renderSubRectToScreen.

Well, that's it for now. My next goal is to get the majority of the events working so I can load a room and the background image, music, etc. load automatically. So until next time, happy coding!

-RichieSams

by RichieSams (noreply@blogger.com) at January 23, 2014 02:28 AM

Implementing a generic single value container in c++

In my previous post I explained the format of the script system for ZEngine. Each Puzzle has a Results section which essentially stores function names and their arguments:
results {
action:assign(5985, 0)
background:timer:7336(60)
event:change_location(C,B,C0,1073)
background:music:5252(1 a000h1tc.raw 1)
}
I wanted to be able to store each action inside a struct, and then have a linked list of all the structs. However, the problem is that both the number of arguments and the size of the arguments are variable. Marisa Chan's solution was to store all the arguments in a space delimited char array. IE:
char arguments[25] = "1 a00h1tc.raw 1";

Simple, but not without it's problems.
  1. Since the char array is in a struct, the size is fixed. In order to make sure we never overflow, we have to allocate a fairly large array. That said, in this particular case, each 'large' array in this case would only be ~30 bytes per struct.
  2. By storing everything as strings, we put off parsing till the action function is actually called. At first glace, this doesn't seem too bad, since the data will have to be parsed anyway. However, this method forces it to be parsed at every call to that action function.

Another option was to have everything stored in a linked list of void pointers. However, I don't think I need to convince anyone that void pointers are just gross and using them would be just asking for problems.

What I really wanted was a typed way to store a variably typed (and sized) value. Therefore I created what I'm calling the "Object" class. (I'm up for suggestions for a better name)

The heart of the class is a union that stores a variety of pointers to different types and an enum that defines what type is being stored:
class Object {
public:
enum ObjectType : byte {
BOOL,
BYTE,
INT16,
UINT16,
INT32,
UINT32,
FLOAT,
DOUBLE,
STRING,
};

private:
ObjectType _objectType;

union {
bool *boolVal;
byte *byteVal;
int16 *int16Val;
uint16 *uint16Val;
int32 *int32Val;
uint32 *uint32Val;
float *floatVal;
double *doubleVal;
Common::String *stringVal;
} _value;
}
_objectType keeps track of what type of data the object is storing and _value points to the actual data. If _value were instead to hold the actual data value, the union would be forced to sizeof(Common::String), which is quite large (~34 bytes), due to internal caching. Then we're back to the argument of storing things in containers much larger than what they need. By putting the data on the heap and only storing pointers to the data, we save the wasted space, but at the CPU cost of heap allocation.

Now that the data is stored, how do we get it back? My original idea was to have implicit cast operators:
operator bool();
operator byte();
operator int16();
.
.
.
However, LordHoto, one of the GSoC mentors and ScummVM developers, brought my attention to the problems that can arise when using implicit casting. For example, a user could try to cast the data to a type that wasn't stored in the Object and the cast would work, but the data would be completely corrupted. Also, from a user point of view, it wasn't intuitive.

Therefore, I removed the cast operators and created accessor methods:
bool getBoolValue(bool *returnValue) const;
bool getByteValue(byte *returnValue) const;
bool getInt16Value(int16 *returnValue) const;
.
.
.

bool Object::getBoolValue(bool *returnValue) const {
if (_objectType != BOOL) {
warning("'Object' not of type bool.");
return false;
}

*returnValue = *_value.boolVal;
return true;
}
This adds a layer of type semi-protection to the class.

Lastly, I added assigment operators to the class, but rather than making this post even longer, I'll just link the full source here and here.


Advantages of 'Object' class
  • Can store relatively 'any' type of data. (Any type not currently supported could be trivially added)
  • Only uses as much space as needed.
  • Transforms dynamically typed data into a statically typed 'box' that can be stored in arrays, linked lists, hashmaps, etc. and can be iterated upon
Disadvantages of 'Object' class
  • Adds a small memory overhead per object. ( 1 byte + sizeof(Operating System pointer) )
  • Adds one heap memory allocation per object


So is it better than Marisa Chan's implementation? It really depends on what you define as better. While it does save memory, only requires data to be parsed once, and, in my opinion, adds a great deal of elegance to handling the Results arguments, it does so at the cost of heap storage. Not only the cost of the initial allocation, but the cost of potential defragmentation runs. But then again, is the cost of heap storage really that big, especially since the data should have a relatively long life? (On average, the time an end user spends in a room in the game) That I don't know, since it all depends on the memory allocator implementation.

In the end, I believe both methods perform well, and as such I choose the eloquence of using the 'Object' class. I am very much open to your thoughts on both the class as a whole or on your take of the problem. Also, if I misspoke about something please, please, please let me know.

Thanks for reading and have fun coding,
-RichieSams


Edit: Upon further inspection I noticed that by using Common::String I'm not only negating any memory size benefits from using 'Object', but potentially even using more memory, since Common::String has such a huge size.
Marisa Chan:
char arguments[25] = "1 a00h1tc.raw 1";
size = 25;

Object:
Object arg1 = 1;
Object arg2 = "a00h1tc.raw";
Object arg3 = 1;

size = (3 *sizeof(Object)) + sizeof(byte) + sizeof(Common::String) + sizeof(byte);
size = 15 + 1 + 34 + 1;
size = 51;
I could instead store the data in a char array, but it would mean that the Object class would need a stringLength member, which adds another 1 - 4 bytes on every instance of the class. Even with this new insight, I think I will continue to use 'Object', again for the added eloquence it adds. The memory difference is still rather small.

by RichieSams (noreply@blogger.com) at January 23, 2014 02:28 AM

December 01, 2013

Paul Gilbert (Dreammaster)

Thanksgiving with a Ring.

"They said it would it would never be done. They were wrong." After a slight hiatus at the start of the year, work has finally finished on our next TsAGE game, Return to Ringworld. The only remaining work still in progress is a card game available on the in-game consoles, and is completely unrelated to the main story.

This is a direct sequel to the original Ringworld game.. after saving the puppeteer race from extermination and uncovering some powerful ancient technology on the first game, Quinn, Seeker of Vengeance and Miranda Rees find themselves searched for as fugitives by all three major species, so they plan to go to Ringworld to hide. But it turns that their ship has suffered problems. And fixing them and getting to Ringworld will only be the start of their problems..

The ScummVM Team is proud to announce that Return to Ringworld is now playable in ScummVM using the latest daily builds, and ready for testing. As usual, all bugs should be reported to our bug tracker following our bug submission guidelines. While you play through the game, we would also love it if you could take some screenshots for us.

by Dreammaster (noreply@blogger.com) at December 01, 2013 07:40 PM

 

curved edge   curved edge