XGroup Tutorial: GRP-Xtrax part
Krado @ KradoVision 
krado at aol dot com

Tutorial code: Quickest xtrax methods (It may be a good idea to press F-11 if using IE)

    When Ash ask me if I would release the source code for XGroup, I said yea, pretty much before I thought about it. At the time I was rewriting XGroup for Build because I'd learned more about the GRP files while writing KrdGroup for us.  The code was a mess and I'd forgotten about XGroup until recently.  There for a while when I was changing from the old "Toggle Method" to the methods below, XGroup was so Buggy I didn't think I'd ever get it to the point I wanted.  I think I finally got all those little Bubba bugs taken care of in Version 1.02.0005.  If not, I'll git the little &&$%%#^@@*&&@#*# s!

    I'm not all that keen on releasing source, so I've opted for this tutorial.  After all, why show long timers how I handle the more mundane things and just give newbies source to cut, paste and claim credit for?  I've been victimized about as much as I'm willing to tolerate.  I hope that little asshole who asked me for help writing his MP3 player chokes on each and every $29.95 he gets from my automation code!  Turns out he wasn't a newbie, he was just lazy, stupid or both! 

    There are all kinds of different methods for extracting files from a GRP.  I'm only going to cover the method I'm currently using. The old method for Version 1.00.0001 was an experiment in trying to show the user the program was working, without using a progress bar (I hate those!) and at the same time extract the files.  This method worked but was so slow that it was impractical.  I started researching the GRP file format (6:20 PM - 8/16/03 (Log file entry)) and was still learning about Redneck Rampage and the Build Engine in general when that version was written.  The RR community at the time didn't seem very interested in BETA testing or contacting me in a timely fashion to be useful if they did!

    Below is the code to Open a GRP file, extract a single file or multiple files.  Since Bubba may only want to browse the file I chose to load the information into the list boxes and close the file.  If he changes his mind and wants to extract, then the info is handy.


Comments are RED
VB Key Words are BLUE
Non Key code text is BLACK

' ' Open Selected grp file
Private Sub OpeGrp_Click()
Dim i As Long, cnt As Long
On Error GoTo OGErr
' ' Early on in VB I learned the hard way to check a Path's back slash "\" due to file not found errors.
' ' PathTo is declared as a string either: Public PathTo as String or Dim PathTo as string.

ChkSlash 
' ' xFileName is also a string so the user can change folders.  The program remembers the location of the file
' ' that's file list is displayed in the List Boxes.  Public xFileName as String or Dim xFileName as String.
xFileName = PathTo & File1.FileName
' ' In VB you open the file for Binary Access Read.  Just opening as Binary, you run the 
' ' risk of possibly damaging (oops! Overwriting GRP) the file if something goes wrong errorwise on your part.
Open xFileName For Binary Access Read As #1

' ' I use a Private Type in the general part of the form to get the FileID which would be, KenSilverman, 
' ' and the Number of files (NOF)
' '

Get #1, 1, fProps

' ' I use 2 hidden Text Boxes to stash the length of the header. This is just what I call the byte length of the 
' ' List of files.  If you have 10 files in a group the equation would look like this:

' '  (NOF * 16) + 16  = 176  + 1 = 177
LOH.Text = (fProps.Lngth + 1) * 16
NOF.Text = fProps.Lngth - 1
' ' ------------ Check File ID -----------------------
' ' Using the InStr function I compare the first 12 bytes, which is a string, to the name that should be there,
' ' KenSilverman.
' ' If not, notify the user the GRP file isn't valid. (Window's Program manager also uses/used the GRP extension).

If InStr(1, fProps.fName, "KenSilverman") Then
Label1.Caption = "File ID: " & fProps.fName
Else
MsgBox File1.FileName & " is not a Valid kGroup file!"
Beep
Close #1
' ' If the file isn't valid, exit the sub
Exit Sub
End If

' ' -------------- Load ListBoxs -------------
' '     On the form are 3 List Boxes, List1 holds the filenames, List3 holds the file sizes and List2 holds the
' ' calculated start byte for the file's data stored below the file list in the GRP.  They are synced so when you access the
' ' list properties of List1, list2 and 3 follow suit.  List boxes 2 & 3 are hidden.  The user really only needs to see
' ' the filenames so he, she or it can choose/browse...
' '     I chose this method because VB can't really read the same file in 2 different locations quickly when you go
' ' to extract, unless you open xFileName twice.  When I tried, I got the, "Yore buffer overflowed Bubba!" error 
' ' message.  The 2 File method, you read the list from open file 1, pass the information gathered to the code that's 
' ' handling the data read/copy from open file 2.  All while using the same FOR - NEXT loop.
' '
     My method is, if your filename, it's size and start byte are taken care of, then the program only has to read 
