Sign in to follow this  
  • entries
    455
  • comments
    639
  • views
    422430

Bonsai Progress

Sign in to follow this  
_the_phantom_

83 views

So, after yesterdays successful graphics based testing and release of a Bonsai based game (and my thanks to rip-off for the feedback, glad to see it working on someone elses machine [grin]) I decided to get my hackery hat on to fix something that was bothering me.

With the aforementioned release the dlls were in the same directory as the scripts and the exe. Along with this the bootstrap system loads 'engine.dll' directly via the require statement. However you may have noticed that the system is called Bonsai, so really a require "bonsai" would be nicer.

With this in mind we set off down a little adventue in to Lua's package system and Windows DLL loading.

Lua's Package Management System

When we issue a 'require' function call Lua goes off and does a basic search of a set of folders for the file you asked for, in general it will work something like this;

require "cheese"
Will search in (something like) this order;
First it looks in a 'preload' table to see if we have a function to load this module
.\cheese.lua
-full path-\lua\cheese.lua
-full path-\lua\cheese\init.lua
-full path-\cheese.lua
-full path-\cheese\init.lua
If it fails to find a .lua file it goes off on it's DLL search, which is much the same;
.\cheese.dll
-full path\cheese.dll
-full path\loadall.dll

Of course, failing to find any of those creates a lovely little error [grin]

If it finds the .lua version then file is simply loaded and executed to load the module into memory; a fact which is exploited in the game.

In the case of the dll versions Lua loads the dll via the normal platform means and then tries to find a function named luaopen_[module name]. So in our example above it tries to find luaopen_cheese; if found the function is called and the module should use this to set itself up and register what it needs to with Lua.

So, with that in mind I shuffled the dlls into a Bonsai directory, created an 'init.lua' which read as follows; require "engine"

The test lua file was then adjusted to require "bonsai" at the top and run was hit.

A few moments later I was rewarded with a stack trace saying it couldn't find the "engine" package.

Hmmmm.

A quick look at the search path shows the problem; Lua loads based on the exe and not on the location of the .lua file.

Bah.

So, back to the Lua book I go, as I seem to recall a section on 'sub packages'. Now, a subpackage allows you to arrange packages within each other, so we could have a file b.lua which is in the package "a" and we would load it thusly require "a.b".

How does this work?
Well, as part of it's search Lua turns '.' into whatever the local directory seperator is.
So, in this case we would find .\a\b.lua

Now, the same trick is applied when loading dll files, however as with the previous rules it should try to load luaopen_a.b, however as we all know C function names can't have a dot in them, so Lua replaces any dots with underscores to produce luaopen_a_b as the function name.

So, understanding all this the require in the init.lua file becomes: require "Bonsai.engine".

However, applying this to bonsai we have an... issue. You see, the graphics module's entry point was defined as luaopen_engine. Now, I could have just gone in and prefixed the function name however the name was getting a bit long at this point and well, I didn't like it.

However, I had also read up on another little trick; if you prefix a part of the module name with a '-' then Lua ignores everything else before it when searching for the function to open. You also have to rename the dll so it has a '-' prefix, but this is a minor issue.

So, a quick rename to "-engine.dll" and a change to require "Bonsai.-engine", a running of the progam and it all works... yes?

No.

Adventures into Window's dll loading

You see, the application still crashes, this time with a memory access error. As mentioned in another entry I had pushed all the windowing and event functions out to a "windowmgr.dll" which is dynamically loaded and bound by engine.dll during Lua registration.

Now, the call to load it was simply loadlibrary(L".\windowmgr.dll") however it totally fails to find the dll; this is down to window path searching voodoo.

Windows has a set order it searches for dll, well it has a couple, but for all intents and purposes it follows practically the same route;
- The directory from which the application loaded.
- The system directory. Use the GetSystemDirectory function to get the path of this directory.
- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
- The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- The current directory.
- The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key.

As you shouldn't be doing naughty things like shoving your own dlls into system folders I've highlighted the two of interest to us. However, in this instance these two directories are one and the same; the location where 'startup.exe' was run from.

So, when we ask for our windowmgr.dll the system fails to find it. Bugger.

Now, there are a couple of ways around this problem;
1) Put the dll in that directory. Yeah, it works but kinda makes this whole exercise pointless.
2) Change the current directory. This isn't recommended.
3) Add a directory to the search path. This is recommended over the above, however the function call only exists on Vista and Windows Server 2003. Given the amount of XP machines out there this isn't really an option.
4) Load the dll via the full path.

So, option 4 is it then.
A quick look into the MSDN docs shows that there is a handy little function: GetModuleFileName() which returns the full path to a module given it's handle.
It just so happens a little function called GetModuleHandle() also exists; now most of the time I've seen this used it is being used with a 'NULL' module name as often you just want to get into your own module and aren't too worried about specifics, however in this case we know our module name which is a good thing as it's our module's path we are intrested in and not the exe which we are loaded into.

