XGroup Tutorial: MakeGroup part
Krado @ KradoVision
krado at aol dot com
Tutorial code: Best MakeGroup code so far (It may be a good idea to press
F-11 if using IE)
If you're reading this, then I assume you've learned
how to extract files from a GRP. The code for MakeGroup has changed a LOT
since the first version. 1.02.0006 is capable of fetching files from
multiple sources. It does this by using 3 list boxes, two of which are
hidden under the main "GrpListBox". List4, hidden, holds the
path of each file being written to the group. List3, hidden, holds the
file's size. Plus it's much faster!
Note: The code below is from 1.02.0006. The
.0005 code worked fine for the average user created GRP if it was 20 Megs or
so. But when testing XGroup against kextract.exe / kgroup.exe for speed,
132 MB files would have the last 3 filenames sullied and the files would not
extract. I have no idea what caused this. Nothing I did fixed the
problem. No errors were returned.
I had to go back to writing the File List to the GRP
first. Then writing the binary data. Using all the CON and ART files
from Redneck Rampage Rides Again, it took 18 seconds to write 70 files @ 132
megs total on my 866. Bummer... It's back to being a little slow but
the files are more accurate. Sorry!
' ' Make group button
' ' New code rewritten Sunday, June 13, 2004
Private Sub Command3_Click()
On Error GoTo mgErr
' ' Call common dialog show save
cd1.CancelError = True
cd1.DialogTitle = "Write GRP File:"
cd1.Filter = "Build Engine Grp files (*.grp)|*.grp"
cd1.InitDir = App.Path
cd1.ShowSave
mgFileName$ = cd1.FileName
clFileName$ = cd1.FileTitle
' ' If check box checked add list.txt
' ' file to current GRP
If Check1.Value = 1 Then
Form2.Caption = "Adding current list file"
' ' The sub that creates the List.txt file is below
' ' Send it clFileName because it appears in the list file
' ' but if Bubba renames the GRP file later it'll look stupid...
' ' Oh well, I though it was a good idea at the time!
AddList2GRP (clFileName$)
End If
' ' Disable controls to keep user from interrupting
the process. DisCons is a sub
' ' that disables the various controls on the form. Create your own or
comment mine out.
DisCons
' ' If write path = a CD drive then tell Bubba
' ' "He cain't do that!", and exit the routine.
Form2.Caption = "Checking write path..."
' ' Yet again protecting Bubba from himself is a
pain. GetDriveType is a WinAPI function added
' ' to the project. Unless you create your own mod, (cut and paste code at
bottom of this page) to
' ' handle this you should comment out the code that check the type of drive
you're wanting to write to.
' ' The code below just reads the "Drive Letter and
the colon" from the Common Dialog Box filename,
' ' and checks if it's a CD. If it's another write
protected drive the CatchErr sub will handle it...
chkCdbDrive = GetDriveType(Left(mgFileName$, 2))
If chkCdbDrive = 5 Then
MsgBox "Bubba! Unable to write to CD!", vbOKOnly, "CD Write
Error"
Form2.Caption = "MakeGroup"
Drive1.Enabled = True
cd1.FileName = ""
' ' An error has occurred so re-enable the controls so
Bubba can fix it.
EnCons
Exit Sub
End If
' '----------------------- Check For Long Filename in list --------
' ' While reading the list of files in the List Box, the length of each
file name is checked
' ' If there are more than 12 characters you'll be notified.
Form2.Caption = "Checking filename length..."
For pl = 0 To
List1.ListCount - 1
If Len(List1.List(pl)) > 12 Then
msg$ = "You'll have to rename " & List1.List(pl) & vbCrLf _
& "The name can only be 12 characters!" & vbCrLf _
& "Select the file in list and press R. Type" & vbCrLf _
& "new name into the input box. Hit OK."
Ttl$ = List1.List(pl)
MsgBox msg, vbOKOnly, Ttl
Form2.Caption = "MakeGroup"
' ' If long name detected then close the file and
exit the sub
' ' The user can rename the file and before
proceding can
' ' edit a con file if necessary...
Close #2
' ' EnCons calls a sub to re-enable the controls
EnCons
Exit Sub
End If
Next pl
' ' ----------------Begin creating GRP file
-------------------------
' ' If there are no length errors we begin creating the FileID and list of files
Open mgFileName$ For Binary As #1 ' New Grp
file
Dim ks As String * 12
Dim NOF As Long
Dim fLeng As Long
' ' Make sure not to allocate space for this
string!
Dim
inFileName As String
' ' -------------- Create Header -------------
ks = "KenSilverman"
NOF = List1.ListCount
Put #1, , ks
Put #1, 13, NOF
' ' This may seem a bit odd here. Once KenSilverman
and the number of files is
' ' written in the first 12 bytes and the long NOF written from 13-16. You have
to calculate
' ' the write positions of each file name and it's file size because you're
jumping from the list
' ' down to where the data is stored and back. Since you're going back and
forth you have
' ' to calculate this as far as I know. Plus there's the issue of 20 20s
& 00 00s
' ' I tried to use Set Seek but that
didn't work out too well.
fp& = 17 ' ' First position of the first file
in the list
lp& = 29 ' 'First position of the first File's
size in the list
tot& = 0
' ' --------------Create list--------------------
' ' When adding files to your list, List 4 holds the path
to the file.
' ' This is how we can get files from multiple local folders, drives, remote
drives
' ' mapped drives etc...
For
w = 0 To List1.ListCount - 1
' ' First filename is written
to byte position 17
Put #1, fp&, List1.List(w)
fLeng = List3.List(w)
' ' First file's size is written to byte position 29
Put #1, lp&, fLeng
' ' The first file name has been written to the GRP, it's size.
' ' Now to place following file names & sizes in the proper places you add 16 for the
file name
' ' and then 12 more for the file's size. See
Example and more information below
fp& = (fp& + 17) - 1
lp& = (fp& + 13) - 1
Next w
' ' ------------ Put data in file ------------
Dim b()
As Byte
For i = 0 To List1.ListCount - 1
' ' List4 may contain something like Root:\XGroup\ & List1 may
contain Game.con
getFromFile = List4.List(i) & List1.List(i)
Open getFromFile For Binary
Access Read As #2
Form2.Caption = "Processing: " & List1.List(i)
fLeng = List3.List(i)
' ' The first iteration of the for - next loop, wp& = 0 and
tot& = 0
' ' If there are 10 files in the list then (NOF * 16) = 160
' ' You add 16 bytes to that which is "KenSilverman"(12) and number of
files
' ' which is a Long, 4
bytes, so this would equal 176. 176 is the last byte of the file list.
' ' You add 1 which = 177. 177 is where the program will place the first
file, Game.con.
' ' See the example on the previous page.
' ' The "tot&" counter keeps a running total of the length of the
current file
' ' adding the length of the following file so on and so forth.
' ' Game.con's start byte is 177, it's byte length/ filesize is 151190. To
get the write position for
' ' the next file, 2000.map, 177 is added to the length of Game.con. 177 +
151190 = 151367.
wp& = (NOF * 16) + 16 + 1 + tot&
' ' Re Dimension the Byte Array/Buffer to the
length of file being added to the group.
ReDim
b(1 To
List3.List(i))
' ' Get the file's data
Get #2, , b
DoEvents
' ' wp& is the start position of the current
file being written to the GRP.
Put #1, wp&, b
' ' Close the current "Get From File" and
proceed to the iteration.
Close #2
tot& = tot& + fLeng
Next i
mgErr:
Form2.Caption = "MakeGroup"
ReDim b(0) As Byte
Form1.File1.Refresh
' ' Release counters
wp& = 0
tot& = 0
' ' Close any open files in case of error
Close #1, 2
EnCons
' ' If user hits cancel on the common dialog box the
err.number = 32755
If Err.Number = 32755 Then
Exit Sub
' ' If the error is caused trying to write the GRP
to a write protected disc or CD then:
ElseIf Err.Number = 70 Then
MsgBox "That disc may be Write-Protected?" & vbCrLf _
& "Slide that lil tab and try 'er agin!", vbOKOnly, "Write
protected"
' ' Change directories back to the program's path
ChDir App.Path
Exit Sub
Else
' ' Any other error is handled by the gerneral
error handler.
' ' Unless you create a general error handler you should comment out
' ' references to it...
CatchErr
End If
End Sub
' ' This sub creates a list file from the current
list, writes in the program's
' ' folder and adds it to the list at the end.
Sub AddList2GRP(clFileName$)
On Error GoTo ListErr
cTime = Date & " - " & Time ' My Date / Time format
wPath$ = App.Path & "\List.txt"
' ' Open the list text file with "List"
designation
' ' and print opener line, Name of file list is for and cTime
' ' For Output will overwrite a previous version.
Open wPath$ For Output
As #1
Print #1, "List of files & sizes: "
& clFileName$ & " " & cTime
Print #1,
' ' Print the list as it appears in the listbox adding a
formatted
' ' filesize to the end of each string
For cl = 0 To
List1.ListCount
Print #1, List1.List(cl); " - ";
Format(List3.List(cl), "###,###,##0")
Next
' ' Tell 'em who did this
Print #1,
Print #1, "List created with MakeGroup."
Close #1
lFileSize = FileLen(App.Path & "\List.txt")
List1.AddItem "List.txt"
List3.AddItem lFileSize
List4.AddItem App.Path & "\"
' ' GRPS keeps up with the total size of your group and is
reported in the
' ' label under the "Remove 1" button. You may want to comment
this out as well.
GRPS = GRPS + lFileSize
Exit Sub
ListErr:
CatchErr
End Sub
Cut and paste the code below into a mod. It's
the get drive type Function code.
Attribute VB_Name = "Module1"
Public Declare Function GetDriveType Lib "Kernel32" _
Alias "GetDriveTypeA" (ByVal nDrive As String) As Long
Private Declare Function GetLogicalDrives Lib "Kernel32" () As Long
Const DRVIE_CDROM = 5
Const DRIVE_FIXED = 3
Const DRIVE_RAMDISK = 6
Const DRIVE_REMOTE = 4
Const DRIVE_REMOVABLE = 2
Thursday, June, 10
2004
There have been a couple of questions as to why I
calculate the write positions of the filenames and their sizes. I'll try
to answer this as simply as possible. Think of the file list as being a
group of boxes 16 spaces wide. In reality "Dim ks as String *
12" is unnecessary since Ken's name is 12 characters long and it's
constant.. When you
add the "* 12" you allocate 12 spaces whether you use all of them or
not. This is where I had my trouble at first. Since I allocated 12
spaces for Ken's name, I also allocated 12 spaces for the file names, "Dim
lFileName as String * 12". This caused the program to fill in the
UNUSED spaces, if a file's name was LESS than 12 characters, with
"empties" (ASCII Chr#32, Hex 20). Using this method the GRP files
looked perfect but kextract would give me an error message when I tried to
test/extract the files. The game would NOT play the GRP.
Once Ken Silverman told me what I may be looking for
here, I deleted the "* 12" allocation for the filenames. Then the
program would write the filename in but it would also place the FileSize(LONG)
right up against the name ie: Game.con### and the GRP still wouldn't
work. So I came up with the "Magic Number = 16" and began
to calculate where to write the filenames and file sizes in the proper places
without the "20 20's".
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
K |
e |
n |
S |
i |
l |
v |
e |
r |
m |
a |
n |
2 |
00 |
00 |
00 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
G |
a |
m |
e |
. |
c |
o |
n |
00 |
00 |
00 |
00 |
# |
# |
# |
00 |
G |
a |
m |
e |
. |
c |
o |
n |
20 |
20 |
20 |
20 |
# |
# |
# |
00 |
In the example table above, line
number 5 is wrong if you dim the filenames as String * 12.
Line number 4 is correct when you calculate the start positions of the filename
and file size.
Note: In hex Number of files 2 would not look like a 2, it
would be characters that windows will not show properly.
Once
I realized what was happening I came up with my equation and the fp& = 17 (fileposition(LONG)
and lp& =29 (lengthPosition(LONG). If you'll notice in the table above
the "G" in Game.con is in position 17 and it's size starts at
29. Even though line 5 has the 20s, the name is still in the proper
position at 33 and it's size at position 45. So you Dim lFileName as
String. Since there's no allocation, when the string's length is reached
the program fills unused spaces with "nulls" ie: 00 00 00... Note:
If you have XGroup.exe version 1.02.0004 then DELETE it and get the
newest. While trying to clean up the code for this I, like a Bubba, added
"* 12" to Dim lFileName as string * 12. I didn't notice it until
the next morning when I put up this tutorial and was checking to see if the page
would come up. I nearly had a heart attack!
About the only things I intend to change in XGroup
will be how you do the Multi-Selection in the list boxes. You may end up
having to hold either the "Ctrl" key or a "Shift" key down
while working with these boxes. I will also add a button that will allow
you to select multiple files in the file list box on the MakeGroup form manually
and press the " > " button to add this batch to the GRP List
box. I may as well add these features to XGroup since I've added them to
the "xLaunch" program...
Another issue has come up regarding System
Requirements. If you have an older machine, less than a p2 / 233 w/128 mgs
of ram, you may get buffer problems especially if you're trying to work with
GRPs the size of the Rides.grp. Some of the art files are 9 megs and
that's a lot for Windows to deal with paging to the swap file. My ole 233
can extract all of the ART files but just barely....
That's it. Now maybe you can create your own
version of XGroup or whatever you want to call it...
I started my various tests by
writing CON and other plain text files to the GRPs. I could open these GRP
files with a program called List.com and read the files in the group, check the
file list and file sizes. I've had List for years and it makes a good
program for viewing files of this type. If you open a file with it you can
hit Alt + h to view the file in HEX. I also used it to study GRP files
created with KGroup.exe, by comparing the kgroup files to the MakeGroup
files. You may still be able to find this program on the net
somewhere. Once I got the plain text files right, I began adding maps and
testing / comparing those.
Thanks to Ken Silverman for his help last year when I didn't
notice that the "20 20s" were supposed to be "00 00s"!
And I sat and stared at those files for days!
KradoVision
XGroup
xLaunch
Krado
|