' ' and copy the data from the GRP to the filename.ext outside the group.

' ' Get GRP filename & number of files contained in GRP
' ' and place those in the form's title bar
capString$ = File1.FileName
grpNum$ = fProps.Lngth
Form1.Caption = capString$ & " - " & grpNum$
' ' In VB MIN - MAX dosn't work the same as it does in C++, or least I couldn't make it.  In the case below,
' ' Max is just the variable used.  Dim Max as Long, in General area of the form.
Max = (fProps.Lngth + 1) * 16
' ' At this point I add 16 to the list.  This is just a place holder.  It's removed later to sync up the list boxes
List2.AddItem "16"    ' ' 16 could be replaced with fProps.Lngth

' ' Since I'm using a Private Type to Load the list boxes, I use step 16. Sixteen is the number of bytes I wish
' ' to retrieve during each iteration of the FOR - NEXT loop.  Filename = 12, Filesize = 4
cnt = 0
For i = 1 To Max Step 16
Get #1, i, fProps
List1.AddItem fProps.fName

' ' Here is where the start byte is calculated.
' ' cnt keeps up with the running total of the filesizes. 
' ' For instance, if you have 10 files in a group the length of the file list would be 160 bytes.
' ' Plus the name KenSilverman + the number of files which = 16 bytes, so that would be 176 bytes;
' ' To get the start byte of the first file in the list you add 1 which equals 177. 
' ' The problem is you CANNOT extract KenSilverman because there's no related DATA, nor the 
' ' Number Of Files.  These are handled by subtracting the number of files before placing the 
' ' StartByte calculation into the List2 list box.  

cnt = cnt + fProps.Lngth
List2.AddItem (cnt + Max) - NOF.Text ' ' If you don't subtract the NOF the start byte would be off by the NOF...
List3.AddItem fProps.Lngth ' ' Add length of current File(i) to list3
Next

Example below tutExample.grp

List Box 1
Filename

List Box 2
Start Pos
List Box 3
File Size

GAME.CON

