Storm.dll (StormLib) question

Blitz3D Forums/Blitz3D Beginners Area/Storm.dll (StormLib) question

tyoud(Posted 2011) [#1]
Hello, I'm interested in being able to read from Blizzard's MPQ file archives using Blitz3d. To do that I'm using the open source StormLib implementation at:
http://www.zezula.net/en/mpq/stormlib.html

I have a compiled StormLib.dll (that I found from another project, but it looks fine),

And a nice StormLib.decls file written - I just put in the definitions for the 3 functions I want to try out (to open the mpq, extract a file, and then close the mpq):

.lib "StormLib.dll"
SFileOpenArchive( szMpqName$, dwPriority%, dwFlags%, phMPQ* )
SFileCloseArchive( hMPQ )
SFileExtractFile( hMPQ, szToExtract$, szExtracted$ )

----

I think the .decls match the C++ function declarations?

;bool WINAPI SFileOpenArchive(
; const char * szMpqName, // Archive file name
; DWORD dwPriority, // Archive priority
; DWORD dwFlags, // Open flags
; HANDLE * phMPQ // Pointer to result HANDLE
;);


;bool WINAPI SFileCloseArchive(
; HANDLE hMpq // Handle to an open MPQ
;);

;bool WINAPI SFileExtractFile(
; HANDLE hMpq, // Handle to a file or archive
; const char * szToExtract, // Name of the file to extract
; const char * szExtracted // Name of local file
;);

-----

Next a short test .bb file - testing it on an installation of Diablo 2 that I have a copy of -

; Open the archive
SFileOpenArchive("F:\Diablo II\d2music.mpq", 0, 0, Handle(hMpq) )
; Extract a file in the archive, e.g. "data\global\music\Act1\tristram.wav"
SFileExtractFile(hMpq, "data\global\music\Act1\tristram.wav", "tristram.wav")
; Close the archive
SFileCloseArchive(hMpq)

-----

And the error - ObjectHandle must be used with an object - makes some logical sense, but ... I would have to create a type? of?

I looked at the Object and Handle page but I didn't understand it.
:/ Is it really obvious?

Or is there any easier way to use pointers in Blitz3d? Peek? Poke?

Last edited 2011


tyoud(Posted 2011) [#2]
*** GOT IT!! ***

2 problems - first was I needed to point to an MPQ file and a file in it which actually existed ...
second: needed this Peek/Poke stuff to make the pointers work out.

If the file doesn't exist, or the MPQ doesn't exist, the Extract function will write out the file - but it will have 0 bytes in it.
So you'd want to test for file existance first.

;-------- test code
hMpq = CreateBank(4)
Print hMpq

; Open the archive
Print PeekInt(hMpq,0)
SFileOpenArchive("d2sfx.mpq", 0, 0, hMpq )
Print PeekInt(hMpq,0)

; Extract a file in the archive
SFileExtractFile(PeekInt(hMpq,0), "Data\Global\sfx\ambient\creature\cow1.wav", "cow1.wav")

; Close the archive
SFileCloseArchive(PeekInt(hMpq,0))

FreeBank hMpq

Last edited 2011


Kryzon(Posted 2011) [#3]
Handle() and Object() deal with Blitz3D's internal IDs that are assigned to every instance of custom user types. They're not related to memory handles. You can ignore them for all of this you're trying to do.


---
I don't think you should send a string to that function, since it's expecting a 'char pointer'.
Taking the snippet in this thread as reference, you need a function to convert Blitz3D's Strings into Banks:
Function StringToBank%(txt$)
	Local bank% = CreateBank(255)
	Local temp$ = txt + Chr$(0) ;Add C-style string end.
	
	For i% = 1 to Len(temp)
		PokeByte bank, i-1, Asc( Mid$(temp, i, 1) )
	Next
	Return bank
End Function

Do you know how 'HANDLE' is declared on the DLL's source? we need to see that.

With these things out of the way, the DECLS should be this:
.lib "StormLib.dll"
SFileOpenArchive( szMpqName*, dwPriority%, dwFlags%, phMPQ* )
SFileCloseArchive( hMPQ% )
SFileExtractFile( hMPQ%, szToExtract*, szExtracted* )


And using the DECLS:
hMPQ% = CreateBank(4)

tempBank% = StringToBank("F:\Diablo II\d2music.mpq") ;Using the function previously described.
SFileOpenArchive( tempBank, 0, 0, hMPQ )

tempBank% = StringToBank("data\global\music\Act1\tristram.wav")
tempBank2% = StringToBank("tristram.wav")
SFileExtractFile( PeekInt(hMPQ,0), tempBank, tempBank2 )

SFileCloseArchive( PeekInt(hMPQ,0) )

FreeBank hMPQ
See if that works.

EDIT: lol... the moment I finished writing this you solved it. How's that for a coincidence?

Last edited 2011


Yasha(Posted 2011) [#4]
I don't think you should send a string to that function, since it's expecting a 'char pointer'.


Actually passing a string to a char * is fine, Blitz handles that for you in the background. It can even convert them coming the other way too (i.e. return a string), as long as the returned pointer doesn't reference stack memory.

There's an example of this in the userlibs spec file.

As for the Object/Handle error, you seem to have it worked out, but let's type out a unnecessary explanation anyway -

Banks and instances of user-defined types are both controlled in B3D as double-pointers. The variable you use in Blitz code - the bank int handle or the strongly-typed object pointer - points to a wrapper object that B3D uses to safely control the object - for banks, it holds the size, to prevent overflows; for type instances, it holds things like the Before and After pointers. In both cases the first field in this meta-object is the pointer to the actual malloc-ed block that makes up the object.

When you pass something to a userlib function that you've declared in the decls file as a * parameter, B3D assumes it's one such wrapped object, and the parameter passed to the DLL will be the unwrapped "bare" pointer to the object itself, dereferencing the outer pointer for you. So your initial error is connected to the fact that you passed it some handle number when it was expecting a meta-object and didn't get one (file handles, 3D entities, images etc. all have bare pointers as their int handles in B3D - it's just banks and type instance that are wrapped this way).

So you could simplify your working code by replacing a bank with a resultPointer user type:

;-------- test code
Type resultPointer
    Field val
End Type

Local hMpq.resultPointer = new resultPointer
Print hMpq\val

; Open the archive
Print hMpq\val
SFileOpenArchive("d2sfx.mpq", 0, 0, hMpq )
Print hMpq\val

; Extract a file in the archive
SFileExtractFile(hMpq\val, "Data\Global\sfx\ambient\creature\cow1.wav", "cow1.wav")

; Close the archive
SFileCloseArchive(hMpq\val)

Delete hMpq


It's always better to define a type to describe what you're doing than to use banks. They're faster in all operations, and you don't have to remember offset pointer addresses. It's also much clearer for anyone reading your code.

Of course, the simplest thing of all (since you compiled it from source) might be to wrap the relevant functions from within the DLL so that they just return the value you want directly, instead of using an out parameter at all (example, may not be accurate to your library):

void * _stdcall SFileOpenArchive_wrap(char * name, int priority, int flags) {
    void * outParameter;
    SFileOpenArchive(name, priority, flags, &outParameter);
    return outParameter;
}


That way you can just call it from B3D as a normal function the returns a result, and not mess about with either banks or a result type. You could then rename this function to be called from B3D using the optional renaming syntax in the decls file so that it has the same name as the function it wraps, and you never need to think about out-parameters again.

Last edited 2011


tyoud(Posted 2011) [#5]
Thanks both for the help!!

I do see now how I could use the Types and val -