So, with a bit of hackery later we get this code abomination;

TCHAR path[1024];
int len = GetModuleFileName(GetModuleHandle(L"-gfxengine.dll"),path,1024);
std::basic_string dir (path);
size_t offset = dir.find_last_of(L"\\");

std::basic_string finalpath = dir.substr(0,offset+1) + L"windowmgr.dll";
HINSTANCE hinstLib = LoadLibrary(finalpath.c_str());




First, yeah I know about randomly sized arrays and 'just big enough', however the function takes the lenght so it's going to be safe from over writes. I should really come up with a repeat system incase the path does end up greater than 1024 characters, but for now it works.

A Minor Refactor

The code above also gives a hint at a minor refactor I did; namely changing the dll name from "engine" to "gfxengine"; I felt that this name better reflected the usage of the dll's functions. Also, it allows for 'audioengine', more on that in a moment.

I've also restarted the SVN for the project. Due to some bad commiting on my part at the start things were in the SVN repo. which didn't need to be. So, I renamed the old, make a new Bonsai repo., cleaned up the directory tree, renamed a few files and resubmitted it from the start again.

I also shifted some common code out of the graphics dll and into a common directory above the project directories; no sense if duplicating it and I get the feeling I'm going to be using it alot [smile]

Sound..

So, with all that out of the way I started on the sound, as input already kinda works via events I figured this would be a good thing to add.

Turns out Creative have a new OpenAL SDK, so my first job was to grab that as this sound system is based on OpenAL. I might expand this to support some other API's as well at a later date, a thin layer over FMOD maybe. I'll probably dream up some extension system or some such [grin] Infact, to do that I'll probably want to dynamically load OpenAL32.dll instead of the current autoload, incase the end user lacks OpenAL support.

Anyways, that's for the future.

Right now only a few basic functions exist, all of which can be seen from this little code snippet;

audiodevs = bonsai.enumerateAudioDevices()
for k,v in pairs(audiodevs)
do
print(k .. "\t" .. tostring(v["default"]))
end

audio = bonsai.createAudioContext()
a,b,c = audio:createSources(3)
tab = {}
audio:createSources(3,tab)
for k,v in pairs(tab)
do
print(k .. "\t" .. tostring(v))
end




Basically, the functions break down as follows;

- enumerateAudioDevices
This returns a list of Audio devices and their caps in a table. The keys are the names of the devices, which can be passed back to Bonsai to select a perticular device.

- createAudioContext()
This creates an audio context to work with; you can either call it with or without a device name. The former uses that device while the latter uses a default device.

- createSources()
This basically creates sound sources which you can later attach buffers to so that you can play them. This function has two modes of operation;
* If you give it a table then it will fill the table with buffer 'objects'
* If not then it will return the request number of buffer 'objects'

I can envision both uses being handy.

Sources do have a couple of functions, however atm they aren't very easy to show as the buffers aren't working.

Sound loading and bloody libraries

Now, when it came to graphics loading I struck out on my own and created the Game Texture Loader to deal with this, a library I use often myself which is why it was created. However, when it came to sounds I didn't fancy repeating this, so when it came to needing a method to load sounds to load into buffers I hit the free libraries list here so see what was about.

After a quick look down the Audio list only one library seemed to fit the bill; libsndfile. Now, it's LGPL which makes my brain twitch but as I'm dll'ing things I figured I'd just load the dll dynamically (see previous hacks [grin]) in and be done with it.

However, which is supported the formats I wanted (and then some) it hasn't been updated since 2006 and FLAC, one of my target support formats, has had an update since then.

So, I figured I'd just rebuild the library as a dll with the new Ogg FLAC stuff and be done with it... apprently not.

The problem is that the build system seems to rely on the whole ./configure thing, which is fine (in an 'outdated tools' kinda way) on Linux; not a great deal of use on Windows however when you've got Visual Studio and no configure program to use to set it up.

*sigh*

I've considered to ways forward;
1) hack out what I want and declare the LPGL can bite me.
2) Game Audio Loader anyone?

The 2nd seems more appealing, some kinda of simply library which does for sound files what GTL does for images.

I'll give it some thought, but the idea of GAL does appeal; it will of course be zlib and when things are up and going linux and OS X ports would be appricated [grin]

In closing...

So, in closing things are moving forward, however a slight bump has been hit. However I'm not that worried, it seems that ever since I've started working and taking Sudafed for my ears I've felt a whole lot more motivated to program my own stuff in my own times; as such this bump will be over come!

For now, I'm going to watch a film (or something) and ponder the interface for GAL. I suspect I'll start work on it tomorrow when I get up and before I go out a brain rotting...

huzzah!
Sign in to follow this  


1 Comment


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now