As I've been a bit busy the past few months, the Wintermute-engine port for ScummVM hasn't seen as much progress as I would have liked, but I still think it's time to get the ball rolling a bit again:
One of the games I tested against this summer was Chivalry is not dead. So far I haven't had any issues with this game, BUT, this game is also very open-ended, and thus a bit hard to test thoroughly.
Thus any help testing this game would be greatly appreciated.
Just grab a copy of the game from the link above, and a nightly build of ScummVM, and test the game. Report any issues to the ScummVM Bug-Tracker, in the group "Wintermute". I would also love to hear in this thread if you played through the game without any issues at all.
Note: Chivalry uses the Arial-font currently, which we haven't yet worked around (eventually a similar FreeFont will be used), so to get decent fonts in the game, you'll have to put arial.ttf in the game's folder (on macs you can find it in /Library/Fonts, on Windows it should be in C:\Windows\Fonts, linux-users can either download a replacement from FreeFonts/OpenFonts, or get some solution involving Microsoft Core Fonts.)
Monday, 3 December 2012
Saturday, 8 September 2012
Aftermath of GSoC
Ok, so GSoC is over, I passed and a few things has happened since then:
Some games had issues with interlaced PNGs and TGAs, this should be less of an issue, as I posted pull-requests for using libpng (instead of the internal variation that was in use before), as well as a TGA-loader, both of which have been accepted and merged now. (This means that Five Magical Amulets and Five Lethal Demons now atleast start).
The Wintermute-engine was opened as a pull-request, which after some adjustment and discussion, ended up getting merged into the master-branch for ScummVM. This means that the Wintermute-engine is available in the latest daily builds, although it is still marked as unstable, and no specific games are officially supported at this point. (BUT, from personal experience atleast Dirty Split and Chivalry is not Dead should be completable). The development of the engine will continue there instead of in my own fork.
Speaking of which, I am now joining the ScummVM-team as a developer, to continue working on the Wintermute-engine (and hopefully getting a few games release-worthy). The road ahead for Wintermute isn't short, as there are plenty of bugs left to squash, and plenty of performance left to improve upon, so I guess I'll have my hands full for a while longer.
So, if you want to try out the results of this summer's work, go ahead and grab a daily build if you are on Windows, or build your own if you are on OS X or Linux.
Oh, and while I won't be accepting bug-reports for specific games for a while yet, feel free to send me detection-entries for the multitude of games I haven't gotten around to adding yet.
Einar Johan
Some games had issues with interlaced PNGs and TGAs, this should be less of an issue, as I posted pull-requests for using libpng (instead of the internal variation that was in use before), as well as a TGA-loader, both of which have been accepted and merged now. (This means that Five Magical Amulets and Five Lethal Demons now atleast start).
The Wintermute-engine was opened as a pull-request, which after some adjustment and discussion, ended up getting merged into the master-branch for ScummVM. This means that the Wintermute-engine is available in the latest daily builds, although it is still marked as unstable, and no specific games are officially supported at this point. (BUT, from personal experience atleast Dirty Split and Chivalry is not Dead should be completable). The development of the engine will continue there instead of in my own fork.
Speaking of which, I am now joining the ScummVM-team as a developer, to continue working on the Wintermute-engine (and hopefully getting a few games release-worthy). The road ahead for Wintermute isn't short, as there are plenty of bugs left to squash, and plenty of performance left to improve upon, so I guess I'll have my hands full for a while longer.
So, if you want to try out the results of this summer's work, go ahead and grab a daily build if you are on Windows, or build your own if you are on OS X or Linux.
Oh, and while I won't be accepting bug-reports for specific games for a while yet, feel free to send me detection-entries for the multitude of games I haven't gotten around to adding yet.
Einar Johan
Tuesday, 7 August 2012
A slow week
This has been a rather slow week, my exams are starting up, and I only got in some time to start looking into the slowness that still persists with this engine.
The major source of slowness at the moment, stems from the fact that the SDL-backend in ScummVM seems to want to down-convert 32bpp to 16bpp, and since the engine is still doing full-screen updates, that means a full-screen down-conversion for every frame, which takes quite a bit of time. The problems from this can be avoided by using the OpenGL-backend, although that still has problems of it's own (like not supporting the PixelFormat I originally started out with (RGBA)), it does a decent job as a way of testing the other parts of the drawing pipeline without drowning them out in conversion.
Getting the down-conversion out of the way, revealed that the single blitting-function I'd been using was indeed rather heavy, and I'd split it out earlier to two separate functions, where one supported colour-masking, and the other didn't, making it easier to see in a profiler how much of the drawing actually used colour-masking (very little). I added in an additional function for handling opaque drawing, which helped quite a bit on the speed (skipping atleast 3 multiplications, 3 shifts and 6 additions per pixel).
For Dirty Split the numbers I get on my computer are roughly (Using the OpenGL-backend):
27 % Opaque-draws
20 % Scale
9 % Alpha-blitting
Where percentages are of the total CPU-load for the process (which now actually can be different from the CPU-load for the entire core it runs on, at max FPS). Now, there is obviously room for improvement here, in particular, the scaling, which as a result of the changes I did to implement dirty rects, ended up being done for every draw (basically, the non-dirty rect version would create renderTickets that were instantly drawn, and then discarded, and since the renderTickets were responsible for keeping the scaled copies, they were rescaled every frame). Thus I ended up partially adding renderTickets to the full-screen update system:
Upon a render call, the render-queue is checked for a matching render-ticket, if one exists, that ticket is used for drawing, otherwise, a new ticket is generated, drawn and added to the render-queue. Any unused ticket in the queue (a misnomer really, as it's really a list, but hey...) is deleted when the screen-buffer is flipped onto the screen.
This gives the benefit of not having to rescale every frame, now the results after doing that weren't exactly comparable, as I didn't exactly set forward a specific enough test (simply watching a bit of the intro movie, and then letting it settle for a while in the first frame), but, atleast the scaling is almost nowhere to be seen:
26.6% Opaque-draws
25.8 % Alpha-blits
2% Scale
This makes for quite an improvement, and should possibly make the games playable on a bit slower computers than what has been the case so far.
The major source of slowness at the moment, stems from the fact that the SDL-backend in ScummVM seems to want to down-convert 32bpp to 16bpp, and since the engine is still doing full-screen updates, that means a full-screen down-conversion for every frame, which takes quite a bit of time. The problems from this can be avoided by using the OpenGL-backend, although that still has problems of it's own (like not supporting the PixelFormat I originally started out with (RGBA)), it does a decent job as a way of testing the other parts of the drawing pipeline without drowning them out in conversion.
Getting the down-conversion out of the way, revealed that the single blitting-function I'd been using was indeed rather heavy, and I'd split it out earlier to two separate functions, where one supported colour-masking, and the other didn't, making it easier to see in a profiler how much of the drawing actually used colour-masking (very little). I added in an additional function for handling opaque drawing, which helped quite a bit on the speed (skipping atleast 3 multiplications, 3 shifts and 6 additions per pixel).
For Dirty Split the numbers I get on my computer are roughly (Using the OpenGL-backend):
27 % Opaque-draws
20 % Scale
9 % Alpha-blitting
Where percentages are of the total CPU-load for the process (which now actually can be different from the CPU-load for the entire core it runs on, at max FPS). Now, there is obviously room for improvement here, in particular, the scaling, which as a result of the changes I did to implement dirty rects, ended up being done for every draw (basically, the non-dirty rect version would create renderTickets that were instantly drawn, and then discarded, and since the renderTickets were responsible for keeping the scaled copies, they were rescaled every frame). Thus I ended up partially adding renderTickets to the full-screen update system:
Upon a render call, the render-queue is checked for a matching render-ticket, if one exists, that ticket is used for drawing, otherwise, a new ticket is generated, drawn and added to the render-queue. Any unused ticket in the queue (a misnomer really, as it's really a list, but hey...) is deleted when the screen-buffer is flipped onto the screen.
This gives the benefit of not having to rescale every frame, now the results after doing that weren't exactly comparable, as I didn't exactly set forward a specific enough test (simply watching a bit of the intro movie, and then letting it settle for a while in the first frame), but, atleast the scaling is almost nowhere to be seen:
26.6% Opaque-draws
25.8 % Alpha-blits
2% Scale
This makes for quite an improvement, and should possibly make the games playable on a bit slower computers than what has been the case so far.
Monday, 30 July 2012
Autumn Cleaning
The past week has seen quite a few deletions from the codebase, I've removed the debugger, the registry and a lot of smaller pieces here and there in the code, all stuff that isn't really necessary in a ScummVM-version of the engine (like in-engine fullscreen-switching-handling).
I've also found time for quite a bit of cleanup, with the assistance of the nifty astyle tool I added braces to all if-, for- and while-statements, breaking single-line ifs that looked like this:
if (conditional) doStuff();
into:
if (conditional) {
doStuff();
}
which made for quite a bit more readable and consistent code.
I've also removed Base as super-class for a few classes that didn't really need to reference BaseGame (formerly CBGame), thus easing the cross-file-dependencies that were making the compile-times horrible.
File Management
The package management in the file manager has been refactored into a subclass of Common::Archive, which at this point means that it's quite close to being able to be added to SearchMan, and then just using SearchMan directly, one of the blockers for that is the filename-handling for absolute paths (i.e. redirecting C:\Windows\fonts somewhere usefull, another is that quite a few classes use the "readWholeFile"-function (those that just got a bad case of "endianness"-itch, can relax, it's only used for text files)).
Save games
Where formerly save games were taking ages and ages to load/save, they now should take quite a bit shorter, the speed-issue here came from the fact that the "load-progress-indicator" was drawn for every instance of every class that was loaded/saved, this in turn made a full screen update (as everything does for the moment, for lack of fully working dirty rect-updates). This meant that for every millisecond of usefull load/save-work, the engine would be doing hundreds of milliseconds of work updating the screen just to possibly move a progress bar another pixel.
I changed this behaviour to only update the exact region of the screen where the indicator is, and only as far as the indicator has progressed (and, only when the progress bar has changed at all). This might result in minor differencies in how the progress bar looks (as it isn't redrawn on top of itself for every single instance any longer). But that's a cost I think most users will be willing to take when the end result is a decrease in save/load-time by a factor of 10.
Lazy loading of images
The engine used to take a few seconds to startup a game on my computer, the reason for that was simply that all the images in the entire game would be loaded. I looked into this, and the only reason most of these images were loaded, was simply that their width and height was needed to set the default size of _rect in BaseSubFrame. I changed _rect to private and made ALL access to it use getters and setters (even internally in the class). Then I added the field _wantsDefaultRect, that kept track of whether _rect should have the width/height-values or something else. When getRect is triggered, and _wantsDefaultRect is true, the image will be loaded, to get at those values.
Now, this might sound like a rather odd approach for a simple getter, but the end result is that the state of the class is preserved WITHOUT loading every single image on startup, thus reducing both the load time and the memory footprint by quite a bit. (For Dirty Split I halved the memory footprint of the first screen, and reduced the load-time to between a fifth and a tenth).
One side-effect of this that might prove this solution quirky, is that there is no guarantee that all frames of an animation reside in memory now, which might result in some animations being slow or late the first time they are played.
Singleton and separation
Instead of passing the File Manager around to everyone and everything, it's now moved to a singleton that is supposed to keep it, and other stuff that survive between loading savegames. Since BaseGame is already quite a bit on the heavy side (clocking in at over 4 KLOCs), I think it's appropriate to try to move as much functionality as possible OUT of that class and put it elsewhere.
Minor stuff
I've also found time for quite a bit of cleanup, with the assistance of the nifty astyle tool I added braces to all if-, for- and while-statements, breaking single-line ifs that looked like this:
if (conditional) doStuff();
into:
if (conditional) {
doStuff();
}
which made for quite a bit more readable and consistent code.
I've also removed Base as super-class for a few classes that didn't really need to reference BaseGame (formerly CBGame), thus easing the cross-file-dependencies that were making the compile-times horrible.
File Management
The package management in the file manager has been refactored into a subclass of Common::Archive, which at this point means that it's quite close to being able to be added to SearchMan, and then just using SearchMan directly, one of the blockers for that is the filename-handling for absolute paths (i.e. redirecting C:\Windows\fonts somewhere usefull, another is that quite a few classes use the "readWholeFile"-function (those that just got a bad case of "endianness"-itch, can relax, it's only used for text files)).
Save games
Where formerly save games were taking ages and ages to load/save, they now should take quite a bit shorter, the speed-issue here came from the fact that the "load-progress-indicator" was drawn for every instance of every class that was loaded/saved, this in turn made a full screen update (as everything does for the moment, for lack of fully working dirty rect-updates). This meant that for every millisecond of usefull load/save-work, the engine would be doing hundreds of milliseconds of work updating the screen just to possibly move a progress bar another pixel.
I changed this behaviour to only update the exact region of the screen where the indicator is, and only as far as the indicator has progressed (and, only when the progress bar has changed at all). This might result in minor differencies in how the progress bar looks (as it isn't redrawn on top of itself for every single instance any longer). But that's a cost I think most users will be willing to take when the end result is a decrease in save/load-time by a factor of 10.
Lazy loading of images
The engine used to take a few seconds to startup a game on my computer, the reason for that was simply that all the images in the entire game would be loaded. I looked into this, and the only reason most of these images were loaded, was simply that their width and height was needed to set the default size of _rect in BaseSubFrame. I changed _rect to private and made ALL access to it use getters and setters (even internally in the class). Then I added the field _wantsDefaultRect, that kept track of whether _rect should have the width/height-values or something else. When getRect is triggered, and _wantsDefaultRect is true, the image will be loaded, to get at those values.
Now, this might sound like a rather odd approach for a simple getter, but the end result is that the state of the class is preserved WITHOUT loading every single image on startup, thus reducing both the load time and the memory footprint by quite a bit. (For Dirty Split I halved the memory footprint of the first screen, and reduced the load-time to between a fifth and a tenth).
One side-effect of this that might prove this solution quirky, is that there is no guarantee that all frames of an animation reside in memory now, which might result in some animations being slow or late the first time they are played.
Singleton and separation
Instead of passing the File Manager around to everyone and everything, it's now moved to a singleton that is supposed to keep it, and other stuff that survive between loading savegames. Since BaseGame is already quite a bit on the heavy side (clocking in at over 4 KLOCs), I think it's appropriate to try to move as much functionality as possible OUT of that class and put it elsewhere.
Minor stuff
- I had forgotten to add the space-bar as a "printable" key in the events-checks, which made typing anything but rather long words in i.e. save-game titles impossible. That is fixed now.
- Sounds that were playing when saving now resume properly on load.
- Screen-fading now works.
- Save game thumbnails now scale properly (thanks to some scaling code I got from clone2727), where before they would repeat the first column in the last few rows, owing to some integer-division gone haywire.
- The settings that were formerly in the settings.xml-file have now been moved to ConfMan, which is responsible for the ScummVM-settings-file (.scummvmrc/ScummVM Preferences/scummvm.ini), any game-specific settings will be stored there with the prefix priv_ (for instance the subtitles-flag in Dirty Split is stored as priv_Subtitles).
Monday, 23 July 2012
Start your engines
Last week I listed a few issues that were on my "TODO"-list, well of course some progress has been made, so without further ado:
Volume-settings:
While formerly volume-settings were completely ignored, now I've removed the internal settings in the engine for sfx/speech/music-volume, and mapped those directly to the matching ScummVM-settings, which means that changing the volume in the GMM, or in-game will show up nicely in both places. Some games are a tad aggressive on updating the sliders, which makes for a bit of jumpiness on their sliders, but it works atleast.
The engine also has a concept of "master-volume", while ScummVM doesn't, so I ended up deciding that the most consequent way to solve that, was to simply apply that as a multiplication on the other values, thus setting Master to 50% when Speech is at 50% would yield 25%, and still get that updated when the values are changed through the GMM.
Sound-format-support:
I added in RAW-WAVE support (no MSADPCM-support yet), which means that the white chamber now also plays all it's sound-effects. The engine will error out if it meets any other formats, a solution I chose to be able to pick up on missing format-support if some game needs any more than this.
TTF-Fonts:
As I said last week, the fonts now draw without being too dark, it remains to be checked if the line-lengths still match well (seems that Rosemary has a few problems with lines overflowing without increasing the background for the text.
Detection:
Ok, so this is where I've put a lot of my work the past week. While most of the games I have are added with proper MD5-detection entries, one of the bigger selling points of this engine is the ability to make your own games, and, well having to recompile ScummVM with new MD5s for every iteration of your development would be kind of a hassle, which is why I added a fallback-detector. Currently this works like follows for any given folder.:
- If the folder doesn't contain a "data.dcp"-file ignore the folder
- Otherwise, start up the basic initialization of the engine.
- Add in the DCPs in that folder to the engine (although, "data.dcp" should be enough)
- Parse "startup.settings" to get the GAME-variable (usually "default.game")
- Parse the file defined from "startup.settings"'GAME-variable, looking for NAME and CAPTION.
- Then work with those vars to create a gameid and a game-description (using the extra-field of the detection entry to fill in the Caption if it differs from the game-name)
This gives a bit of overhead for each game, as quite a bit of the engine needs to be loaded to use the file-manager to read from a DCP, and the same holds true for the parser used, in all fairness this only happens whenever a "data.dcp" is found in a folder AND that data.dcp doesn't match a known MD5, but still, long-term I guess a more clearcut solution would be in order. The only file that really gets parsed by the engine-parser right now is startup.settings though, while default.game gets put through Common::StringTokenizer simply because I only need 2 fields of that file.
One problem with this solution, is that I don't use the StringTables in the engines, thus any localized captions will not be localized but instead contain the placeholder-string. In any case this isn't too big of an issue, as using non MD5-listed games should become the rare case for developers, and the game name can always be added explicitly to the MD5-based detection.
File-access:
Changing the engine to be able to do detection without loading up the ENTIRE kitchensink of classes, while working with an explicit folder, lead me to investigate the file manager-system further. I had to change the use of SearchMan into a system of explicit FSNodes, which means that now paths for archive-less files are parsed with FSNodes, and all DCP-Packages now carry a FSNode to their location. (Instead of opening them with SearchMan when needed).
This might sound like an odd solution, but it has a decent use case, a few games have a "language" subfolder, that contains localizations for multiple languages, with a SearchMan-based solution I ended up registering ALL packages there, thus often ending up with czech ingame-text (alphabetic ordering of files...) To avoid this I needed a special-case for the "languages"-subfolder, filtering the dcp's there, to only load the really necessary language. Right now this is hardcoded to be "english.dcp" only, but it should be quite possible to use the language specified in the game descriptor to select properly.
Variable-renaming:
While I still find the odd variable with the wrong convention, I have handled most of them now, one of the bigger changes was replacing the semi-global variable "Game", which basically is a reference to the main "CBGame"-object, that every object carries around with it, that was left behind after the big rename a few weeks ago, mainly to make sure no big issues arose. But it has now been renamed to _gameRef.
I also changed all the class-name-conventions, removing the C-prefix and expanding on the classnames, thus any CBClass became BaseClass, and CAdClass became AdClass, and CSysClass became SystemClass, and so on. This was also followed up with renaming the entire file-hierarchy, to follow one filename-convention (i ended up with all_lower_case.{h,cpp}, as I'd had far too many case-typos when jumping between OS X and Linux in this project, this should atleast make it easy to remember the case-choice for any wme-filename).
Additional game detections added:
I added in quite a few more MD5-entries, to have more games to test with, here are the games I remember of the top of my head:
Note: Not all these games actually work, for reasons stated below in the "Remaining-issues" section.
Remaining-issues:
- Videos still desynch, something I won't be looking much further into in this GSoC, as the videos are a bit outside the main scope of this project, and well, the same issues still happen in Sword25, from where I got the video_decoder I'm using anyhow.
- Dirty-rect rendering is still sketchy at best. (and thus disabled by default.
- Interlaced PNGs are not yet supported, but that is also a bit outside of my control, I did open up a semi-complete pull-request that added more LodePNG-code to the PNG-decoder, but it still has a few files it chokes on.
- 32 bpp BMPs are not supported in ScummVM, shouldn't be impossible to implement, as I already did some tweaks to work past the crash that ensued (simply skipping the fourth byte of every pixel in the BMP atleast yields some results).
- Non v.1.1 JPEGs are not supported in ScummVM, this I haven't worked or looked any on, but it blocks a few games from even starting.
Monday, 16 July 2012
What's left?
I'm back again, after a week of harvest-work, I'm now ready to start the second half of GSoC, a big question then, is what's left to do?
- Volume-settings are currently ignored (well, mute works, but that's it)
- Videos desynch a bit (I used the video-code from the Sword 2.5-engine, which states this to be a known issue)
- Only OGG-audio is supported at the moment, while games like "the white chamber" also use WAV.
- Detection still needs a bit of work:
- Detection is hardcoded for the games I have tested with, and doesn't allow for user-games with changing/unknown MD5's.
- Detection is set with a common target, and Savegames use hardcoded filenames, thus making ALL WinterMute-games share savegame-namespace. This means that slot 3 in J.U.L.I.A. also is slot 3 in Dirty Split...
- TTF-fonts still need a bit of adjusting:
- TTF-fonts currently lack a decent fallback to theme-fonts (in ANY case text will be drawn, just not with a font that looks at all similar to what was intended when the games were made)
- TTF-fonts drew a bit on the dark side (cheap Star Wars-jokes aside, this was because I drew 16bpp for some reason, and then converted that to 32bpp, I have a fix for it now)
- Variable renaming:
- There are still the odd variable name here and there that follows either the VarName-naming convention, or the style_where_you_put_in_a_bunch_of_these. I think I got most of the former. And the latter, well it will have to change as well.
- Drawing is slow, or as _sev put it:
<_sev> i don't agree it is slow
<_sev> it is über-slowWell, this is the bit that I talked a bit about last time, sadly I have still not solved the dirty rect-thing (since I haven't really been working on GSoC the past week, owing to the mentioned harvest-work). I did go through the blit-function I use right now, and noticed two things:
As fuzzie mentioned back when I originally tried to refactor the blit-code from the Sword25-engine for common usage, I changed the constant shifts to variable shifts (i.e. "pix << 24 & 0xff" became "pix << blueShift & 0xff"), which would disable any compiler-optimizations that could change that to byte-access, instead of a shift, accordingly I also changed the byte-writes to format.colorFromARGB-calls, which add even more shifts. Doing some profiling on thise code revealed that ~30% of the total runtime was spent in this function. Simply changing the shifts back to constant shifts, and the format.colorFromARGB-calls to byte-writes, reduced this to ~19%. Which now makes flip() the heaviest part of the entire code (since it does a complete redraw of the frame every frame), counting in at ~57% of the total runtime spent in g_system->updateScreen(). Finishing up dirty rects should reduce that load by quite a bit more.
I did do some minor game testing the past week, as meta kindly provided me a retail version of J.U.L.I.A. I was able to play on from the demo, the downside being that I found J.U.L.I.A. to be using sprite rotation, which is not supported in WME Lite, from which I have based this port. This makes it rather unlikely that the full J.U.L.I.A.-version will become completable with this port during GSoC, although, it isn't unlikely that I'll end up adding in sprite rotation at some future point.
Going forwards, I'll try to get the things listed above fixed, and hopefully end up with a reducing my list of TODOs, instead of finding new roadblocks.
Wednesday, 4 July 2012
Time to clean those rects up.
My current solution to the rendering in WME is rather slow, capping out at around 30 fps on my i7, which means I get lower frame rates in Dirty Split than I do in Diablo III. The major difference between the two would of course be partially explained by Dirty Split being rendered entirely in software. But that alone shouldn't excuse a non-changing 2D scene not managing to get more than 30 fps.
Now there are a few places that the rendering can be improved: First of all, is there any reason to redraw the screen if NOTHING has changed? Probably not. Is there any reason to produce as many frames per second as possible, maxing the CPU-load? Probably not.
So, I put in a framerate cap, for the time being this is capped at 25 fps, to keep my CPU from maxing (and thus my laptop from getting quite hot). In practice it should probably go a bit higher than that, but since the rendering itself limits it's possibility at the moment, that discussion is rather moot.
To get the rendering to go a bit smoother, I had to detect if the new frame was at all different from the last frame, and preferably also WHAT was different from the previous frame, and then only redraw the parts that were changed. (The "dirty rectangles")
Replacing this means I have to take care to keep the same behaviour intact, which means:
Now there are a few places that the rendering can be improved: First of all, is there any reason to redraw the screen if NOTHING has changed? Probably not. Is there any reason to produce as many frames per second as possible, maxing the CPU-load? Probably not.
So, I put in a framerate cap, for the time being this is capped at 25 fps, to keep my CPU from maxing (and thus my laptop from getting quite hot). In practice it should probably go a bit higher than that, but since the rendering itself limits it's possibility at the moment, that discussion is rather moot.
To get the rendering to go a bit smoother, I had to detect if the new frame was at all different from the last frame, and preferably also WHAT was different from the previous frame, and then only redraw the parts that were changed. (The "dirty rectangles")
The old renderer
Originally, all surfaces that wanted to be rendered would apply a scale to themselves and then pass the scaled surface to the renderer (the surfaces would also cache the last scale, to avoid reapplying the scale every frame if it was only drawn at one size). Every frame would start off with clearing the screen-buffer, then drawing the surfaces one by one. Thus there was almost no difference between drawing a very different frame, or the same frame again, as all the surfaces would need to be redrawn anyhow.Replacing this means I have to take care to keep the same behaviour intact, which means:
- Any area that was drawn last frame, but isn't drawn now needs a redraw
- Any area that doesn't get anything drawn in it, should get filled with the clear-colour (which was originally drawn into the screen-buffer on clear anyhow)
- If any element was drawn before element X, it still needs to be redrawn before element X if it needs to be redrawn.
- If any element was drawn after element X, it still needs to be redrawn before element X f it needs to be redrawn.
Render-tickets
The name I chose for my solution is "renderTickets". Where before any draw-call would mean an immediate update to the screen-buffer, in the new system, a renderTicket makes note of the operation that was asked for (a ticket), as well as a copy of the data that was to be used as a source for the operation.
When adding a ticket, a search is done to see if the surface that asked to draw issued any tickets last frame, and if they are the same as the one's this frame. Any tickets that are unchanged will be reused, while any new tickets will trigger an update in their target screen region.
Additionally the renderer keeps track of the order of the tickets, a ticket is only accepted for reuse if it is asked for at the same point it was asked for last frame, with one exception: A new ticket that arrives as ticket N, will increment the expected order of the tickets that should arrive as N+1 and on, since a new ticket will get it's region redrawn completely anyhow, this should still keep the Z-order, but avoid having to redraw uneccesarrily areas that arrive in-order, but with new tickets before them. Any ticket
that triggers a redraw, has it's target-position added as a dirty rect (at this point, that means that the single rect used is scaled to include it's area)
Finally, when the engine asks for a flip from the back-buffer to the screen-buffer, the actual drawing starts:
- First, the list of render tickets is purged of all items that were drawn last frame, but did not receive requests for draw this frame.
- The dirty rect is filled with the clear-colour
- Any tickets that intersect the dirty rect get's that section redrawn
- Finally the back-buffer is copied to the screen buffer and onto the screen.
Issues
The current implementation has a few issues with Z-order, and uses a bit more memory than the old solution. The memory usage was expected, as every ticket has to keep a copy of the section it wants to draw (as the scale can differ between draws, or the Surface that asked for a draw can be destroyed before screen-flip). Thus the implementation isn't enabled by default at this point.
There also is no code done yet for Fading/Line-drawing with dirty-rects, and I also might want to use multiple dirty rects, instead of scaling a single update-area. Happily though, the current solution allows the engine to idle without problem, which means that a screen that doesn't change doesn't even trigger a buffer-copy, and puts the CPU-usage down from 100% to less than 10% when nothing is happening.
Other developments
I did a quick test of the engine on my PPC-machine, and fixed an endian-assumption in the scripts, which means that I possibly might be the first person to ever start up J.U.L.I.A. and Dirty Split on a PPC Mac :D That did expose the need for a way more efficient render-solution though, because while the game did RUN fine, it wasn't fast enough to be very playable.
In some free moments the past week I have also done quite a bit of variable and function-renaming to move towards following the ScummVM convention (ScummVM uses "funcName()" and "varName", while WME uses "FuncName()" and "VarName"), thanks to _sev's earlier help, the "m_MemberVar" -> "_memberVar" rename is already done, although a few of those might need a bit of lookthrough to catch cases like _iD (which probably should either be _id or _ID).
Current engine status
While there are no fancy pictures this time around, I thought I'd list my estimates on the engine-status:
- Graphics: 80-90% Works completely, but is very slow
- Sound: 50-60% Works, but lacks volume-control and WAV-file support
- Fonts: 70-80% Works fine, but is a bit darker than in the original, also no solution is in place for replacing system-fonts (that won't necessarily be available on non-Windows-platforms)
- PPC-support: Can't guess at a number, but the engine starts, and seems to run OK
- Video: Works, but has the same issues as Broken Sword 2.5 lists, video desyncs from audio, and is very slow
- Savegames: 80-90% Currently broken by a mis-setting of version-numbers, but that should be a quick fix, otherwise works fine, and has no noticed memory-leaks/issues.
- Renaming to ScummVM-convention: 40-50% would be my estimate, but I would guess at closer to 40 than 50.
- Sprite mirroring: 100%
Tuesday, 26 June 2012
A real saviour
one thing that is rather common among all adventure games, is not only the ability, but also (without naming names) the NEED to save early and save often, certain games could even make good use of a version control system for that particular thing...
Oh well, puns and jokes aside, last week I added savegame-support in the WME-port, initially this worked fine for everything BUT J.U.L.I.A. and after looking for the problem for a good day, I noticed that the savegame-code for the theora-wrapper had a special case for savegame-versions below a certain threshold, thus reading fewer bytes than was written. (And well, I had reset the savegame-version system to 1.0.0 at the time, since I wasn't aiming for savegame-compatibility anyhow). Fixing that allowed for working savegames in all the games I have tested so far (which would be Dirty Split and J.U.L.I.A.).
I also made some headway with the renaming of functions the past few days, now a good chunk of the functions follow the "funcName()"-convention, instead of the "FuncName()"-convention. A few still remain, and the arguments and variables still stay mostly unchanged.
I also finally got around to compiling the port on a PPC Mac, only to realize how much worked there: the mouse cursor. Yup, some Big-Endian-love is needed to get this train rolling on such CPUs. The main problem at the moment lies in how the scripts are read: *(uint32*)char_buffer... I'm thinking I'll replace most of that code with MemoryStream-code, to make the endian-handling a tad easier (and possibly avoid having to read the entire file in one big chunk at game-start.
The persistence system used in WME originally made a note of the saveable classes by having a static instance of CSysClass per class, this was a bit too global for my tastes, and I have changed that to a function that manually registers all the classes, this way it can be cleaned up properly on exit.
Along with these changes, I also added in load-from-launcher support, and RTL-support, right now the thumbnails are a bit off for load-from-launcher, but the same problem exists for ingame thumbnails too, I guess I'm using a bit too much of the render-surface for the thumbnail, either that, or the downscaling works a bit quirkily.
I'm hoping to have atleast the scripts rolling on PPC this week.
Sorry for the lack of exciting pictures this time, but atleast this means both your game progress and bandwidth can be saved now.
Thursday, 14 June 2012
10 days later
I left the last blogpost with an image of a video from J.U.L.I.A. being played inside ScummVM, but that image still didn't have the clearing of the screen included, so to start of this post, here is how it currently looks:
The J.U.L.I.A.-demo intro |
That improvement did however exist at the time I posted that last blogpost, so let's get on with what has improved since then:
TTF-Rendering looks better
Last time I posted a screenshot of the TTF-rendering that looked like this:
How fonts looked last week |
Notice that the text is impossible to read, as well as being quite small. There was also an issue with the size of each line making the "descending" parts of characters cut off on the lowest lines. Thanks to a bit of help from LordHoto, I managed to find out that I had been stupid, and used the wrong variables for creating the drawing-surface for the fonts, I had used the font height that the scripts ask for, while in reality I should have used the font height that the TTF-loader in ScummVM returns (which is the height from the lowest point to the highest point, instead of the height from the baseline to the highest point).
While looking through the code to find out why this happened, I took this screenshot, which shows red lines at the points I was asking the TTF-font to render each line:
Why using the wrong height looks wrong. |
The fonts were also drawn about too small, as WME used a dpi that was 4/3 of what ScummVM uses in it's TTF-code (96 vs 72), simply multiplying the font-size specified by the scripts made the font a lot better, although, there were still some issues relating to the "layering" that WME does to get the shadowed-fonts it has. As it turns out, this was done by drawing the same line multiple times with different colour-values, at differing offsets. The current draw-code did something rather silly when it got offsets (well, it simply changed the left and top parts of the rect that specifies the draw-area, without making sure the width stayed the same, making it an odd skew-and-scale-operation). Fixing that as well provided the following result:
Font-drawing after fixing offsets and sizing. |
Making sure we even bother to use the text alignment the scripts ask for, as well as mostly the right colours, provides quite an improvement for Dirty Split too, compare last posts font-rendering:
Last week's rendering |
To this weeks version of the exact same scene:
This weeks rendering |
It's not perfect, as the text should be a bit lighter, but it's getting there atleast.
String-concatenation works again:
... and I didn't even know I broke it, well, turns out I must have replaced some variable-names in the II_ADD-script-function, and forgot to make sure everything was changed correctly, which made concatenation of strings always return NULL instead of what it was supposed to return, thus making things like SetImage completely broken, putting that right again, provided one of the biggest leaps of functionality I've seen in a while:What difference one single variable-name can have. |
Entity-videos now work too
I forgot that I had to add a few bits and pieces to AdEntity too, to get the videos working for non-fullscreen scenes like this one:Waking up all alone in the middle of nowhere, literally. |
Alpha-masked video |
As a final comparison: Look at this picture from last week:
Last week's "Enter manual override" |
This week's version of the same scene |
In the upper picture you can see the rectangular box that the videos are being played in, that is hidden by the alpha masking now. Also note the difference in the menu-buttons, and the alignment and presentation of the text. You'd almost think that the demo was completable now...
And it is:
After fixing all of the above, and getting rid of a few hundred megabytes of memory leaks, the game still hung when going down to the planet, so I started looking, and it turns out that the string-splitter function had a bug that caused it to never move on from the first token, but simply continued to push that token to it's result-list, thus eating up a few gigabytes in a matter of seconds. Replacing the STL-string-like solution with ScummVM's Common::StringTokenizer, fixed the issue, and allowed me to complete the planet-side mission, thus letting me complete the last two puzzles.
The last puzzle in the J.U.L.I.A.-demo running in ScummVM |
In the tradition of showing something that DOESN'T work as a closing-point, here's a picture of how J.U.L.I.A. looks when closing the game during the tutorial...
Monday, 4 June 2012
A show and tell in 10 steps
If the last post was worth a few thousand words, on account of it's images, then this one might be worth quite a lot more than that, as I've had quite some progress in the visual parts of the Wintermute-engine, without further ado, ten improvements from the last post:
_sev commented on my last blogpost:
Notice that the trees look a lot better, along with the bars on the wall. The hair is also less jaggy, although that is not that visible in this small screenshot. A little downside about the current solution is that it is quite CPU-intensive. But that's a problem for another day, for now it atleast works properly.
Now, these bitmap-fonts are BMPs, which have rather quirky alpha-channels, the original WMELite-code had this comment hanging around:
As for the weird spacing, the engine uses BSurface::IsTransparentAtLite to detect the width of the characters. This is also the reason why the engine explicitly requires the characters to be left-aligned.
Now IsTransparentAtLite was one of the functions that were stubbed when I removed the SDL2-requirement, so adding in new code to check for transparency solved that problem:
Now, simply hooking the right data back up to the blit-code from Sword 2.5, made this look way better:
I also had a few issues with another silly bug: After adding in handling of key-presses, every key seemed to trigger the exit-game dialogue. After hunting that one for quite some time, I realized that I'd made a silly mistake in the event-handling in PlatformSDL.cpp, right after the case for case Common::EVENT_KEYDOWN: was case Common::EVENT_QUIT: and... there was no break; between them...
1: Alpha-blending now works properly
As you might remember from my last post, alpha blending was sort of non-existent in the code at that point. I did do specific skipping of completely transparent pixels, but that didn't prove to pretty:
Previous version, without alpha-blending for anything but full transparency |
"I recommend to take a look at the code of RenderedImage::blit() in engines/sword25/gfx/image/renderedimage.cpp"Which I did, and although I haven't completed the refactoring into common-code that he asked for yet, I did add the code to my own branch, which gives the following result:
New version, with alpha transparency |
2: Bitmap-fonts are drawn correctly now
Previously, the bitmap-fonts were drawn with some rather weird offsets, making the characters have odd spacing. The transparency in those images were also quite off:Previous version, with broken bitmap-fonts |
Which explained that part of the problem, I clearly had to replace the existing alpha-channel with the colour key specified by the game.// convert 32-bit BMPs to 24-bit or they appear totally transparent (does any app actually write alpha in BMP properly?)
As for the weird spacing, the engine uses BSurface::IsTransparentAtLite to detect the width of the characters. This is also the reason why the engine explicitly requires the characters to be left-aligned.
Now IsTransparentAtLite was one of the functions that were stubbed when I removed the SDL2-requirement, so adding in new code to check for transparency solved that problem:
New version, with working bitmap-fonts. |
3: Sprite scaling works
If you look at that last picture, do you notice anything else that's different from the previous ones? Yeah, Baxter is a bit smaller, and for a good reason, the code I borrowed from the Sword 2.5-engine included scaling before drawing, which solves quite a few issues. Previously if you tried to walk up to the door, Baxter would walk in-place, and not seem to go anywhere, simply because he couldn't "walk into the screen" in any visible fashion (he would have had to get smaller to show it). But now, he's scaled accordingly. That screenshot is actually quite close to this screenshot from the original WME Lite-engine, that you might remember from my last post:
The original WME Lite-engine |
Yes, there are a few minor differencies, for one, there's some definite breakage in the gradient that makes up the road he's standing on, but the important bit here is, that this is the very first scene in the game, without any movement done, if you compare this to the first two images in this post, notice how he was way to big to begin with? Well, that's solved now.
4: Sprite mirroring works
One of the features that were missing from WME Lite, when compared to the full Wintermute-engine, was sprite mirroring/rotation, since the Sword 2.5 code I borrowed also contained support for vertical/horizontal flips, it was rather straight-forward to add that little feature back in. Now for testing this, I had to find a game that used it, which in this case was the game Rosemary. Walking left in this game was done by simply mirroring the walk-right sprites, which made it look rather odd without sprite mirroring enabled:
Walking left in Rosemary, without sprite-mirroring |
Walking left in Rosemary, WITH sprite-mirroring |
5: Fixed some bugs that crept in while refactoring code
Yes, bad things might happen when handling large code-bases, back before the last blog post, I changed a lot of char* into const char* to silence some warnings, and ease the hunting of a few segfaults, this had the interesting side effect of breaking a few virtual-functions, making inheriting classes no longer override the proper functions (C++ can be tricky there, as const char* and char* are quite different types that can be cast only one way). This led to scripts like playRandomMouseOver in Rosemary not running, as the super-class version got run instead of the correct one (the reasoning behind the bug was simple; the function-name was stored in a char* and the super class declared a function that accepted char* as parameter, while the subclasses accepted const char*, thus the wrong one was chosen).
6: Fixed speaking to characters (and in doing so, most of the sound)
Previously, if you tried to talk to any character in Dirty Split, the conversation would be cut short, this was because the engine needs to know the length of the text, to know how long it should wait before issuing new lines of text, and unlocking the objects that are speaking. When I did the sound-system before the last blog-post, I only added in the necessary bits for getting something to play, but I didn't bother doing all the various other functions. Specifically GetLength() proved to be quite important for this little tidbit, as it was previously just stubbed to 0. Explaining the rather short conversations.
What remains to be done in the sound-system, is (among other smaller things) supporting loops and seeking in the audio.
7: Added detection for a few more games
I changed the detection-scheme to use the Advanced Detector so that I could easily flag the various games for how compatible they were, as well as detect them based on more detail than the original detection solution. Now, adding every single game to this list will be a never-ending job, as new games are made all the time, but at least the known good and known bad games should be added over time. Particularly the known bad ones should be important to add here (such as all the 3D-games), so that the users will know why their particular game won't work. Another thing planned for this solution, is to have a simple fallback that registers unknown games by simply finding a data.dcp file that doesn't match any known ones. This should allow playing any new or in-development titles without having to first put the hashes into ScummVM.
The currently added games are:
I'll happily add more, if anyone has any good suggestions, but these at least let me test a few of the functions in the engines rather thorougly.
8: TrueType-font support
I have started working on allowing the games to load the TTF-fonts they ask for, which is rather necessary to for instance get subtitles in the conversations in Dirty Split. WME does support TTF-fonts, and defaults to using Arial, this does pose a problem for a ScummVM-port though, as while Arial can be loaded easily on Mac OS X or Windows, from the system-font-directories, various other platforms either lack Arial, or even a system font-concept at all. I'm looking at finding a way to use GNU FreeFont as a fallback alternative for these platforms.
There is also the ever-existing question of what do to if FreeType2 isn't available when building, I guess there will have to be some sort of fallback to one of the GUI-fonts in ScummVM, as a fallback in that case.
Anyhow, TTF-support is still a bit lacking in the WME-port at the moment, as I haven't entirely worked out the details for getting the proper sizes for the fonts, but that's work that will be solved in the coming weeks.
For the curious, this is how TTF-font-rendering looks in WME Lite:
TTF-font in WME Lite |
And this is how it currently looks in ScummVM:
TTF-font in ScummVM |
For comparison's sake, both of these use Arial at the moment, but long term, if users want to have Arial or something similar used with their games, they'll have to put a copy of the font file in their game folder.
One of the things that are handled fine at this point though, is text-wrapping:
Text-wrapping in J.U.L.I.A. |
Yes, it's ugly at the moment, and the picture above even uses a few cheats to get the lower parts of the characters to get drawn (think the "cellar-part" of a g for instance), but it is what it is, a work in progress.
9: Screen refreshing now works
The original engine cleared the screen to black before every frame, that was another part of the SDL-code that I removed while getting it compiling in ScummVM. At certain points this would mean that parts of the screen that were supposed to be black, just kept what was there in the last frame that updated it. Which isn't really pretty:
J.U.L.I.A.-demo without clearing |
Especially the particle engine in the above shot gives use problems. Anyway, making the entire screen draw a color between frames made things quite a lot prettier:
J.U.L.I.A.-demo with clearing |
The downside of this though, is that the blitting functions used here aren't exactly free CPU-wise, so having to redraw everything every frame doesn't exactly reduce the CPU-usage. But atleast J.U.L.I.A. looks right now.
10: Theora-video-support
Now here comes the reason why I was talking about images in the opening of this post, because, if one image is worth a thousand words, then full motion video must be worth rather vastly larger sums than that again.
I mentioned J.U.L.I.A. in #9, and that's a game that really needs video support to shine. I got bored of hunting a bug at some point during the past few days, and decided to do something else for a while, which meant adding back Theora-video-support to the engine.
This was yet another field where the Sword 2.5-engine came to the rescue, as it already had a ScummVM-friendly implementation of libtheora, ready for use as a VideoDecoder, which meant that all I had to do was download the original WME sources (after contacting Mnemonic to make sure I could use them under GPLv2, instead of GPLv3), and dig out the removed pieces of code from there. In this case, that meant the code for functions like PlayTheora in AdEntity/AdGame, as well as the Vid-files from Base/ to see what the interface had been originally.
Connecting up the Sword 2.5 wrapper for libtheora to the WME-theora handler wasn't much work, and soon I had video's playing in all my games. A current limitation though, is that seeking is not yet implemented for Theora.
I'll close of this rather lengthy blogpost, with an image of a video playing in J.U.L.I.A.:
The intro video from the J.U.L.I.A.-demo (note that this image was taken before fixing the screen-clearing issue) |
Tuesday, 22 May 2012
A picture says more than a thousand words
...which should put this blog entry far beyond 10 000 words. There has been a bit of progress with the Wintermute-engine, preliminary support for graphics, sound and I/O is in place, but it is far from perfect, or finished in any way, shape or form.
Right now, the sound system only allows playback of OGG-files, and it only does Play() for them, which was a rather simply task to add in, simply to verify that the sound system still works after having been stripped down from WinterMute Lite, either way no further work is planned on that part for atleast a few weeks.
I also added in some quick hacks to make the engine draw inside ScummVM, right now this is done in 24 bit hardcoded blitting, and was mainly done to see what had to be done. At this point it might be worth noting the work that was done on WME Lite prior to attempting to merge it into the ScummVM-tree as a branch. I started off by forking the original WME-Lite repos onto Github, then I went on to strip out all the dependencies (BASS, Boost, SDL2), as well as removing as much as possible of the "forbidden"-bits (ScummVM doesn't allow direct usage of the C-APIs for file-accesses and random number generation among other things). As I went on, i ported over as much as possible of the ScummVM common code to WME Lite, until I reached the point where I might as well move the WME source over into my ScummVM-fork.
One of the things I did quite early, was to try to use the ScummVM-image loaders, as opposed to the SDL2-code that was used already in WME Lite, I made that kind of work, but transparency proved problematic for now. I picked the game Dirty Split as a test case for the moment, here is how it looks:
This is how the inventory in Dirty Split looks in the original prebuilt WME Lite |
... and this is how it looks in my modified WME Lite-version, that uses the ScummVM image loader. |
... and this is how it looks in ScummVM at the moment. |
Clearly, the model's need to scale appropriately, but, atleast input works well enough to get the character to move over there. The action-menu for Dirty Split reveals a few more of the transparency-quirks:
In ScummVM |
In WME Lite (with my modifications) |
I'll be looking into solving these issues as I implement a more proper drawing-solution in the weeks to come.
Oh well, that's about it for now, I'm off to do my exams, so what better way to end of, than to show how the three variations of the game look when you try to close it? Yes, there are a few minor differencies her, as I didn't go through the menu in ScummVM, but simply Ctrl-C-ed the terminal session.
ScummVM |
Modified WME Lite |
Original WME Lite |
Sunday, 6 May 2012
A beginning
Since I started thinking about approaching this task, I've been doing a little work on the WME Lite code base. I originally forked that repos, and started removing the bits I knew that wouldn't work with ScummVM, which mainly meant stubbing all the dependencies away (Boost, BASS, SDL2), and getting to grips with where STL was used (as ScummVM doesn't allow me to use those).
I then did a bit of "reverse-porting", copying over pieces of ScummVM to that repos, so that I could start replacing pieces of WME-code that depended on things that aren't available in ScummVM, with the proper ScummVM-solutions. This worked up until a certain point, namely getting files to actually load (that would require copying over LARGE amounts of ScummVM-code). At this point I'd gotten to know the WME-codebase a bit better, and also untangled a bit of the header-dependencies that was there (which was usefull, to avoid having to copy the ENTIRE codebase in one go), to the point that I could start copying the files over to a branch in my ScummVM-fork.
This led to a day or so of resolving problems relating to common/forbidden.h (I hadn't purged all the STL/FILE-related pieces of code, since the main reason for moving on from a fork of WME, to a fork of ScummVM, was to get files loading). Additionally I also had to follow the "HOWTO: Engines" guide in the ScummVM-Wiki, to get the connections correct with the rest of ScummVM
But, in the end, I managed to atleast get a repos that compiles, and links in-tree. Right now, it even detects the game data, and should be just a few hours away from being able to get the scripts out of the data files, to start running the main loop of the engine.
This puts me quite a bit into stuff that was planned a bit further down the road in my GSoC-timeline, but sometimes things tend to happen in rather different orders than what one actually plans, there is still work to be done for what was planned as the first week's work (Refactoring), but happily, I've gotten some help from Sev with automated renaming of variables, which should save me a day or two of manual labour. (Combined with some application of astyle, this atleast gets me of the start line for making the code base follow the ScummVM code-formatting guidelines).
In closing:
I might not be getting too much work into the next few weeks, as I have exams coming up, but I do plan to get the game data files at least loading, and hopefully to start looking through the files for any code formatting-breakages in the upcoming time.
Einar Johan
I then did a bit of "reverse-porting", copying over pieces of ScummVM to that repos, so that I could start replacing pieces of WME-code that depended on things that aren't available in ScummVM, with the proper ScummVM-solutions. This worked up until a certain point, namely getting files to actually load (that would require copying over LARGE amounts of ScummVM-code). At this point I'd gotten to know the WME-codebase a bit better, and also untangled a bit of the header-dependencies that was there (which was usefull, to avoid having to copy the ENTIRE codebase in one go), to the point that I could start copying the files over to a branch in my ScummVM-fork.
This led to a day or so of resolving problems relating to common/forbidden.h (I hadn't purged all the STL/FILE-related pieces of code, since the main reason for moving on from a fork of WME, to a fork of ScummVM, was to get files loading). Additionally I also had to follow the "HOWTO: Engines" guide in the ScummVM-Wiki, to get the connections correct with the rest of ScummVM
But, in the end, I managed to atleast get a repos that compiles, and links in-tree. Right now, it even detects the game data, and should be just a few hours away from being able to get the scripts out of the data files, to start running the main loop of the engine.
This puts me quite a bit into stuff that was planned a bit further down the road in my GSoC-timeline, but sometimes things tend to happen in rather different orders than what one actually plans, there is still work to be done for what was planned as the first week's work (Refactoring), but happily, I've gotten some help from Sev with automated renaming of variables, which should save me a day or two of manual labour. (Combined with some application of astyle, this atleast gets me of the start line for making the code base follow the ScummVM code-formatting guidelines).
In closing:
I might not be getting too much work into the next few weeks, as I have exams coming up, but I do plan to get the game data files at least loading, and hopefully to start looking through the files for any code formatting-breakages in the upcoming time.
Einar Johan
Subscribe to:
Posts (Atom)