177 151190
2000.MAP 151367 144302
E1L3.MAP  295669 156,698
E2L1.MAP 452367 110898
TILES003.ART 563265 2779400
BRICDOOR.VOC 3342665 46513
LN_CRAP.VOC 3389178 21266
D3DTIMBR.TMB 3140444 3328
TABLES.DAT 3413772 8448
ARTFORM.TXT 3422220 5188
/*    (some data of first file, Game.con)
--------------------------------------------------------------------------------
Duke Nukem 3D Version 1.4
By Todd Replogle
(c) 1996 3D Realms Entertainment
-----------------------------------------------------

' ' You have to remove unnecessary entries from the list boxes at this point
List3.RemoveItem (0)  
List2.RemoveItem (0) ' ' List2 is holding 16
List1.RemoveItem (0) ' ' KenSilverman not a file remove from list
' ' The information for the "Extract or Browse" is loaded, close the file.
' ' I disable Open Group button so Bubba won't click on it again.
' ' If he clicks on the same group again in the list or another GRP it's re-enabled.
Close #1
OpeGrp.Enabled = False
Exit Sub
OGErr:
CatchErr  ' General Error handler
End Sub   '  We done with the load part Bubba!

' ' Below is the sub I use to check the backslash for the path 
' ' This cured the file not found errors when Bubba puts stuff in the ROOT directory of a drive!
Sub ChkSlash()
PathTo = Dir1.Path
If Right(PathTo, 1) = "\" Then ' ' end if and exit
Else
PathTo = PathTo & "\"  ' ' Place BackSlash in proper place
End If
End Sub


' ' Extract selected file
' ' Bubba has selected a file and wants to extract it from the GRP.
' ' We open the GRP file whose file list, file size and start byte for each, is already displayed in the list boxes.
Private Sub Xtract_Click()
' ' By now the button is enabled, check to make sure the files in the list are selected.
' ' If not exit the sub.

If List1.SelCount = 0 Then Exit Sub

' ' If more than one file is selected then call the Multiple_Extraction Sub
' ' In version 1.00.0001, I just automated the list box as if the user was selecting a file
' ' and hitting the button as fast as he could.
If List1.SelCount > 1 Then
MultiEX     ' ' goto MultiExtract
Exit Sub
End If
' ' If only one file is selected then continue on.
On Error GoTo
xTErr
' ' Init Byte Array/buffer
Dim
b() As Byte
ChkSlash
' ' pFileName will be created in the selected folder and the extracted file's data
' ' written to it.

pFileName = PathTo & List1.Text
' ' Open the GRP file to read from
Open
xFileName For Binary Access Read As #1
' ' Open the filename to wrtie the data to.
Open
pFileName For Binary Access Write As #2
' ' Re dimension the Byte Array to the size of the file
ReDim
b(1 To List3.Text)
' ' The start byte of the file, in List2, is already synced up with it's name in List1, it's size in List3
' ' If we're going to get the 2nd file in the list then; we start the read at byte position
151367 in the GRP and
' ' read until we get to the length of the file in List3,
144302.  Rather than read bytes until a buffer is
' ' filled by streaming, VB basically reads/gathers the data in one pass as a "Chunk".

Get #1, List2.Text, b
' ' Place data in the newly created file outside the group.
Put
#2, , b
' ' Do Events gives up time slice to other running processes including XGroup itself to report
' ' the activity to the title bar...

DoEvents
' ' Refresh the File List box to reflect the newly added file on the MakeGroup form.
Form2.File1.Refresh
' ' Close the files
Close
#1, #2
' ' Clear memory used by the chunk.  VB is supposed to do this on it's own, but I do it anyway....
ReDim b(0) As Byte
' ' Show the name of the file extracted in the form's title bar.
Form1.Caption = "Extracted: " & List1.Text
Exit Sub  ' ' If there's no error we done!
xTErr:
' ' If there is an error close the files in case Bubba wants to try again without opening and closing XGroup.
Close
#1, 2
' ' Report the error and the filename in title bar
Form1.Caption = "Error extracting " & List1.Text
End Sub


' ' Extract Multiple selections
' ' Ok so Bubba wants more that one file from the GRP he opened previously.
' ' This method is the fastest I could come up with using the info already in the
' ' List Boxes.

Sub
MultiEX()
On Error GoTo xTErr
' ' Init Byte Array/Buffer
Dim
b() As Byte
ie = 0  ' ' init counter for number extracted
ChkSlash
' ' The necessary information is already in the List Boxes so we Open the GRP file again.
Open xFileName For Binary Access Read As #1
' ' Instead of the old POKEY method in Version 1.00.0001 we don't select the items in the
' ' list box.  Lol, seemed like a good way to entertain Bubba at the time.  Anyhow, we access
' ' the List Property as shown below.

For
mx = 0 To List1.ListCount - 1
' ' If an item in the list is selected then open the "ExtractTo" file, get the data, put it in this file and close it.
If List1.Selected(mx) = True Then
' ' pFileName is the file written to.
pFileName = PathTo & List1.List(mx)
Open pFileName For Binary Access Write As #2
' ' Re-Dimension the byte Array/Buffer to hold the length of the data copied from the GRP.
ReDim b(1 To List3.List(mx))  ' ' 1 to List3 is the number of bytes / file's size.
Get #1, List2.List(mx), b  ' ' The text in list2 holds the start byte position in the GRP
Put #2, , b
DoEvents
ie = ie + 1 ' ' Keep a running total of the number of items extracted.
' ' report activity in the form's title bar.
Form1.Caption = "Extracted: " & List1.List(mx)
' ' Close current "ExtractTo" file and repeat until we've reached the list count.
Close
#2
List1.Selected(mx) = False  ' '  Deselect the files as they're extracted.
End If
Next
mx
' ' Refresh MakeGroup's file list box
Form2.File1.Refresh
' ' All selected files have been extracted, so close the GRP and release memory used.
Close
#1
ReDim b(0) As Byte
' ' Notify user we done it!
MsgBox "We Done Bubba! Items extracted = " & ie, vbOKOnly, "Multi-Extract"
Form1.Caption = "GRP-XTrax"
Command1.Enabled = True
Exit Sub
xTErr:
' ' If an error occurred then notify the user, close the files.
If Err.Number <> 0 Then
Close #1, 2
Form1.Caption = "Error extracting " & List1.List(mx)
' '  I created a sub to handle the error if a file is being over written and is a read only file.
' '  I'm sure you can figure out your own way to handle this...

errFile$ = PathTo & List1.List(mx)
cAttrRO (errFile$)
End Sub

This concludes the Extraction part of this tutorial.   MakeGroup

Krado